Full Code of qgis/qgis-js for AI

main 8feda36392eb cached
133 files
352.6 KB
92.5k tokens
255 symbols
1 requests
Download .txt
Showing preview only (385K chars total). Download the full file or copy to clipboard to get everything.
Repository: qgis/qgis-js
Branch: main
Commit: 8feda36392eb
Files: 133
Total size: 352.6 KB

Directory structure:
gitextract_sch8wsm5/

├── .clang-format
├── .editorconfig
├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .gitmodules
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│   ├── c_cpp_properties.json
│   └── settings.json
├── CHANGELOG.md
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build/
│   ├── actions/
│   │   ├── clean.ts
│   │   ├── compile.ts
│   │   ├── install.ts
│   │   ├── lib/
│   │   │   ├── BuildType.ts
│   │   │   └── QgisJsOptions.ts
│   │   ├── libs.ts
│   │   └── size.ts
│   ├── scripts/
│   │   ├── clean.sh
│   │   ├── compile.sh
│   │   └── install.sh
│   ├── vcpkg-ports/
│   │   ├── libspatialindex/
│   │   │   ├── portfile.cmake
│   │   │   └── vcpkg.json
│   │   └── qgis/
│   │       ├── portfile.cmake
│   │       └── vcpkg.json
│   ├── vcpkg-toolchains/
│   │   └── qgis-js.cmake
│   ├── vcpkg-triplets/
│   │   └── wasm32-emscripten-qt-threads.cmake
│   └── vite/
│       ├── CrossOriginIsolationPlugin.ts
│       ├── DirectoryListingPlugin.ts
│       └── QgisRuntimePlugin.ts
├── docs/
│   ├── architecture.md
│   ├── bundling.md
│   ├── ci.md
│   ├── compatibility.md
│   ├── debugging.md
│   ├── examples/
│   │   ├── qgis-js-example-api/
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   ├── package.json
│   │   │   └── vite.config.js
│   │   └── qgis-js-example-ol/
│   │       ├── index.html
│   │       ├── main.js
│   │       ├── package.json
│   │       ├── style.css
│   │       └── vite.config.js
│   ├── performance.md
│   └── profiling.md
├── package.json
├── packages/
│   ├── qgis-js/
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── QgisApiAdapter.ts
│   │   │   ├── emscripten.ts
│   │   │   ├── index.ts
│   │   │   ├── loader.ts
│   │   │   └── runtime.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   ├── qgis-js-ol/
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── QgisCanvasDataSource.ts
│   │   │   ├── QgisJobDataSource.ts
│   │   │   ├── QgisXYZDataSource.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   └── qgis-js-utils/
│       ├── README.md
│       ├── package.json
│       ├── src/
│       │   ├── fs/
│       │   │   ├── FileSystem.ts
│       │   │   ├── GithubProject.ts
│       │   │   ├── LocalProject.ts
│       │   │   ├── Project.ts
│       │   │   ├── RemoteProject.ts
│       │   │   └── index.ts
│       │   └── index.ts
│       ├── tsconfig.json
│       └── vite.config.ts
├── pnpm-workspace.yaml
├── qgis-js.ts
├── sites/
│   ├── dev/
│   │   ├── index.html
│   │   ├── package.json
│   │   ├── public/
│   │   │   └── projects/
│   │   │       └── village/
│   │   │           ├── buildings.dbf
│   │   │           ├── buildings.prj
│   │   │           ├── buildings.qpj
│   │   │           ├── buildings.shp
│   │   │           ├── buildings.shx
│   │   │           ├── project.qgs
│   │   │           └── rgb.tif
│   │   ├── src/
│   │   │   ├── demo.css
│   │   │   ├── index.ts
│   │   │   ├── js.ts
│   │   │   ├── layers.ts
│   │   │   └── ol.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   └── performance/
│       ├── .gitignore
│       ├── index.html
│       ├── package.json
│       ├── playwright.config.ts
│       ├── report.html
│       ├── src/
│       │   └── index.ts
│       ├── tests/
│       │   └── performance.spec.ts
│       ├── tsconfig.json
│       └── vite.config.ts
├── src/
│   ├── api/
│   │   ├── QgisApi.cpp
│   │   ├── QgisApi.ts
│   │   └── QgisModel.ts
│   ├── model/
│   │   ├── QgsLayerTreeGroup.hpp
│   │   ├── QgsLayerTreeGroup.ts
│   │   ├── QgsLayerTreeLayer.hpp
│   │   ├── QgsLayerTreeLayer.ts
│   │   ├── QgsLayerTreeModelLegendNode.hpp
│   │   ├── QgsLayerTreeModelLegendNode.ts
│   │   ├── QgsLayerTreeNode.hpp
│   │   ├── QgsLayerTreeNode.ts
│   │   ├── QgsMapLayer.hpp
│   │   ├── QgsMapLayer.ts
│   │   ├── QgsMapRendererJob.hpp
│   │   ├── QgsMapRendererJob.ts
│   │   ├── QgsMapRendererParallelJob.hpp
│   │   ├── QgsMapRendererParallelJob.ts
│   │   ├── QgsMapRendererQImageJob.hpp
│   │   ├── QgsMapRendererQImageJob.ts
│   │   ├── QgsPointXY.hpp
│   │   ├── QgsPointXY.ts
│   │   ├── QgsRectangle.hpp
│   │   └── QgsRectangle.ts
│   ├── qgis-js.cpp
│   └── qt.conf
├── tsconfig.json
└── vcpkg.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .clang-format
================================================
#see https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Language: Cpp
ColumnLimit: 100
ContinuationIndentWidth: 2
UseTab: Never
IndentWidth: 2
TabWidth: 2
IndentCaseLabels: true
IncludeBlocks: Preserve
SortIncludes: true
SortUsingDeclarations: false
AlignConsecutiveMacros: false
AlignEscapedNewlines: DontAlign
AlignAfterOpenBracket: AlwaysBreak
AlignOperands: false
AlignTrailingComments: false
BinPackArguments: false
BinPackParameters: false
SpacesInContainerLiterals: false
Cpp11BracedListStyle: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Always
FixNamespaceComments: false
ReflowComments: false
NamespaceIndentation: All
BreakStringLiterals: false
ConstructorInitializerIndentWidth: 2
SpaceBeforeCtorInitializerColon: true
BreakBeforeInheritanceComma: false
BreakInheritanceList: AfterColon
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true


================================================
FILE: .github/workflows/build.yml
================================================
name: Build

on:
  workflow_dispatch
  # push:
  #   branches:
  #     - main
  # pull_request:
  #   branches:
  #     - main

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  build-qgis-js:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - name: Set Swap Space
        if: ${{ !env.ACT }}
        uses: pierotofy/set-swap-space@master
        with:
          swap-size-gb: 8
      - name: Update apt cache
        run: >-
          sudo apt-get update
      - name: Install system dependencies
        run: >-
          sudo apt-get install -y
          ninja-build
          pkg-config
          flex
          bison
          autoconf
          autoconf-archive
          automake
          libtool
      - name: Free Disk Space (Ubuntu)
        if: ${{ !env.ACT }}
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          swap-storage: false
      - name: Setup node from package.json
        uses: actions/setup-node@v4
        with:
          node-version-file: "package.json"
      - uses: pnpm/action-setup@v2
      - name: Install dependencies
        run: pnpm install
      - name: Compile qgis-js
        run: pnpm run compile:release
      - name: Save logs on failure
        if: ${{ failure() }}
        uses: actions/upload-artifact@v4
        with:
          name: vcpkg-buildtrees-logs
          path: build/vcpkg/buildtrees/**/*.log
      - name: Build qgis-js packages
        run: pnpm run build
      - uses: actions/upload-artifact@v4
        with:
          name: "package-qgis-js"
          path: |
            packages/qgis-js/package.json
            packages/qgis-js/README.md
            packages/qgis-js/dist/**/*
      - uses: actions/upload-artifact@v4
        with:
          name: "package-qgis-js-utils"
          path: |
            packages/qgis-js-utils/package.json
            packages/qgis-js-utils/README.md
            packages/qgis-js-utils/dist/**/*
      - uses: actions/upload-artifact@v4
        with:
          name: "package-qgis-js-ol"
          path: |
            packages/qgis-js-ol/package.json
            packages/qgis-js-ol/README.md
            packages/qgis-js-ol/dist/**/*
      - name: Build site
        run: pnpm run dev:build
      - name: Setup Pages
        uses: actions/configure-pages@v4
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: "./sites/dev/dist"
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4


================================================
FILE: .gitignore
================================================
package-lock.json
CMakeLists.txt.user
build/wasm
node_modules
dist
packages/*/dist
packages/*/etc
bin/act
docs/api
.DS_Store


================================================
FILE: .gitmodules
================================================
[submodule "build/vcpkg"]
	path = build/vcpkg
	url = https://github.com/microsoft/vcpkg.git
[submodule "build/emsdk"]
	path = build/emsdk
	url = https://github.com/emscripten-core/emsdk.git


================================================
FILE: .nvmrc
================================================
v22.16.0


================================================
FILE: .prettierignore
================================================
public
dist
node_modules
build/wasm
build/emsdk
build/vcpkg
build/vcpkg-ports
build/vcpkg-toolchains
build/vcpkg-triplets
build/qt-patches
pnpm-lock.yaml


================================================
FILE: .prettierrc.json
================================================
{}


================================================
FILE: .vscode/c_cpp_properties.json
================================================
{
  "configurations": [
    {
      "name": "Linux",
      "includePath": [
        "${workspaceFolder}/build/wasm/vcpkg_installed/wasm32-emscripten-qt-threads/include",
        "${workspaceFolder}/build/wasm/vcpkg_installed/wasm32-emscripten-qt-threads/include/qgis",
        "${workspaceFolder}/build/emsdk/upstream/emscripten/system/include"
      ],
      "defines": [],
      "compilerPath": "/usr/bin/gcc",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "clang-x64"
    }
  ],
  "version": 4
}


================================================
FILE: .vscode/settings.json
================================================
{
  "workbench.editor.enablePreview": false,
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  "files.insertFinalNewline": true,
  "files.associations": {
    "**/package.json": "json",
    "**/*.json": "jsonc",
    "**/*.svg": "xml",
    "**/*.cpp": "cpp",
    "**/*.hpp": "cpp",
    "functional": "cpp"
  },
  "terminal.integrated.env.linux": {
    "PATH": "${workspaceRoot}/build/emsdk:${workspaceRoot}/build/emsdk/upstream/emscripten:${workspaceRoot}/build/emsdk/upstream/bin:${workspaceRoot}/build/vcpkg:${env:PATH}"
  },
  "search.exclude": {
    "build/emsdk": true,
    "build/vcpkg": true,
    "docs/api": true,
    "*/*/node_modules": true,
    "*/*/dist": true,
    "*/*/temp": true,
    "*/*/.vitepress/dist": true
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "C_Cpp.autoAddFileAssociations": false,
  "C_Cpp.clang_format_path": "${workspaceRoot}/node_modules/clang-format/bin/linux_x64/clang-format",
  "cmake.cmakePath": "${workspaceFolder}/build/vcpkg/downloads/tools/cmake-3.27.1-linux/cmake-3.27.1-linux-x86_64/bin/cmake",
  "cmake.environment": {
    "VCPKG_BINARY_SOURCES": "clear"
  },
  "cmake.configureSettings": {
    "CMAKE_TOOLCHAIN_FILE": "${workspaceFolder}/build/vcpkg/scripts/buildsystems/vcpkg.cmake",
    "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${workspaceFolder}/build/vcpkg-toolchains/qgis-js.cmake",
    "VCPKG_TARGET_TRIPLET": "wasm32-emscripten-qt-threads",
    "VCPKG_OVERLAY_TRIPLETS": "${workspaceFolder}/build/vcpkg-triplets",
    "VCPKG_OVERLAY_PORTS": "${workspaceFolder}/build/vcpkg-ports"
  },
  "cmake.buildDirectory": "${workspaceFolder}/build/wasm",
  "cmake.emscriptenSearchDirs": ["build/emsdk/upstream/emscripten"],
  "prettier.configPath": ".prettierrc.json",
  "prettier.documentSelectors": [
    "**/*.{js,cjs,json,ts,vue,html,css,svg,yaml,md,webmanifest,clang-format,npmrc}"
  ],
  "clang-format.executable": "${workspaceRoot}/node_modules/clang-format/bin/linux_x64/clang-format"
}


================================================
FILE: CHANGELOG.md
================================================
This document describes changes between tagged qgis-js versions

## 4.1.0 (in development)

- Replaced `mapLayers()` with `layerTreeRoot()`. (#25)
  - Exposing the full layer tree hierarchy (groups, nested layers, visibility, expand/collapse).
  - Use `layerTreeRoot().findLayers()` as a migration path for flat layer access.
- Added `QgsMapLayer` and `QgsVectorLayer` wrappers with `name`, `opacity`, `id()`, `type()`, and `subsetString()`. (#59)
- Added layer legend support. (#59)
  - `QgsLayerTreeLayer.legendNodes()` returns individual legend entries with `label()` and `symbolImage()`.
  - `renderLegend()` renders the full project legend as a high-DPI PNG (base64 data URL).
  - `QgsLayerTreeGroup.renderLegend()` and `QgsLayerTreeLayer.renderLegend()` for subtree/single-layer legends.
- Added optional `layerIds` parameter to all render functions (`renderImage`, `renderXYZTile`, `renderJob`, `fullExtent`, `renderLegend`). (#59)
  - Enables rendering subsets of layers and composing multiple OL layers from different QGIS layer groups.
  - All three OL data sources (`QgisCanvasDataSource`, `QgisXYZDataSource`, `QgisJobDataSource`) accept `layerIds` in options.
- Added `loadLayerDefinition()` to load .qlr files at runtime into the project layer tree. (#59)

## 4.0.0 (16. March 2026)

- Major version bump to align with QGIS 4 (based on QGIS 4.0.0). (#39)
  - Upstreamed patches to the QGIS repository for long-term maintainability.
  - Made possible by the QGIS.org grant programme 2025.
  - Special thanks to Matthias for the substantial contributions.
- Dependency updates:
  - Updated Qt to 6.10.1
  - Updated emsdk to 5.0.2
  - Updated Node to 22.16.0
  - Updated Vite to 8.0.0
  - Updated OpenLayers ("ol") to 10.8.0
- Refactored `Rectangle` and `PointXY` types to `QgsRectangle` and `QgsPointXY`
  across the codebase. This is a breaking API change. (#53)
- Added API to get/set global and project variables.
- Switched from pinned vcpkg to git submodule (based on QGIS).


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.15)

project(qgis-js CXX)

set(CMAKE_CXX_STANDARD 17)

find_package(unofficial-sqlite3 CONFIG REQUIRED)
find_package(PROJ CONFIG REQUIRED)
find_package(GEOS CONFIG REQUIRED)
find_package(GDAL CONFIG REQUIRED)
find_package(expat CONFIG REQUIRED)
find_package(libzip CONFIG REQUIRED)
find_package(exiv2 CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
find_package(zstd CONFIG REQUIRED)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Xml Network Concurrent Core5Compat PrintSupport Widgets)

find_package(Qt6Keychain CONFIG REQUIRED)

# Set debug prefix early so all find_library calls use it
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
  set(qgis_debug_prefix "debug/")
  set(spatialindex_name "spatialindexd")
else()
  set(qgis_debug_prefix "")
  set(spatialindex_name "spatialindex")
endif()

find_library(SPATIALINDEX_LIBRARY ${spatialindex_name} PATHS ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/${qgis_debug_prefix}lib NO_DEFAULT_PATH REQUIRED)

find_path(QGIS_INCLUDE_DIR
  NAMES qgis.h
  PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include"
  PATH_SUFFIXES qgis
  NO_DEFAULT_PATH
)

find_library(
 QGIS_LIBRARY
 NAMES qgis_core
 PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/${qgis_debug_prefix}lib"
 PATH_SUFFIXES qgis
 NO_DEFAULT_PATH
)

# because on the initial build the Qt toolchain file is not yet generated and therefore not included by qgis-js.cmake
# this will ensure that the Qt toolchain file is included after qtbase is built
if(EXISTS ${QT_TOOLCHAIN_FILE})
  set(QT_CHAINLOAD_TOOLCHAIN_FILE ${EMSCRIPTEN_TOOLCHAIN_FILE})
  include(${QT_TOOLCHAIN_FILE})
else()
  message(FATAL_ERROR "Could not find Qt toolchain file: ${QT_TOOLCHAIN_FILE}")
endif()

# since Qt 6.3 qt_standard_project_setup should be used so set some default values
qt_standard_project_setup()

set(QGISJS_SOURCES
 src/qgis-js.cpp
 src/api/QgisApi.cpp
)

# this creates also .html + qtloader.js + adds various flags to the build
qt_add_executable(qgis-js MANUAL_FINALIZATION ${QGISJS_SOURCES})


target_compile_options(qgis-js PRIVATE "-fwasm-exceptions")
target_link_options(qgis-js PRIVATE "-fwasm-exceptions")

# TODO reenable -msimd128
# target_compile_options(qgis-js PRIVATE "-msimd128")
# target_link_options(qgis-js PRIVATE "-msimd128")

target_include_directories(qgis-js PRIVATE ${QGIS_INCLUDE_DIR})

target_link_libraries(qgis-js PRIVATE
  Qt6::Xml
  Qt6::Concurrent
  Qt6::Network
  Qt6::Core
  Qt6::Gui
  Qt6::Core5Compat
  Qt6::PrintSupport
  Qt6::Widgets # because QgsApplication -> QApplication
  )

set(QGIS_PROVIDERS_LIST
  provider_arcgisfeatureserver
  provider_arcgismapserver
  provider_delimitedtext
  provider_wms
  provider_wcs
)

foreach (provider ${QGIS_PROVIDERS_LIST})
 find_library(
  QGIS_${provider}_LIBRARY
  NAMES ${provider}_a
  PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/${qgis_debug_prefix}lib"
  PATH_SUFFIXES qgis
  NO_DEFAULT_PATH
 )
 target_link_libraries(qgis-js PRIVATE ${QGIS_${provider}_LIBRARY}
 )
endforeach ()

# qgis_core must come after providers (they depend on it)
target_link_libraries(qgis-js PRIVATE ${QGIS_LIBRARY})

target_link_libraries(qgis-js PRIVATE PROJ::proj)
target_link_libraries(qgis-js PRIVATE unofficial::sqlite3::sqlite3)
target_link_libraries(qgis-js PRIVATE GEOS::geos_c)
target_link_libraries(qgis-js PRIVATE GDAL::GDAL)
target_link_libraries(qgis-js PRIVATE expat::expat)
target_link_libraries(qgis-js PRIVATE protobuf::libprotobuf-lite)
target_link_libraries(qgis-js PRIVATE ${SPATIALINDEX_LIBRARY})
target_link_libraries(qgis-js PRIVATE libzip::zip)
target_link_libraries(qgis-js PRIVATE Qt6Keychain::Qt6Keychain)
target_link_libraries(qgis-js PRIVATE $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>)

qt_finalize_executable(qgis-js)

#
# emcc settings (see https://emsettings.surma.technology/)
#
# NOTE: We set our flags after qt_finalize_executable in order to override the flags set by Qt
#
# Flags set by Qt:
# -s PTHREAD_POOL_SIZE=4
# -s INITIAL_MEMORY=50MB 
# -s EXPORTED_RUNTIME_METHODS=UTF16ToString,stringToUTF16,JSEvents,specialHTMLTargets,FS
# -s MAX_WEBGL_VERSION=2
# -s FETCH=1 
# -s WASM_BIGINT=1
# -s STACK_SIZE=5MB
# -s MODULARIZE=1
# -s EXPORT_NAME=createQtAppInstance
# -s ALLOW_MEMORY_GROWTH
#- s ASYNCIFY_IMPORTS=qt_asyncify_suspend_js,qt_asyncify_resume_js
# -s ERROR_ON_UNDEFINED_SYMBOLS=1

# Emscripten Runtime
target_link_options(qgis-js PRIVATE "SHELL: \
-s EXPORT_ES6"
)

# FS (see https://emscripten.org/docs/api_reference/Filesystem-API.html)
target_link_options(qgis-js PRIVATE "SHELL: \
-s FORCE_FILESYSTEM=1"
)

# Threading (see https://emscripten.org/docs/porting/pthreads.html)
set(MINIMAL_THREAD_POOL_SIZE "4")
set(MAXIMAL_THREAD_POOL_SIZE "16")
target_link_options(qgis-js PRIVATE "SHELL: \
-s PTHREAD_POOL_SIZE=\"Math.min(Math.max(((navigator&&navigator.hardwareConcurrency)||${MINIMAL_THREAD_POOL_SIZE}),${MINIMAL_THREAD_POOL_SIZE}),${MAXIMAL_THREAD_POOL_SIZE})\" \
-s PTHREAD_POOL_SIZE_STRICT=2 \
-s PTHREAD_POOL_DELAY_LOAD=1 \
-s MALLOC=mimalloc"
)

target_link_options(qgis-js PRIVATE "SHELL: \
--preload-file ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/share/proj@/proj \
--preload-file ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/share/qgis/resources/srs.db@/qgis/resources/srs.db \
--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/src/qt.conf@/qt.conf"
)

# Non-release builds: optimize link step to reduce function count (237K+ causes V8 OOM in Chrome)
# This only affects the post-link wasm-opt pass, not source-level compilation
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
  target_link_options(qgis-js PRIVATE "-O2")
endif()

# Debug build settings
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
  target_compile_options(qgis-js PRIVATE "-Og")
  # DWARF debug info (see https://developer.chrome.com/blog/faster-wasm-debugging/)
  target_link_options(qgis-js PRIVATE "-gseparate-dwarf" "-gdwarf-5" "-gsplit-dwarf" "-gpubnames")
  # Allow the wasm function table to grow dynamically (debug builds have more indirect call targets)
  target_link_options(qgis-js PRIVATE "SHELL:-s ALLOW_TABLE_GROWTH=1")
endif()

# TODO remove this fix (see https://github.com/emscripten-core/emscripten/issues/21844)
target_link_options(qgis-js PRIVATE "SHELL: \
-s EXPORTED_FUNCTIONS=_main,__emscripten_thread_crashed,__embind_initialize_bindings"
)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to qgis-js

Thank you for considering contributing to qgis-js! We welcome all contributions, big or small 🙏

## QGIS Code of Conduct

Please note that for this project the [QGIS Code of Conduct](https://qgis.org/en/site/getinvolved/governance/codeofconduct/codeofconduct.html) also applies.

## Getting Started

To get started with contributing, please follow these steps:

1. Fork the repository and clone it to your local machine.
2. See the [README](../README.md) for instructions on how to build the project.
3. Make your changes and test them locally.
4. Submit a pull request with your changes.

## Code Style

Please follow the existing code style when making changes. We use [Prettier](https://prettier.io/) and [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) to enforce consitent code style, so please make sure your changes pass the linter by running `npm run lint`.

## Reporting Bugs

If you find a bug, please open an issue on the [issue tracker](https://github.com/qgis/qgis-js/issues) with a detailed description of the problem and steps to reproduce it.

## Contact

If you have any questions or concerns, please reach out as follows:

- [qgis-js issues](https://github.com/qgis/qgis-js/issues)
- [qgis-js discussions](https://github.com/qgis/qgis-js/discussions)
- [QGIS Developers mailing list](https://lists.osgeo.org/mailman/listinfo/qgis-developer)

> For general questions about QGIS, have a look at the [Get Involved in the QGIS Community](https://qgis.org/en/site/getinvolved/index.html) site


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

In addition, as a special exception, the QGIS Development Team gives
permission to link the code of this program with the Qt library,
including but not limited to the following versions (both free and
commercial): Qt/Non-commercial Windows, Qt/Windows, Qt/X11, Qt/Mac, and
Qt/Embedded (or with modified versions of Qt that use the same license
as Qt), and distribute linked combinations including the two. You must
obey the GNU General Public License in all respects for all of the code
used other than Qt. If you modify this file, you may extend this
exception to your version of the file, but you are not obligated to do
so. If you do not wish to do so, delete this exception statement from
your version.


            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.


================================================
FILE: README.md
================================================
# qgis-js

**QGIS core ported to WebAssembly to run it on the web platform**

Version: `4.0.0` (based on QGIS 4.0.0)

[qgis-js Repository](https://github.com/qgis/qgis-js) | [qgis-js Website](https://qgis.github.io/qgis-js)

> ⚠️🧪 **Work in progress**! Currently this project is in public beta and only does very basic things like loading a QGIS project and rendering it to an image _(see [Features](#features) and [Limitations](#limitations))_

> 🌱👋 **Help wanted**! Please try out your QGIS projects and report [issues](https://github.com/qgis/qgis-js/issues) and [ideas](https://github.com/qgis/qgis-js/discussions/categories/ideas) on GitHub. We are also warmly welcoming contributions to this project _(see [Contributing](#contributing))_

## About

This project provides recipes to compile [QGIS](https://qgis.org/) core and its [dependencies](#libraries) to [WebAssembly](https://webassembly.org/) using [Emscripten](https://emscripten.org/), [CMake](https://cmake.org/) and [vcpkg](https://vcpkg.io).

qgis-js provides a JavaScript/TypeScript API to interact with QGIS, load projects and render beautiful QGIS-based maps on the web platform (see [Features](#features)).

Please note that our focus is currently on making the QGIS core usable. The project does not aim to bring the full QGIS desktop application, GUI library, or Python bindings (see [Limitations](#limitations)).

> 📚 See the [qgis-js Website](https://qgis.github.io/qgis-js) or [`./docs`](https://github.com/qgis/qgis-js/tree/main/docs) for more detailed information

## Packages

| Package                                                  | Description                                                           | npm                                                                                                                   |
| -------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| **[qgis-js](./packages/qgis-js/README.md)**              | The qgis-js API (which also ships the `.wasm` binary)                 | [![qgis-js on npm](https://img.shields.io/npm/v/qgis-js)](https://www.npmjs.com/package/qgis-js)                      |
| **[@qgis-js/ol](./packages/qgis-js-ol/README.md)**       | [OpenLayers](https://openlayers.org/) sources to display qgis-js maps | [![@qgis-js/ol on npm](https://img.shields.io/npm/v/@qgis-js/ol)](https://www.npmjs.com/package/@qgis-js/ol)          |
| **[@qgis-js/utils](./packages/qgis-js-utils/README.md)** | Utilities to integrate qgis-js into web applications                  | [![@qgis-js/utils on npm](https://img.shields.io/npm/v/@qgis-js/utils)](https://www.npmjs.com/package/@qgis-js/utils) |

## Getting started

| Example                              | Source code                                                                | StackBlitz                                                                                                                                                                                                                                              |
| ------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 📐 **Using the qgis-js API example** | [`docs/examples/qgis-js-example-api`](./docs/examples/qgis-js-example-api) | [![Open the "Using the qgis-js API" example in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/qgis/qgis-js/tree/main/docs/examples/qgis-js-example-api?file=main.js&title=qgis-js-example-api) |
| 🗺️ **Minimal OpenLayers example**    | [`docs/examples/qgis-js-example-ol`](./docs/examples/qgis-js-example-ol)   | [![Open the "Minimal OpenLayers" example in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/qgis/qgis-js/tree/main/docs/examples/qgis-js-example-ol?file=main.js&title=qgis-js-example-ol)      |

## Compatibility

A modern desktop browser is needed. At the moment we only support/test **Chromium-based browsers (>= 95)** and **Firefox (>= 100)**

> 📚 See [docs/compatibility.md](docs/compatibility.md) for more details

## Features

This project is a work in progress. Currently it provides the following features:

- QGIS core (and its [dependencies](#libraries)) compiled to WebAssembly
  - JavaScript/TypeScript API to interact QGIS core
- Loading of QGIS projects
- Non-blocking rendering of QGIS maps/tiles to [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData?retiredLocale=de)
- Optional [OpenLayers](https://openlayers.org/) integration

## Limitations

Compared to the native build of QGIS, there are various limitations:

- The API surface is very limited at the moment
- Network-based layers (e.g. WMS, WFS, WMTS, XYZ, COG, Vector Tiles) are not supported at the moment
- No Python (PyQGIS) available
- No Qt GUI provided
- Some providers that need to communicate with a server using sockets will probably never work without proxies (e.g. PostgreSQL)

## How to build qgis-js

> 💡 NOTE: To just use qgis-js you don't need to build it yourself, you can install it from npm. See the provided [Packages](#packages).

### Install dependencies

#### Install the following **system packages** (on Ubuntu 22.04):

```
sudo apt-get install pkg-config ninja-build flex bison
```

#### Install dependencies with pnpm:

```
npx pnpm install
```

> This will also invoke `./qgis-js.ts -v install` on "postinstall" which
>
> - downloads and installs emsdk in `build/emdsk`
> - downloads and installs vcpkg in `build/vcpkg`
> - boostraps vcpkg and downlaod the ports sources
>
> see also [`build/scripts/install.sh`](./build/scripts/install.sh) for manual installation

### Compile qgis-js (and its [dependencies](#libraries)) with Emscripten

```
npm run compile
```

> - Can also be ivoked with `compile:debug` or `compile:release`, see [Build types](#Build-types)
> - Will take about 30 minutes on a modern machine to compile all the vcpkg ports during the first run... ☕
> - see also [`build/scripts/compile.sh`](./build/scripts/compile.sh) for manual compiltion

### Build `qgis-js` packages

You want to compile with a `Release` [build type](#build-types) first

```
npm run compile:release
```

After successful compilation, you can build the packages with Vite:

```
npm run build
```

> see the [packages listed at the beginning of this README](#packages)

### Development

You probably want to compile with a `Dev` or `Debug` [build type](#build-types) first

```
npm run compile:dev
```

Start a Vite development server:

```
npm run dev
```

Open your browser at http://localhost:5173

## Libraries

<!--NOTE: this can be generated with "./qgis-js.ts libs -o markdown"-->

| Library                                                                                                                                                                                                                                                                                                                                                 | License                      | Links                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **abseil** (20260107.1)<br /><div style="max-width:30em">_Abseil is an open-source collection of C++ library code designed to augment the C++ standard library. The Abseil library code is collected from Google's own C++ code base, has been extensively tested and used in production, and is the same code we depend on in our daily coding lives._ | Apache-2.0                   | [Website](https://github.com/abseil/abseil-cpp) - [Source code](https://github.com/abseil/abseil-cpp)                       |
| **double-conversion** (3.4.0)<br /><div style="max-width:30em">_Efficient binary-decimal and decimal-binary conversion routines for IEEE doubles._                                                                                                                                                                                                      |                              | [Website](https://github.com/google/double-conversion) - [Source code](https://github.com/google/double-conversion)         |
| **egl-registry** (2025-05-27)<br /><div style="max-width:30em">_EGL API and Extension Registry_                                                                                                                                                                                                                                                         |                              | [Website](https://github.com/KhronosGroup/EGL-Registry) - [Source code](https://github.com/KhronosGroup/EGL-Registry)       |
| **exiv2** (0.28.8)<br /><div style="max-width:30em">_Image metadata library and tools_                                                                                                                                                                                                                                                                  | GPL-2.0-or-later             | [Website](https://exiv2.org) - [Source code](https://github.com/Exiv2/exiv2)                                                |
| **expat** (2.7.4)<br /><div style="max-width:30em">_XML parser library written in C_                                                                                                                                                                                                                                                                    | MIT                          | [Website](https://github.com/libexpat/libexpat) - [Source code](https://github.com/libexpat/libexpat)                       |
| **freetype** (2.13.3)<br /><div style="max-width:30em">_A library to render fonts._                                                                                                                                                                                                                                                                     | FTL OR GPL-2.0-or-later      | [Website](https://www.freetype.org/) - [Source code](https://gitlab.freedesktop.org/freetype/freetype)                      |
| **gdal** (3.12.2)<br /><div style="max-width:30em">_The Geographic Data Abstraction Library for reading and writing geospatial raster and vector data_                                                                                                                                                                                                  |                              | [Website](https://gdal.org) - [Source code](https://github.com/OSGeo/gdal)                                                  |
| **geos** (3.14.1)<br /><div style="max-width:30em">_Geometry Engine Open Source_                                                                                                                                                                                                                                                                        | LGPL-2.1-only                | [Website](https://libgeos.org/)                                                                                             |
| **inih** (62)<br /><div style="max-width:30em">_Simple .INI file parser_                                                                                                                                                                                                                                                                                | BSD-3-Clause                 | [Website](https://github.com/benhoyt/inih) - [Source code](https://github.com/benhoyt/inih)                                 |
| **json-c** (0.18-20240915)<br /><div style="max-width:30em">_A JSON implementation in C_                                                                                                                                                                                                                                                                | MIT                          | [Website](https://github.com/json-c/json-c) - [Source code](https://github.com/json-c/json-c)                               |
| **libb2** (0.98.1)<br /><div style="max-width:30em">_C library providing BLAKE2b, BLAKE2s, BLAKE2bp, BLAKE2sp_                                                                                                                                                                                                                                          |                              | [Website](https://github.com/BLAKE2/libb2) - [Source code](https://github.com/BLAKE2/libb2)                                 |
| **libgeotiff** (1.7.4)<br /><div style="max-width:30em">_Libgeotiff is an open source library on top of libtiff for reading and writing GeoTIFF information tags._                                                                                                                                                                                      | MIT                          | [Website](https://github.com/OSGeo/libgeotiff)                                                                              |
| **libiconv** (1.18)<br /><div style="max-width:30em">_iconv() text conversion._                                                                                                                                                                                                                                                                         |                              | [Website](https://www.gnu.org/software/libiconv/)                                                                           |
| **libjpeg-turbo** (3.1.3)<br /><div style="max-width:30em">_libjpeg-turbo is a JPEG image codec that uses SIMD instructions (MMX, SSE2, NEON, AltiVec) to accelerate baseline JPEG compression and decompression on x86, x86-64, ARM, and PowerPC systems._                                                                                             | BSD-3-Clause                 | [Website](https://github.com/libjpeg-turbo/libjpeg-turbo)                                                                   |
| **liblzma** (5.8.2)<br /><div style="max-width:30em">_Compression library with an API similar to that of zlib._                                                                                                                                                                                                                                         |                              | [Website](https://tukaani.org/xz/)                                                                                          |
| **libpng** (1.6.55)<br /><div style="max-width:30em">_libpng is a library implementing an interface for reading and writing PNG (Portable Network Graphics) format files_                                                                                                                                                                               | libpng-2.0                   | [Website](https://github.com/pnggroup/libpng)                                                                               |
| **libspatialindex** (2.0.0)<br /><div style="max-width:30em">_C++ implementation of R\*-tree, an MVR-tree and a TPR-tree with C API._                                                                                                                                                                                                                   | MIT                          | [Website](http://libspatialindex.github.com)                                                                                |
| **libzip** (1.11.4)<br /><div style="max-width:30em">_A C library for reading, creating, and modifying zip archives._                                                                                                                                                                                                                                   | BSD-3-Clause                 | [Website](https://github.com/nih-at/libzip)                                                                                 |
| **md4c** (0.5.2)<br /><div style="max-width:30em">_MD4C is a C library providing a Markdown parser._                                                                                                                                                                                                                                                    | MIT                          | [Website](https://github.com/mity/md4c) - [Source code](https://github.com/mity/md4c)                                       |
| **nlohmann-json** (3.12.0)<br /><div style="max-width:30em">_JSON for Modern C++_                                                                                                                                                                                                                                                                       | MIT                          | [Website](https://github.com/nlohmann/json) - [Source code](https://github.com/nlohmann/json)                               |
| **opengl-registry** (2026-01-26)<br /><div style="max-width:30em">_OpenGL, OpenGL ES, and OpenGL ES-SC API and Extension Registry_                                                                                                                                                                                                                      |                              | [Website](https://github.com/KhronosGroup/OpenGL-Registry) - [Source code](https://github.com/KhronosGroup/OpenGL-Registry) |
| **opengl** (2022-12-04)<br /><div style="max-width:30em">_Open Graphics Library (OpenGL)[3][4][5] is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics._                                                                                                                                 |                              |                                                                                                                             |
| **pcre2** (10.47)<br /><div style="max-width:30em">_Regular Expression pattern matching using the same syntax and semantics as Perl 5._                                                                                                                                                                                                                 | BSD-3-Clause                 | [Website](https://github.com/PCRE2Project/pcre2)                                                                            |
| **proj** (9.7.1)<br /><div style="max-width:30em">_PROJ library for cartographic projections_                                                                                                                                                                                                                                                           | MIT                          | [Website](https://proj.org/) - [Source code](https://github.com/OSGeo/PROJ)                                                 |
| **protobuf** (6.33.4)<br /><div style="max-width:30em">_Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data._                                                                                                                                                                                             | BSD-3-Clause                 | [Website](https://github.com/protocolbuffers/protobuf) - [Source code](https://github.com/protocolbuffers/protobuf)         |
| **qgis** (4.0.0)<br /><div style="max-width:30em">_QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)_                                                                                                                                                                                                     | GPL-2.0                      | [Website](https://www.qgis.org/) - [Source code](https://github.com/qgis/QGIS)                                              |
| **qt5compat** (6.10.1)<br /><div style="max-width:30em">_The Qt 5 Core Compat module contains the Qt 5 Core APIs that were removed in Qt 6. The module facilitates the transition to Qt 6._                                                                                                                                                             |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtbase** (6.10.1)<br /><div style="max-width:30em">_Qt Base (Core, Gui, Widgets, Network, ...)_                                                                                                                                                                                                                                                       |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtkeychain** (0.14.3)<br /><div style="max-width:30em">_(Unaffiliated with Qt) Platform-independent Qt6 API for storing passwords securely_                                                                                                                                                                                                           | BSD-3-Clause                 | [Website](https://github.com/frankosterfeld/qtkeychain) - [Source code](https://github.com/frankosterfeld/qtkeychain)       |
| **qtmultimedia** (6.10.1)<br /><div style="max-width:30em">_Qt Multimedia is an add-on module that provides a rich set of QML types and C++ classes to handle multimedia content._                                                                                                                                                                      |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtshadertools** (6.10.1)<br /><div style="max-width:30em">_The Qt Shader Tools module is designed to provide a set of tools and utilities to work with graphics shaders._                                                                                                                                                                             |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtsvg** (6.10.1)<br /><div style="max-width:30em">_Qt SVG provides classes for rendering and displaying SVG drawings in widgets and on other paint devices._                                                                                                                                                                                          |                              | [Website](https://www.qt.io/)                                                                                               |
| **qttools** (6.10.1)<br /><div style="max-width:30em">_A collection of tools and utilities that come with the Qt framework to assist developers in the creation, management, and deployment of Qt applications._                                                                                                                                        |                              | [Website](https://www.qt.io/)                                                                                               |
| **sqlite3** (3.51.2)<br /><div style="max-width:30em">_SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine._                                                                                                                                                               | blessing                     | [Website](https://sqlite.org/)                                                                                              |
| **tiff** (4.7.1)<br /><div style="max-width:30em">_A library that supports the manipulation of TIFF image files_                                                                                                                                                                                                                                        | libtiff                      | [Website](https://libtiff.gitlab.io/libtiff/) - [Source code](https://gitlab.com/libtiff/libtiff)                           |
| **utf8-range** (6.33.4)<br /><div style="max-width:30em">_Fast UTF-8 validation with Range algorithm (NEON+SSE4+AVX2)_                                                                                                                                                                                                                                  | MIT                          | [Website](https://github.com/protocolbuffers/protobuf) - [Source code](https://github.com/protocolbuffers/protobuf)         |
| **zlib** (1.3.1)<br /><div style="max-width:30em">_A compression library_                                                                                                                                                                                                                                                                               | Zlib                         | [Website](https://www.zlib.net/) - [Source code](https://github.com/madler/zlib)                                            |
| **zstd** (1.5.7)<br /><div style="max-width:30em">_Zstandard - Fast real-time compression algorithm_                                                                                                                                                                                                                                                    | BSD-3-Clause OR GPL-2.0-only | [Website](https://facebook.github.io/zstd/) - [Source code](https://github.com/facebook/zstd)                               |

## Build types

### `Dev` build type

- Optimized for **fast link times** during development
  - Symbols are present (e.g. meaningful stack traces)
  - Enables some Emscripten assertions
  - No DWARF debug info
- Empty `CMAKE_BUILD_TYPE` in CMake

### `Debug` build type

- Optimized for **debugging** with DWARF in Chromium-based browsers
  - Includes symbols and DWARF debug info
  - Enables most Emscripten assertions
- see [docs/debugging.md](docs/debugging.md) on how to get started
- Will take much longer to build than the default `Dev` build type
- `CMAKE_BUILD_TYPE=Debug` in CMake

### `Release` build type

- Optimized for **performance and minimal package size**
  - No symbols, assertions or DWARF debug info
  - Minified JavaScript files
- Will take much longer to build than the default `Dev` build type
- `CMAKE_BUILD_TYPE=Release` in CMake

## Contributing

Contributions welcome, see [CONTRIBUTING.md](CONTRIBUTING.md) for how to get started

## License

[GNU General Public License v2.0](LICENSE)


================================================
FILE: build/actions/clean.ts
================================================
import { CommandLineAction } from "@rushstack/ts-command-line";

import { QgisJsOptions } from "./lib/QgisJsOptions";

import "zx/globals";

export class CleanAction extends CommandLineAction {
  private _options: QgisJsOptions;

  public constructor(options: QgisJsOptions) {
    super({
      actionName: "clean",
      summary: "Clean qgis-js build tree",
      documentation: `Cleans emsdk, vcpkg and the qgis-js build tree in "build/wasm".`,
    });
    this._options = options;
  }

  protected onExecuteAsync(): Promise<void> {
    return new Promise<void>(async (resolve) => {
      const v = this._options.verbose;
      $.verbose = this._options.verbose;

      if (v) console.log(`- cleaning build/emsdk`);
      await $`(cd build/emsdk && git clean -xfd)`;

      if (v) console.log(`\n- cleaning build/vcpkg`);
      await $`(cd build/vcpkg && git clean -xfd)`;

      if (v) console.log(`\n- cleaning build/wasm`);
      await $`(rm -rf build/wasm && mkdir build/wasm)`;

      if (v) console.log(`\n`);
      resolve();
    });
  }
}


================================================
FILE: build/actions/compile.ts
================================================
import { dirname, join } from "path";
import { fileURLToPath } from "url";

import {
  CommandLineAction,
  CommandLineChoiceParameter,
  CommandLineFlagParameter,
} from "@rushstack/ts-command-line";

import { BuildType } from "./lib/BuildType";
import { QgisJsOptions } from "./lib/QgisJsOptions";

import "zx/globals";

const CMakeCacheFile = "build/wasm/CMakeCache.txt";

export class CompileAction extends CommandLineAction {
  private _options: QgisJsOptions;

  private _buildType: CommandLineChoiceParameter;
  private _debug: CommandLineFlagParameter;

  public constructor(options: QgisJsOptions) {
    super({
      actionName: "compile",
      summary: "Compiles qgis-js",
      documentation:
        "Uses emsdk, vcpkg and CMake to configure and build qgis-js.",
    });
    this._options = options;

    this._debug = this.defineFlagParameter({
      parameterLongName: "--debug",
      parameterShortName: "-d",
      description:
        "Prints debug information during build (and runs the compilation single threaded)",
    });

    this._buildType = this.defineChoiceParameter({
      parameterLongName: "--builde-type",
      parameterShortName: "-t",
      description: "Specify the CMake build type",
      alternatives: ["Dev", "Release", "Debug"],
      environmentVariable: "QGIS_JS_BUILD_TYPE",
      defaultValue: "Dev" as BuildType,
    });
  }

  protected onExecuteAsync(): Promise<void> {
    return new Promise<void>(async (resolve, reject) => {
      const v = this._options.verbose;
      $.verbose = true;

      const repo = join(dirname(fileURLToPath(import.meta.url)), "../..");

      const buildType = (this._buildType.value || "Dev") as BuildType;
      const debug = this._debug.value || false;

      if (v) console.log("Build Type:", buildType);

      // check if CMakeCache.txt needs to be regenerated
      if (fs.existsSync(CMakeCacheFile)) {
        if (v)
          console.log(
            `"${CMakeCacheFile}" exists, checking if it has to be regenerated`,
          );
        const lastBuild = await lastBuildType();
        if (lastBuild === buildType) {
          if (v)
            console.log(
              `Build type has not changed, skipping regenerating of ${CMakeCacheFile}`,
            );
        } else {
          if (v)
            console.log(
              `Build type has changed, regenerating ${CMakeCacheFile}`,
            );
          await $`rm ${CMakeCacheFile}`;

          if (v)
            console.log(`Build type has changed, removing build artifacts`);
          const artifacts = await glob(["build/wasm/{qt*,qgis-js*}"]);
          if (artifacts.length > 0) await $`rm ${artifacts}`;
        }
      } else {
        if (v) console.log(`"${CMakeCacheFile}" does not exist`);
      }

      // set environment variables for CMake
      process.env.VCPKG_BINARY_SOURCES = "clear";
      if (debug) {
        process.env.VERBOSE = "1";
        process.env.EMCC_DEBUG = "1";
      }

      // if vcpkg has installed its own cmake, use that, otherwise use the system cmake
      const cmake =
        await $`find . -iwholename './build/vcpkg/downloads/tools/cmake-*/*/bin/cmake' | grep bin/cmake || echo cmake`;

      // configure and build vcpgk dependencies
      try {
        await $`${cmake} \
-S . \
-B build/wasm \
-G Ninja \
-DCMAKE_TOOLCHAIN_FILE=${repo}/build/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${repo}/build/vcpkg-toolchains/qgis-js.cmake \
-DVCPKG_OVERLAY_TRIPLETS=./build/vcpkg-triplets \
-DVCPKG_OVERLAY_PORTS=./build/vcpkg-ports \
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten-qt-threads \
-DCMAKE_BUILD_TYPE=${buildType !== "Dev" ? buildType : ""}`;

        // build
        await $`${cmake} --build build/wasm`;
      } catch (error) {
        reject(error);
        return;
      }

      resolve();
    });
  }
}

async function lastBuildType(): Promise<BuildType | undefined> {
  const CMakeCacheFileContents = fs.readFileSync(CMakeCacheFile, "utf-8");
  if (!CMakeCacheFileContents || CMakeCacheFileContents.length === 0)
    return undefined;
  const match = CMakeCacheFileContents.match(/CMAKE_BUILD_TYPE:STRING=(\w+)/);
  return (match ? match[1] : "Dev") as BuildType;
}


================================================
FILE: build/actions/install.ts
================================================
import { CommandLineAction } from "@rushstack/ts-command-line";

import { QgisJsOptions } from "./lib/QgisJsOptions";

import "zx/globals";

export class InstallAction extends CommandLineAction {
  private _options: QgisJsOptions;

  public constructor(options: QgisJsOptions) {
    super({
      actionName: "install",
      summary: "Installs tools and downloads dependency sources",
      documentation: `Installs emsdk and vcpkg and then downlaods the source code of all ports with vcpkg.`,
    });
    this._options = options;
  }

  protected onExecuteAsync(): Promise<void> {
    return new Promise<void>(async (resolve) => {
      const v = this._options.verbose;
      $.verbose = true;

      if (v) console.log(`- installing emsdk`);
      // ensure git submodule is initialized
      await $`git submodule update --init build/emsdk`;
      // read engine "emsdk" from package.json
      const emsdkVersion = JSON.parse(fs.readFileSync("package.json", "utf-8"))
        .engines.emsdk;
      if (!emsdkVersion || emsdkVersion === "")
        throw new Error(`"emsdk" version not found in package.json`);
      await $`(cd build/emsdk && ./emsdk install ${emsdkVersion} && ./emsdk activate ${emsdkVersion})`;

      if (v) console.log(`\n- installing vcpkg`);
      // ensure git submodule is initialized
      await $`git submodule update --init build/vcpkg`;
      // bootstrap vcpkg
      await $`./build/vcpkg/bootstrap-vcpkg.sh -disableMetrics`;

      if (v) console.log(`\n- running vcpkg install`);
      await $`./build/vcpkg/vcpkg install \
--x-install-root=build/wasm/vcpkg_installed \
--only-downloads \
--overlay-triplets=build/vcpkg-triplets \
--overlay-ports=build/vcpkg-ports \
--triplet wasm32-emscripten-qt-threads`;

      if (v) console.log(`\n`);
      resolve();
    });
  }
}


================================================
FILE: build/actions/lib/BuildType.ts
================================================
export type BuildType = "Dev" | "Release" | "Debug";


================================================
FILE: build/actions/lib/QgisJsOptions.ts
================================================
export interface QgisJsOptions {
  verbose: boolean;
}


================================================
FILE: build/actions/libs.ts
================================================
import {
  CommandLineAction,
  CommandLineChoiceParameter,
} from "@rushstack/ts-command-line";

import "zx/globals";
import { QgisJsOptions } from "./lib/QgisJsOptions";

export class LibsAction extends CommandLineAction {
  private _options: QgisJsOptions;
  private _output: CommandLineChoiceParameter;

  public constructor(options: QgisJsOptions) {
    super({
      actionName: "libs",
      summary: "List the vcpkg libraries",
      documentation: `Generates a list of all vcpkg libraries with version and license.`,
    });
    this._options = options;

    this._output = this.defineChoiceParameter({
      parameterLongName: "--output",
      parameterShortName: "-o",
      description: "Specify the commands output type",
      alternatives: ["json", "markdown"],
      environmentVariable: "QGIS_JS_LIBS_OUTPUT",
      defaultValue: "json",
    });
  }

  protected onExecuteAsync(): Promise<void> {
    return new Promise<void>(async (resolve) => {
      $.verbose = this._options.verbose;

      const vcpgkPortList = JSON.parse(
        "" +
          (await $`./build/vcpkg/vcpkg list \
      --x-install-root=build/wasm/vcpkg_installed \
      --overlay-triplets=build/vcpkg-triplets \
      --overlay-ports=build/vcpkg-ports \
      --triplet wasm32-emscripten-qt-threads \
      --x-full-desc \
      --x-json`),
      );

      const custom = {
        qt6: {
          license: "LGPL-3.0",
          website: "https://www.qt.io/",
          source: "https://github.com/qt/qtbase",
        },
        qgis: {
          license: "GPL-2.0",
          website: "https://www.qgis.org/",
          source: "https://github.com/qgis/QGIS",
        },
        "qca-qt6": {
          license: "LGPL-2.1",
          website: "https://userbase.kde.org/QCA",
          source: "https://github.com/KDE/qca",
        },
      } as any;

      const libs = await Promise.all(
        Object.entries(vcpgkPortList)
          .filter(([key]) => key.endsWith("wasm32-emscripten-qt-threads"))
          .map<
            Promise<{
              name: string;
              version: string;
              description: string;
              website?: string;
              license?: string;
              source?: string;
            }>
          >(
            ([, value]) =>
              new Promise(async (resolve) => {
                const vcpgkPort = value as any;
                const response = await fetch(
                  `https://vcpkg.link/ports/${vcpgkPort["package_name"]}.json`,
                );
                if (response.status === 200) {
                  const vcpkgLink = await response.json();
                  resolve({
                    name: vcpgkPort["package_name"].replace(/-qt6$/, ""),
                    version: vcpgkPort["version"],
                    description: vcpgkPort["desc"][0] || "",
                    website: vcpkgLink["homepage_href"],
                    license: vcpkgLink["license"],
                    source: vcpkgLink["repository"]?.["href"],
                  });
                } else {
                  if (vcpgkPort["package_name"] in custom) {
                    resolve({
                      name: vcpgkPort["package_name"].replace(/-qt6$/, ""),
                      version: vcpgkPort["version"],
                      description: vcpgkPort["desc"][0] || "",
                      website: custom[vcpgkPort["package_name"]]["website"],
                      license: custom[vcpgkPort["package_name"]]["license"],
                      source: custom[vcpgkPort["package_name"]]["source"],
                    });
                  } else {
                    throw new Error("Could not find", vcpgkPort);
                  }
                }
              }),
          ),
      );
      if (this._output.value === "json") {
        console.log(JSON.stringify(libs, null, 2));
      } else if (this._output.value === "markdown") {
        const { tsMarkdown } = await import("ts-markdown");
        const rows = libs.map((lib) => ({
          Library:
            `**${lib.name}**` +
            (lib.version ? ` (${lib.version})` : "") +
            (lib.description
              ? `<br /><div style="max-width:30em">_${lib.description}_`
              : ""),
          License: lib.license || "",
          Links:
            [
              ...(lib.website ? [`[Website](${lib.website})`] : []),
              ...(lib.source ? [`[Source code](${lib.source})`] : []),
            ].join(" - ") || "",
        }));
        const table = {
          table: {
            columns: [
              { name: "Library" },
              { name: "License" },
              { name: "Links" },
            ],
            rows,
          },
        };
        console.log(tsMarkdown([table]));
      }
      resolve();
    });
  }
}


================================================
FILE: build/actions/size.ts
================================================
import type { BrotliCompress, Gzip } from "zlib";

import {
  CommandLineAction,
  CommandLineChoiceParameter,
} from "@rushstack/ts-command-line";

import { QgisJsOptions } from "./lib/QgisJsOptions";

import "zx/globals";

export class SizeAction extends CommandLineAction {
  private _options: QgisJsOptions;
  private _output: CommandLineChoiceParameter;

  public constructor(options: QgisJsOptions) {
    super({
      actionName: "size",
      summary: "Measure the size of qgis-js",
      documentation: `Generates a list of all qgis-js assets and checks the compression ratio with "gzip" and "brotli".`,
    });
    this._options = options;

    this._output = this.defineChoiceParameter({
      parameterLongName: "--output",
      parameterShortName: "-o",
      description: "Specify the commands output type",
      alternatives: ["json", "markdown"],
      environmentVariable: "QGIS_JS_SIZE_OUTPUT",
      defaultValue: "json",
    });
  }

  protected onExecuteAsync(): Promise<void> {
    return new Promise<void>(async (resolve) => {
      $.verbose = this._options.verbose;

      const fs = await import("fs");
      const zlib = await import("zlib");

      function measureCompression(
        filePath: string,
        compressionStream: BrotliCompress | Gzip,
      ) {
        return new Promise<number>((resolve, reject) => {
          let compressedSize = 0;
          const fileStream = fs.createReadStream(filePath);
          fileStream.pipe(compressionStream);
          compressionStream.on("data", (chunk) => {
            compressedSize += chunk.length;
          });
          compressionStream.on("end", () => {
            resolve(compressedSize);
          });
          fileStream.on("error", reject);
          compressionStream.on("error", reject);
        });
      }

      function humanFileSize(size: number) {
        const i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
        return (
          Number((size / Math.pow(1024, i)).toFixed(2)) +
          " " +
          ["B", "kB", "MB", "GB", "TB"][i]
        );
      }

      function spaceSaving(originalSize: number, compressedSize: number) {
        return Math.round((1 - compressedSize / originalSize) * 100);
      }

      const distDir = "packages/qgis-js/dist";

      const files = [
        "qgis.js",
        "assets/wasm/qgis-js.js",
        "assets/wasm/qgis-js.data",
        "assets/wasm/qgis-js.wasm",
      ];

      const filesSize = {} as {
        [key: string]: {
          bytes: number;
          bytesGzip: number;
          bytesBrotli: number;
        };
      };

      for (const filename of files) {
        const filePath = `${distDir}/${filename}`;
        if (fs.existsSync(filePath)) {
          const stat = fs.statSync(filePath);
          filesSize[filename] = {
            bytes: stat.size,
            bytesGzip: await measureCompression(filePath, zlib.createGzip()),
            bytesBrotli: await measureCompression(
              filePath,
              zlib.createBrotliCompress({
                params: {
                  // zlib.constants.BROTLI_DEFAULT_QUALITY is 11, which is too slow for stream compression,
                  // so we use the defaul from Apache httpd which is 5
                  // see https://httpd.apache.org/docs/2.4/mod/mod_brotli.html#brotlicompressionquality
                  [zlib.constants.BROTLI_PARAM_QUALITY]: 5,
                },
              }),
            ),
          };
        }
      }

      const total = Object.entries(filesSize).reduce(
        (acc: any, [, value]) => {
          acc.bytes += value.bytes;
          acc.bytesGzip += value.bytesGzip;
          acc.bytesBrotli += value.bytesBrotli;
          return acc;
        },
        {
          bytes: 0,
          bytesGzip: 0,
          bytesBrotli: 0,
        },
      );

      if (this._output.value === "json") {
        console.log(
          JSON.stringify(
            {
              total,
              files: filesSize,
            },
            null,
            2,
          ),
        );
      } else if (this._output.value === "markdown") {
        const { tsMarkdown } = await import("ts-markdown");

        console.log(
          `The size of the package is **\`${humanFileSize(
            total.bytes,
          )}\`** (uncompressed) or \`${humanFileSize(
            total.bytesBrotli,
          )}\` Brotli compressed (${spaceSaving(
            total.bytes,
            total.bytesBrotli,
          )}% space saving) / \`${humanFileSize(
            total.bytesGzip,
          )}\` Gzip compressed (${spaceSaving(
            total.bytes,
            total.bytesGzip,
          )}% space saving)`,
        );
        console.log("");
        console.log("It consists of the following files:");
        console.log("");

        const rows = Object.entries(filesSize).map(([file, size]) => ({
          "File name": `\`${file}\``,
          "Size (uncompressed)": `\`${humanFileSize(size.bytes)}\``,
          "Size (Brotli compressed)": `\`${humanFileSize(
            size.bytesBrotli,
          )}\` (${spaceSaving(size.bytes, size.bytesBrotli)}% space saving)`,
          "Size (Gzip compressed)": `\`${humanFileSize(
            size.bytesGzip,
          )}\` (${spaceSaving(size.bytes, size.bytesGzip)}% space saving)`,
        }));
        const table = {
          table: {
            columns: [
              { name: "File name" },
              { name: "Size (uncompressed)" },
              { name: "Size (Brotli compressed)" },
              { name: "Size (Gzip compressed)" },
            ],
            rows,
          },
        };
        console.log(tsMarkdown([table]));
      }
      resolve();
    });
  }
}


================================================
FILE: build/scripts/clean.sh
================================================
#!/bin/bash
set -eo pipefail

(cd build/emsdk; git clean -xfd)

(cd build/vcpkg; git clean -xfd)

rm -rf build/wasm && mkdir build/wasm


================================================
FILE: build/scripts/compile.sh
================================================
#!/bin/bash
set -eo pipefail

# if vcpkg has installed its own cmake, use that, otherwise use the system cmake
$(find . -iwholename './build/vcpkg/downloads/tools/cmake-*/*/bin/cmake' | grep bin/cmake || echo cmake) \
-S . \
-B build/wasm \
-G Ninja \
-DCMAKE_TOOLCHAIN_FILE=$PWD/build/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$PWD/build/vcpkg-toolchains/qgis-js.cmake \
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten-qt-threads \
-DCMAKE_BUILD_TYPE=

$(echo ./build/vcpkg/downloads/tools/cmake-*/*/bin/cmake) \
--build build/wasm


================================================
FILE: build/scripts/install.sh
================================================
#!/bin/bash
set -eo pipefail

git submodule update --init build/emsdk
build/emsdk/emsdk install 5.0.2;
build/emsdk/emsdk activate 5.0.2;

git submodule update --init build/vcpkg
./build/vcpkg/bootstrap-vcpkg.sh -disableMetrics

./build/vcpkg/vcpkg install \
--x-install-root=build/wasm/vcpkg_installed \
--only-downloads \
--triplet wasm32-emscripten-qt-threads


================================================
FILE: build/vcpkg-ports/libspatialindex/portfile.cmake
================================================
vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO libspatialindex/libspatialindex
    REF "${VERSION}"
    SHA512 a508a9ed4019641bdaaa53533505531f3db440b046a9c7d9f78ed480293200c51796c2d826a6bb9b4f9543d60bb0fef9e4c885ec3f09326cfa4d2fb81c1593aa
    HEAD_REF master
)

vcpkg_cmake_configure(
    SOURCE_PATH "${SOURCE_PATH}"
    WINDOWS_USE_MSBUILD
    OPTIONS
        -DCMAKE_DEBUG_POSTFIX=d
        -DSIDX_BUILD_TESTS:BOOL=OFF
)

vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/${PORT})  
vcpkg_fixup_pkgconfig()
vcpkg_copy_pdbs()

#Debug
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")

# Handle copyright
file(INSTALL "${SOURCE_PATH}/COPYING" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)


================================================
FILE: build/vcpkg-ports/libspatialindex/vcpkg.json
================================================
{
  "name": "libspatialindex",
  "version": "2.0.0",
  "description": "C++ implementation of R*-tree, an MVR-tree and a TPR-tree with C API.",
  "homepage": "http://libspatialindex.github.com",
  "license": "MIT",
  "dependencies": [
    {
      "name": "vcpkg-cmake",
      "host": true
    },
    {
      "name": "vcpkg-cmake-config",
      "host": true
    },
    "zlib"
  ]
}


================================================
FILE: build/vcpkg-ports/qgis/portfile.cmake
================================================
vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO qgis/QGIS
    REF 8d368f3efd5be7a2321ada8cb5663d8f478f7d0f
    SHA512 2d603d81ad2b37c809549ff4d05419adf7fb87e35500cdde3d8cda82b6cb8e5337552a4482b2a3a842fde81453383442bad93632070681e50502c12e57019036
    HEAD_REF master
)

file(REMOVE ${SOURCE_PATH}/cmake/FindQtKeychain.cmake)
file(REMOVE ${SOURCE_PATH}/cmake/FindGDAL.cmake)
file(REMOVE ${SOURCE_PATH}/cmake/FindGEOS.cmake)
file(REMOVE ${SOURCE_PATH}/cmake/FindEXIV2.cmake)
file(REMOVE ${SOURCE_PATH}/cmake/FindExpat.cmake)
file(REMOVE ${SOURCE_PATH}/cmake/FindIconv.cmake)

vcpkg_find_acquire_program(FLEX)
vcpkg_find_acquire_program(BISON)
vcpkg_find_acquire_program(PYTHON3)
get_filename_component(PYTHON3_PATH ${PYTHON3} DIRECTORY)
vcpkg_add_to_path(${PYTHON3_PATH})
vcpkg_add_to_path(${PYTHON3_PATH}/Scripts)
set(PYTHON_EXECUTABLE ${PYTHON3})

# Core options (matching build.sh / wasm.yml)
list(APPEND QGIS_OPTIONS -DFORCE_STATIC_LIBS:BOOL=ON)
list(APPEND QGIS_OPTIONS -DENABLE_TESTS:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DNATIVE_CRSSYNC_BIN=/bin/true)

# Disabled features (matching build.sh / wasm.yml)
list(APPEND QGIS_OPTIONS -DWITH_GUI:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_DESKTOP:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_BINDINGS:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_3D:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_EXIV2:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_PDAL:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_DRACO:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_QTSERIALPORT:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_QTPOSITIONING:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_QTWEBENGINE:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_AUTH:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_SPATIALITE:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_ANALYSIS:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_QGIS_PROCESS:BOOL=OFF)

# Additional disabled features for WASM/qgis-js
list(APPEND QGIS_OPTIONS -DWITH_POSTGRESQL:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_GRASS7:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_CUSTOM_WIDGETS:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_SERVER:BOOL=OFF)
# Note: WITH_EPT and WITH_COPC are NOT disabled - they use WITH_INTERNAL_LAZPERF
list(APPEND QGIS_OPTIONS -DWITH_APIDOC:BOOL=OFF)
list(APPEND QGIS_OPTIONS -DWITH_QUICK:BOOL=OFF)

list(APPEND QGIS_OPTIONS -DWITH_INTERNAL_POLY2TRI=ON)
list(APPEND QGIS_OPTIONS -DWITH_INTERNAL_SPATIALINDEX=OFF)
list(APPEND QGIS_OPTIONS -DWITH_INTERNAL_LAZPERF=ON)

# Point CMake to the installed Qt6, not the buildtrees
list(APPEND QGIS_OPTIONS -DQt6_DIR=${CURRENT_INSTALLED_DIR}/share/Qt6)
list(APPEND QGIS_OPTIONS -DCMAKE_PREFIX_PATH=${CURRENT_INSTALLED_DIR})

list(APPEND QGIS_OPTIONS -DQt6LinguistTools_DIR=${CURRENT_HOST_INSTALLED_DIR}/share/Qt6LinguistTools)

list(APPEND QGIS_OPTIONS -DQT_LRELEASE_EXECUTABLE=${CURRENT_HOST_INSTALLED_DIR}/tools/Qt6/bin/lrelease)

# QGIS likes to install auth and providers to different locations on each platform
# let's keep things clean and tidy and put them at a predictable location
list(APPEND QGIS_OPTIONS -DQGIS_PLUGIN_SUBDIR=lib)

# By default QGIS installs includes into "include" on Windows and into "include/qgis" everywhere else
# let's keep things clean and tidy and put them at a predictable location
list(APPEND QGIS_OPTIONS -DQGIS_INCLUDE_SUBDIR=include/qgis)

list(APPEND QGIS_OPTIONS -DWITH_INTERNAL_POLY2TRI=ON)

vcpkg_configure_cmake(
    SOURCE_PATH ${SOURCE_PATH}
    #PREFER_NINJA
    OPTIONS ${QGIS_OPTIONS}
    OPTIONS_DEBUG ${QGIS_OPTIONS_DEBUG}
    OPTIONS_RELEASE ${QGIS_OPTIONS_RELEASE}
)

vcpkg_install_cmake()


file(GLOB QGIS_CMAKE_PATH ${CURRENT_PACKAGES_DIR}/*.cmake)
if(QGIS_CMAKE_PATH)
    file(COPY ${QGIS_CMAKE_PATH} DESTINATION ${CURRENT_PACKAGES_DIR}/share/cmake/${PORT})
    file(REMOVE_RECURSE ${QGIS_CMAKE_PATH})
endif()

file(GLOB QGIS_CMAKE_PATH_DEBUG ${CURRENT_PACKAGES_DIR}/debug/*.cmake)
if( QGIS_CMAKE_PATH_DEBUG )
    file(REMOVE_RECURSE ${QGIS_CMAKE_PATH_DEBUG})
endif()

file(REMOVE_RECURSE
    ${CURRENT_PACKAGES_DIR}/debug/include
)
file(REMOVE_RECURSE # Added for debug porpose
    ${CURRENT_PACKAGES_DIR}/debug/share
)

# Handle copyright
file(INSTALL ${SOURCE_PATH}/COPYING DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright)


================================================
FILE: build/vcpkg-ports/qgis/vcpkg.json
================================================
{
  "name": "qgis",
  "version": "4.0.0",
  "port-version": 1,
  "homepage": "https://qgis.org",
  "description": "QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)",
  "dependencies": [
    "expat",
    "zlib",
    "zstd",
    "libspatialindex",
    {
      "name": "sqlite3",
      "default-features": false
    },
    "exiv2",
    "protobuf",
    {
      "name": "proj",
      "default-features": false
    },
    "geos",
    {
      "name": "gdal",
      "default-features": false
    },
    {
      "name": "qtbase",
      "default-features": false,
      "features": [
        "concurrent",
        "gui",
        "jpeg",
        "network",
        "png",
        "widgets",
        "sql",
        "sql-sqlite"
      ]
    },
    {
      "name": "qt5compat",
      "default-features": false
    },
    {
      "name": "qtkeychain-qt6",
      "default-features": false
    },
    {
      "name": "qtsvg",
      "default-features": false
    },
    {
      "name": "qtmultimedia",
      "default-features": false
    },
    {
      "name": "qttools",
      "default-features": false,
      "features": [
        "linguist"
      ]
    },
    {
      "name": "qttools",
      "host": true,
      "default-features": false,
      "features": [
        "linguist"
      ]
    },
    {
      "name": "vcpkg-cmake",
      "host": true
    },
    {
      "name": "vcpkg-cmake-config",
      "host": true
    }
  ]
}


================================================
FILE: build/vcpkg-toolchains/qgis-js.cmake
================================================

message(STATUS "Using 'qgis-js' toolchain")

# Setup EMSDK/EMSCRIPTEN_ROOT
if(NOT DEFINED ENV{EMSDK})
  get_filename_component(QGIS_JS_BUILD_EMSDK "${CMAKE_CURRENT_LIST_DIR}/../emsdk" ABSOLUTE)
  set(ENV{EMSDK} ${QGIS_JS_BUILD_EMSDK})
endif()
if(NOT EMSCRIPTEN_ROOT)
   if(NOT DEFINED ENV{EMSDK})
      message(FATAL_ERROR "emsdk not found")
   endif()
   set(EMSCRIPTEN_ROOT "$ENV{EMSDK}/upstream/emscripten")
endif()
if(NOT DEFINED ENV{EMSCRIPTEN_ROOT})
  set(ENV{EMSCRIPTEN_ROOT} ${EMSCRIPTEN_ROOT})
endif()

# Set EMSCRIPTEN_TOOLCHAIN_FILE for use by CMakeLists.txt
set(EMSCRIPTEN_TOOLCHAIN_FILE "${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake")

# Include Emscripten toolchain directly
include("${EMSCRIPTEN_TOOLCHAIN_FILE}")

# Set QT_TOOLCHAIN_FILE path for CMakeLists.txt to include
if(CMAKE_TOOLCHAIN_FILE)
  get_filename_component(VCPKG_ROOT_DIR ${CMAKE_TOOLCHAIN_FILE} DIRECTORY)
  get_filename_component(VCPKG_ROOT_DIR ${VCPKG_ROOT_DIR} DIRECTORY)
  get_filename_component(VCPKG_PACKAGES_PATH "${VCPKG_ROOT_DIR}/../packages" ABSOLUTE)
  set(QT_TOOLCHAIN_FILE "${VCPKG_PACKAGES_PATH}/qtbase_${VCPKG_TARGET_TRIPLET}/share/Qt6/qt.toolchain.cmake")
endif()

# Flags for all ports and qgis-js
# TODO reenable -msimd128
set(QGIS_JS_FLAGS "-pthread -fwasm-exceptions -sSUPPORT_LONGJMP=wasm")
set(CMAKE_C_FLAGS_INIT   "${CMAKE_C_FLAGS_INIT} ${QGIS_JS_FLAGS}")
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} ${QGIS_JS_FLAGS}")


================================================
FILE: build/vcpkg-triplets/wasm32-emscripten-qt-threads.cmake
================================================
message(STATUS "Using 'wasm32-emscripten-qt-threads' triplet")

set(VCPKG_TARGET_ARCHITECTURE wasm32)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Emscripten)

# to avoid building both debug and release of all libs uncomment the following line
# set(VCPKG_BUILD_TYPE "release")

set(VCPKG_ENV_PASSTHROUGH_UNTRACKED EMSDK EMSCRIPTEN EMSCRIPTEN_ROOT PATH)

# Tell autoconf configure scripts this is cross-compilation (fixes error 77)
set(VCPKG_MAKE_BUILD_TRIPLET "--host=wasm32-unknown-emscripten")

# this needs to be present for vcpkg installs, but also the same VCPKG_CHAINLOAD_TOOLCHAIN_FILE
# needs to be present when running CMake so that the project gets it
get_filename_component(QGISJS_TOOLCHAIN_FILE
   "${CMAKE_CURRENT_LIST_DIR}/../vcpkg-toolchains/qgis-js.cmake"
   ABSOLUTE)
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE ${QGISJS_TOOLCHAIN_FILE})

set(VCPKG_ENV_PASSTHROUGH_UNTRACKED EMSDK EMSCRIPTEN EMSCRIPTEN_ROOT PATH)



================================================
FILE: build/vite/CrossOriginIsolationPlugin.ts
================================================
import type { Plugin } from "vite";

export const CrossOriginIsolationResponseHeaders = {
  "Cross-Origin-Opener-Policy": "same-origin",
  "Cross-Origin-Embedder-Policy": "require-corp",
};

/**
 * A Vite plugin that adds Cross-Origin Isolation headers to the server responses.
 */
export default function CrossOriginIsolationPlugin(): Plugin {
  return {
    name: "CrossOriginIsolationPlugin",
    configureServer(server) {
      server.middlewares.use((_req, res, next) => {
        Object.entries(CrossOriginIsolationResponseHeaders).forEach(
          ([key, value]) => {
            res.setHeader(key, value);
          },
        );
        next();
      });
    },
  };
}


================================================
FILE: build/vite/DirectoryListingPlugin.ts
================================================
import { File, Folder } from "../../packages/qgis-js-utils";

import type { Plugin, ResolvedConfig } from "vite";

import { basename, resolve, join } from "path";
import { readdir } from "fs/promises";
import { Dirent } from "fs";

const FILTER_LIST = [".DS_Store", ".git", ".gitignore", ".env"];
const DIRECTORY_LISTING_FILENAME = "directory-listing.json";

let config: ResolvedConfig;

export default function DirectoryListingPlugin(
  directories: string | string[],
): Plugin {
  const directoriesToList =
    typeof directories === "string" ? [directories] : directories;
  for (const directory of directoriesToList) {
    if (!directory.startsWith("public/")) {
      throw new Error(
        `DirectoryListingPlugin: Directory ${directory} is not in the public folder`,
      );
    }
  }
  return {
    name: "DirectoryListingPlugin",
    configResolved(_config) {
      config = _config;
    },
    configureServer(server) {
      const dirs = directoriesToList.map(
        (directory) =>
          config.base +
          directory.replace(/^public\//, "") +
          `/${DIRECTORY_LISTING_FILENAME}`,
      );
      server.middlewares.use(async (req, res, next) => {
        if (req.url && dirs.includes(req.url)) {
          let dir = req.url;
          // remove potential base from start of the url
          if (dir.startsWith(config.base)) {
            dir = dir.slice(config.base.length);
          }
          // remove filename from url at the end
          dir = dir.slice(0, -`/${DIRECTORY_LISTING_FILENAME}`.length);

          const dirParts = dir.split("/");
          const dirBase = dirParts.slice(0, -1).join("/");
          const dirName = dirParts[dirParts.length - 1];

          const drectoryListing = await createDirectoryListing(
            join(process.cwd(), "public", dirBase),
            dirName,
            ".",
          );
          res.setHeader("Content-Type", "application/json");
          res.end(JSON.stringify(drectoryListing, null, 2));
        } else {
          next();
        }
      });
    },
    async generateBundle() {
      for (const dir of directoriesToList.map((directory) =>
        directory.replace(/^public\//, ""),
      )) {
        const drectoryListing = await createDirectoryListing(
          join(process.cwd(), "public"),
          dir,
          ".",
        );
        this.emitFile({
          type: "asset",
          fileName: dir + `/${DIRECTORY_LISTING_FILENAME}`,
          source: JSON.stringify(drectoryListing, null, 2),
        });
      }
    },
  };
}

async function readFolder(folder: string): Promise<Dirent[]> {
  return await readdir(folder, { withFileTypes: true });
}

async function createDirectoryListing(
  root: string,
  base: string,
  directory: string,
) {
  const directoryListing = await readFolder(join(root, base, directory));
  return directoryListing
    .filter((dirent) => dirent.isFile || dirent.isDirectory)
    .filter((dirent) => !FILTER_LIST.includes(dirent.name))
    .reduce(
      async (currentFolderPromise, entry: Dirent) => {
        const currentFolder = (await currentFolderPromise) as Folder;
        if (entry.isDirectory()) {
          currentFolder.entries.push(
            await createDirectoryListing(
              root,
              join(base, directory),
              entry.name,
            ),
          );
        } else {
          currentFolder.entries.push({
            name: entry.name,
            path: join(currentFolder.path, entry.name),
            type: "File",
          } as File);
        }
        return currentFolder;
      },
      Promise.resolve({
        name: basename(resolve(join(base, directory))),
        path: join(base, directory),
        type: "Folder",
        entries: [],
      } as Folder),
    );
}


================================================
FILE: build/vite/QgisRuntimePlugin.ts
================================================
import { join, resolve, basename } from "path";
import { existsSync, readFileSync } from "fs";
import { dirname } from "path";
import { fileURLToPath } from "url";

import { CrossOriginIsolationResponseHeaders } from "./CrossOriginIsolationPlugin";

import type { Plugin, ResolvedConfig } from "vite";

const __dirname = dirname(fileURLToPath(import.meta.url));

const RUNTIME_JS = "js";
const RUNTIME_WASM = "wasm";
const RUNTIME_WASM_MAP = "wasm.map";
const RUNTIME_WASM_DEBUG = "wasm.debug.wasm";
const RUNTIME_DATA = "data";

export const BASE_DIR = "wasm";

export interface Runtime {
  name: string;
  outputDir: string;
}

function patchEmccJs(content: string): string {
  // prevent setting of read-only property "stack" in Firefox
  // (will throw an error in strict mode)
  const search = `e.stack = arr.join("\\n");`;
  return content.replaceAll(
    search,
    `if (! (navigator.userAgent.indexOf("Firefox") !== -1)) { ${search} }`,
  );
}

export default function QgisRuntimePlugin(_runtime: Runtime | null): Plugin {
  let runtime: Runtime;
  if (_runtime === null) {
    throw new Error("QgisRuntimePlugin: No runtime specified");
  } else {
    runtime = _runtime;
  }

  let config: ResolvedConfig;
  let runtimeDir = () => `${config.build.assetsDir}/${BASE_DIR}`;

  const repoRoot = resolve(__dirname, "../..");

  function file(ending: string) {
    return `${runtime.name}.${ending}`;
  }

  const fileRuntimeJs = file(RUNTIME_JS);
  const fileRuntimeWasm = file(RUNTIME_WASM);
  const fileRuntimeWasmMap = file(RUNTIME_WASM_MAP);
  const fileRuntimeWasmDebug = file(RUNTIME_WASM_DEBUG);
  const fileRuntimeData = file(RUNTIME_DATA);

  const filesRuntime = [
    fileRuntimeJs,
    fileRuntimeWasmMap,
    fileRuntimeWasmDebug,
    fileRuntimeWasm,
    fileRuntimeData,
  ];

  return {
    name: "QgisRuntimePlugin",
    enforce: "pre",
    configResolved(_config) {
      config = _config;
    },
    configureServer(server) {
      const filesRuntimeDir = runtimeDir();
      const runtimeFiles = filesRuntime.map(
        (id) =>
          `${config.base}${filesRuntimeDir ? filesRuntimeDir + "/" : ""}${id}`,
      );
      server.middlewares.use((req, res, next) => {
        if (
          req.url &&
          runtimeFiles.some((runtimefile) => runtimefile === req.url)
        ) {
          const filePath = join(repoRoot, runtime.outputDir, basename(req.url));
          if (existsSync(filePath)) {
            const raw = readFileSync(filePath);
            res.statusCode = 200;
            Object.entries(CrossOriginIsolationResponseHeaders).forEach(
              ([key, value]) => {
                res.setHeader(key, value);
              },
            );
            if (
              filePath.endsWith("." + RUNTIME_WASM) ||
              filePath.endsWith("." + RUNTIME_DATA)
            ) {
              if (filePath.endsWith("." + RUNTIME_WASM)) {
                res.setHeader("Content-Type", "application/wasm");
              }
              res.end(raw);
            } else if (filePath.endsWith("." + RUNTIME_JS)) {
              const content = raw.toString();
              res.setHeader("Content-Type", "application/javascript");
              res.end(patchEmccJs(content));
            } else if (filePath.endsWith("." + RUNTIME_WASM_MAP)) {
              const content = raw.toString();
              res.setHeader("Content-Type", "application/json");
              res.end(content);
            }
          } else {
            console.log("404", filePath);
            res.statusCode = 404;
            res.end();
          }
        } else {
          next();
        }
      });
    },
    generateBundle() {
      filesRuntime.forEach((id) => {
        const filesRuntimeDir = runtimeDir();
        const filePath = join(repoRoot, runtime.outputDir, id);
        if (existsSync(filePath)) {
          const raw = readFileSync(filePath);

          let source = raw as Uint8Array;

          if (filePath.endsWith("." + RUNTIME_JS)) {
            const encoder = new TextEncoder();
            const content = patchEmccJs(raw.toString());
            source = encoder.encode(content);
          }

          this.emitFile({
            fileName: `${filesRuntimeDir ? filesRuntimeDir + "/" : ""}${id}`,
            type: "asset",
            source,
          });
        }
      });
    },
  };
}


================================================
FILE: docs/architecture.md
================================================
# Architecture

## qgis-js Repository

## qgis-js Build

## qgis-js Runtime


================================================
FILE: docs/bundling.md
================================================
# Bundling

qgis-js consists of two parts: The runtime generated by Emscripten and the TypeScript/JavaScript API, that can be seen as a wrapper around the runtime. The wrapper can be imported, used and bundled (e.g. tree-shaked) like any other JavaScript library. But it is important that the runtime is not modified and served as is.

See the [`qgis-js` Package `README.md`](../packages/qgis-js/README.md) for more information about the files involved. Everything inside `assets/wasm` is part of the runtime.

To not confuse any downstream bundler, the runtime is [dynamically loaded](../packages/qgis-js/src/loader.ts) in a way that it will not be processed. Therefore **it is up to the end user to include the runtime files in the final build**.

### Explicitly specifying the runtime location

The runtime location can be specified with the `prefix` configuration option. This is useful when the runtime is not in the same directory as the main script or served from a different server (e.g. a CDN).

```js
const { api } = await qgis({
  prefix: "/path/to/runtime/assets",
});
```

## Examples

### qgis-js with [Vite](https://vitejs.dev/)

One can use the [vite-plugin-static-copy](https://github.com/sapphi-red/vite-plugin-static-copy).

For an example see the [`vite.config.ts`](./examples/qgis-js-example-ol/vite.config.js) in the [qgis-js-example-ol](./examples/qgis-js-example-ol) project and note that the COOP/COEP headers have to be set after the plugin (see [`compatibility.md`](./compatibility.md) for more information).

### qgis.js with [Webpack](https://webpack.js.org/)

With Webpack one can use the [copy-webpack-plugin](https://www.npmjs.com/package/copy-webpack-plugin).

Note that the COOP/COEP headers have to be set in the `webpack.config.js` (see [`compatibility.md`](./compatibility.md) for more information).

### Using qgis-js from a CDN

An example of how to use qgis-js from a CDN (e.g. [jsDelivr](https://www.jsdelivr.com/)):

```html
<!doctype html>
<html lang="en">
  <head>
    <title>qgis-js</title>
  </head>
  <body>
    <script type="module">
      // import qgis-js from a CDN
      const { qgis } =
        await import("https://cdn.jsdelivr.net/npm/qgis-js/dist/qgis.js");
      // boot the qgis-js runtime
      const { api } = await qgis();
      // use the qgis-js api
      const rect = new api.QgsRectangle(1, 1, 42, 42);
      const center = rect.center();
      console.log(`Center: x: ${center.x}, y: ${center.y}`);
    </script>
  </body>
</html>
```

Note that the main script has to be explicitly loaded with `qgis-js/dist/qgis.js` (Or a prefix pointing to `qgis-js/dist/assets/wasm` has to be set ([see above](#explicitly-specifying-the-runtime-location))).

And also ensure that the HTML document has the correct COOP/COEP headers set (see [`compatibility.md`](./compatibility.md) for more information).


================================================
FILE: docs/ci.md
================================================
# CI/CD


================================================
FILE: docs/compatibility.md
================================================
# Compatibility

## Features

qgis-js uses the following features which have to be supported by the JavaScript/WebAssembly runtime:

- ES modules with [dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports)
- [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
- [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly)
  - [WebAssembly Threads](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
  - [WebAssembly Exception Handling](https://developer.mozilla.org/en-US/docs/WebAssembly/Exception_handling)

## COOP/COEP

In order to use SharedArrayBuffer a secure cross-origin context is required. This means that the [Cross-Origin-Opener-Policy (COOP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin-Opener-Policy) and [Cross-Origin-Embedder-Policy (COEP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin-Embedder-Policy) headers have to be set by the server.

Alternativley one can use [coi-serviceworker](https://github.com/gzuidhof/coi-serviceworker) to set the headers through a service worker. This makes it possible to use qgis-js for example on GitHub pages.

## Supported Browsers

The [features listed above are supported by the following browsers](https://caniuse.com/es6-module-dynamic-import,wasm,sharedarraybuffer,mdn-javascript_builtins_webassembly_exception,webworkers):

- **Chromium based browsers (>= 95)**

- **Firefox (>= 100)**

- Safari (15.2, yet to be tested!)

### Mobile Browsers

> 🚧 At the moment we don't support mobile browsers

## Supported JavaScript Runtimes

> 🚧 At the moment only browsers are supported


================================================
FILE: docs/debugging.md
================================================
# Debugging

## Setup

- Make sure that you have compiled qgis-js with the [`Debug` build type](../README.md#build-types)

- DWARF debug info is only supported in Chromium based browsers (Chrome, Edge, Brave, ...)
  - Make sure that you have enabled the "Experimental WebAssembly features" flag in your browser (see [Debugging WebAssembly with modern tools](https://developer.chrome.com/blog/wasm-debugging-2020/))

  - Install the [C/C++ DevTools Support (DWARF)](https://chrome.google.com/webstore/detail/pdcpmagijalfljmkmjngeonclgbbannb) extension

## Links

- [Debugging WebAssembly with modern tools](https://developer.chrome.com/blog/wasm-debugging-2020/), Chrome for Developers Blog, 2020
  - [Debugging WebAssembly Faster](https://developer.chrome.com/blog/faster-wasm-debugging/), Chrome for Developers Blog, 2022

- [WASM Debugging with Emscripten and VSCode](https://floooh.github.io/2023/11/11/emscripten-ide.html),
  The Brain Dump, 2023


================================================
FILE: docs/examples/qgis-js-example-api/index.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>qgis-js-example-api</title>
    <link rel="icon" href="data:," />
    <style>
      body {
        font-family: sans-serif;
        margin: 1em;
        padding: 1em;
      }
    </style>
  </head>
  <body>
    <h2>📐 Using the qgis-js API example</h2>
    <p>Open the Console to see the result 🤓</p>
    <script type="module" src="main.js"></script>
  </body>
</html>


================================================
FILE: docs/examples/qgis-js-example-api/main.js
================================================
const QGIS_JS_DIST = window.location.pathname + "node_modules/qgis-js/dist";

// loading the qgis-js library
const { qgis, QGIS_JS_VERSION } = await import(QGIS_JS_DIST + "/qgis.js");
console.log(`qgis-js (v${QGIS_JS_VERSION})`);

// booting the qgis-js runtime
console.log(`- loading qgis-js`);
const { api } = await qgis({
  prefix: QGIS_JS_DIST + "/assets/wasm",
});
console.log(`- qgis-js ready`);

// qgis-js API example
console.log(`- creating a rectangle`);
const rect = new api.QgsRectangle(1, 2, 3, 4);
console.log("-> " + printRect(rect));

console.log(`- scaling the rectangle`);
rect.scale(5);
console.log("-> " + printRect(rect));

console.log(`- getting the center of the rectangle`);
const center = rect.center();
console.log(`-> Point: x: ${center.x}, y: ${center.y}`);

function printRect(rect) {
  return `QgsRectangle: xMaximum: ${rect.xMaximum}, xMinimum: ${rect.xMinimum}, yMaximum: ${rect.yMaximum}, yMinimum: ${rect.yMinimum}`;
}


================================================
FILE: docs/examples/qgis-js-example-api/package.json
================================================
{
  "name": "qgis-js-example-api",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "vite preview --outDir ."
  },
  "dependencies": {
    "qgis-js": "4.0.0"
  },
  "devDependencies": {
    "vite": "^8.0.0"
  }
}


================================================
FILE: docs/examples/qgis-js-example-api/vite.config.js
================================================
import { defineConfig } from "vite";

export default defineConfig({
  preview: {
    headers: {
      "Cross-Origin-Opener-Policy": "same-origin",
      "Cross-Origin-Embedder-Policy": "require-corp",
    },
  },
});


================================================
FILE: docs/examples/qgis-js-example-ol/index.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>qgis-js-example-ol</title>
    <link rel="icon" href="data:," />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <h2>🗺️ Minimal OpenLayers example</h2>
    <div id="map"></div>
    <script type="module" src="main.js"></script>
  </body>
</html>


================================================
FILE: docs/examples/qgis-js-example-ol/main.js
================================================
import { qgis } from "qgis-js";

import { QgisCanvasDataSource } from "@qgis-js/ol";

import { Map, View } from "ol";

import ImageLayer from "ol/layer/Image";
import Projection from "ol/proj/Projection.js";

// start the qgis-js runtime
const { api, fs } = await qgis({
  prefix: "/assets/wasm",
});

// prepare the upload directory
const uploadDir = "/upload";
fs.mkdir(uploadDir);

// fetch demo project and upload it to the runtime
const source =
  "https://raw.githubusercontent.com/boardend/qgis-js-projects/main/demo/World%20GPKG";
const files = ["project.qgz", "world_map.gpkg"];
for (const file of files) {
  const url = `${source}/${file}`;
  const response = await fetch(url);
  const blob = await response.blob();
  const buffer = await blob.arrayBuffer();
  fs.writeFile(`${uploadDir}/${file}`, new Uint8Array(buffer));
}

// load the uploaded project
api.loadProject(`${uploadDir}/${files[0]}`);

// create the ol map with the projection from the project
const projection = new Projection({
  code: api.srid(),
  units: "m",
});

function createQgisLayer() {
  return new ImageLayer({
    source: new QgisCanvasDataSource(api, {
      projection,
    }),
  });
}

const map = new Map({
  target: "map",
  layers: [createQgisLayer()],
  view: new View({
    center: [0, 0],
    zoom: 2,
    projection,
  }),
});

// create a dropdown with all map themes
const mapThemes = api.mapThemes();
if (mapThemes.length > 0) {
  const themeContainer = document.createElement("div");
  themeContainer.style.marginTop = "1em";
  document.body.appendChild(themeContainer);

  themeContainer.appendChild(document.createTextNode("Map theme: "));

  const select = document.createElement("select");
  select.addEventListener("change", () => {
    if (select.value) {
      api.setMapTheme(select.value);
      map.getLayers().clear();
      map.addLayer(createQgisLayer());
    }
  });
  themeContainer.appendChild(select);

  const currentTheme = api.getMapTheme();

  const option = document.createElement("option");
  option.value = "";
  option.text = "";
  if (!currentTheme) {
    option.selected = true;
  }
  select.appendChild(option);

  for (const theme of api.mapThemes()) {
    const option = document.createElement("option");
    option.value = theme;
    option.text = theme;
    if (theme === currentTheme) {
      option.selected = true;
    }
    select.appendChild(option);
  }
}


================================================
FILE: docs/examples/qgis-js-example-ol/package.json
================================================
{
  "name": "qgis-js-example-ol",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "vite dev",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "ol": "^10.8.0",
    "@qgis-js/ol": "4.0.0",
    "qgis-js": "4.0.0"
  },
  "devDependencies": {
    "vite": "^8.0.0",
    "vite-plugin-static-copy": "^3.2.0"
  }
}


================================================
FILE: docs/examples/qgis-js-example-ol/style.css
================================================
@import "node_modules/ol/ol.css";

body {
  font-family: sans-serif;
  margin: 1em;
  padding: 1em;
}
#map {
  width: 800px;
  height: 600px;
  border: 1px solid grey;
}


================================================
FILE: docs/examples/qgis-js-example-ol/vite.config.js
================================================
import { defineConfig } from "vite";

import { viteStaticCopy } from "vite-plugin-static-copy";

const headers = {
  "Cross-Origin-Opener-Policy": "same-origin",
  "Cross-Origin-Embedder-Policy": "require-corp",
};

export default defineConfig({
  build: {
    target: "esnext",
  },
  plugins: [
    {
      name: "headers-after-static-copy",
      configureServer(server) {
        server.middlewares.use((_req, res, next) => {
          Object.entries(headers).forEach(([key, value]) => {
            res.setHeader(key, value);
          });
          next();
        });
      },
    },
    viteStaticCopy({
      silent: true,
      targets: [
        {
          src: "node_modules/qgis-js/dist/assets/wasm/**",
          dest: "assets/wasm",
        },
      ],
    }),
  ],
  preview: {
    headers,
  },
});


================================================
FILE: docs/performance.md
================================================
# Performance and Optimization

## Preface

In Feb-June 2024, we will conduct a performance test to measure the performance of qgis-js and find ways to optimize it. This document outlines the general approach, lists the potential optimizations and tracks the status of those optimizations

More information

- [Performance Comparison (between version 0.0.3 and 0.0.5)](https://github.com/boardend/qgis-js-performance)
- Documentation
  - [`./debugging.md`](./debugging.md)
  - [`./profiling.md`](./profiling.md)

## Deliverables

- [x] [A performance measurement tool for qgis-js](../sites/performance/)
  - [x] [A report on the performance gain](https://github.com/boardend/qgis-js-performance)
- [x] [A final conclusion](#conclusion)

## Conclusion

**Approach**:

- In order to optimize the performance of qgis-js, we have gathered a list of potential [optimizations](#optimizations) and implemented as many as possible in the given time frame
  - The status of each optimization is tracked in the [list](#optimizations)
  - Note that most of them have been implemented
- To challenge the performance of qgis-js, the demo project "AoS - Precipitation per balance basin" with the worst observed performance so far has been selected to verify the optimizations
  - It is used on its worst performing extent (whole of Switzerland) and used to render a full screen map (`1920x1080`)
  - The project was measured in two versions:
    - [`aos-baseline`](https://github.com/boardend/qgis-js-projects/tree/main/performance/aos-baseline): The original version of the project, as it is used on the qgis-js website
    - [`aos-playground`](https://github.com/boardend/qgis-js-projects/tree/main/performance/aos-playground): A optimized version of the project
      - Added/Recalculate indices for all layers
      - Simplified geometries for all vector layers
      - Played with the project settings to achieve potential performance improvements
- A performance measurement tool has been implemented used to measure the performance of the application before (version `0.0.3`) and after the optimizations (version `0.0.5`) and to compare the performance of the `aos-baseline` and `aos-playground` project:
  - The full results can be found in a seperate repository: [Performance Comparison (between version 0.0.3 and 0.0.5)](https://github.com/boardend/qgis-js-performance)

> All changes in the scope of this project have been merged with [PR #38](https://github.com/qgis/qgis-js/pull/38)

**Results**:

- Rendering time could be reduced significantly between version `0.0.3` and `0.0.5`:
  - Chrome: `41.60%`
  - Firefox: `43.04%`
- Chrome is performing better than Firefox
  - Ratio rendering time Chrome/Firefox: `0.47`
- Difference between `aos-baseline`/`aos-playground` project is negligible
  - `100.38%`, `100.51%`, `93.66%`, `101.62%`

> see [Performance Comparison (between version 0.0.3 and 0.0.5)](https://github.com/boardend/qgis-js-performance)

> Compare the two versions in the browser:
>
> - \>= `0.0.5`: https://qgis.github.io/qgis-js/
> - `0.0.3`: https://boardend.github.io/qgis-js-baseline/

**Further steps**:

- Implement the not yet implemented [optimizations](#optimizations)
- Add the performance measurement tool to the GitHub page
- Automate the performance measurement tool to run on every commit/release
- Further [profile](./profiling.md) the performance of the application in order to find bottlenecks
- Compare the performance of the WebAssembly build against the native build of QGIS

## Optimizations

> 💡 **Legend**: Each potential optimization will be prioritized with one of the following labels:
>
> **Priority**:
>
> - 🟢 High priority: _Should definitively taken into account_
> - 🟡 Mid priority: _Would be nice to investigate_
> - 🔴 Low priority: _Probably out of scope for now_
>
> **Status**:
>
> - [x] Implemented
> - [ ] Open

### Toolchain/Dependencies

- [x] 🟢 Update to latest emscripten version
  - currently `3.1.29` is used (more than a year old)
  - by anecdotal evidence, binaries compiled with the latest version are faster (latest LLVM)
- [x] 🟢 Update to Qt version `6.6.1`
  - see [wasm improvements since `6.5.2`](https://github.com/qt/qtreleasenotes/tree/dev/qt)
  - building of Qt is needed anyway, in order to pass custom build flags (see below)
- [x] 🟡 Update to vcpkg version `2024.02.14` (and update the packages with it)
- [ ] 🔴 Update to latest QGIS version
  - Currently `3.32.1` is used
  - Latest would be `3.34.3`
  - 💬 Created an issue to track this: https://github.com/qgis/qgis-js/issues/39

### Build/Setup

- [x] 🟢 Internalize Qt build
  - This makes is possible to also apply optimizations also to the Qt build
  - Check if the Qt build configuration can be optimized
- [x] 🟢 Review and tweak the build flags/settings provided to emscripten
  - `-flto` (Link Time Optimization)
  - see https://emscripten.org/docs/optimizing/Optimizing-Code.html
  - see https://emsettings.surma.technology/
- [x] 🟢 Add [SIMD support](https://webassembly.org/features/)
  - Make use of auto vectorization
  - Add SIMD support for libraries (e.g. GDAL)
  - see https://jeromewu.github.io/improving-performance-using-webassembly-simd-intrinsics/
- [x] 🟢 Ensure [Bulk memory operations](https://webassembly.org/features/)
  - Not sure if we already make use of this
  - See `-mbulk-memory`
  - 💬 Note that this is the default of newer Emscripten versions
- [ ] 🟢 Check if new [WASMFS](https://emscripten.org/docs/api_reference/Filesystem-API.html#new-file-system-wasmfs) helps to improve FS performance
  - 💬 This was not working well with the Qt CMake setup
    - Needs more time to investigate
- [x] 🟡 Check other potential [WebAssembly features](https://webassembly.org/features/)
  - See if we can make use of any other of features generally available
  - 💬 Didn't find anything that could benefit the current setup

### Architecture/Runtime

- **QGIS**
  - [x] 🟢 Switch to `QgsMapRendererParallelJob`
  - [x] 🟢 Check if auto geometry simplification is configured and used
  - [x] 🟢 Check if `QgsMapRendererCache` can help to speed up redraws
    - 💬 This doesn't help with the general rending performance. But could improove e.g. layer toggling. Not in use at the moment
  - [x] 🟡 Check all `MapSettingsFlag`, `ProjectReadFlag`, etc. for potential tweaks
  - [x] 🟡 Have a look at the QGIS Server implementation for potential tweaks
  - **API**
    - [x] 🟢 Check if JS promises can be returned directly from the API
      - 💬 This is possible with Emscriptens [promise.h](https://github.com/emscripten-core/emscripten/blob/main/system/include/emscripten/promise.h). But won't help much with the general render performance
    - [x] 🟡 Check if it is possible to make a zero-copy implementation of the render callback (e.g. [transferable](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects))
      - 💬 This is not possible with the current architecture (But the copy takes less then 5ms)
- **Qt**
  - [ ] 🟢 Check performance impact of OutputImageFormat `Format_ARGB32`
    - Is it faster to render in `Format_ARGB32_Premultiplied` and convert to `Format_ARGB32` afterwards?
- **sqlite**
  - [x] 🟢 Check if WAL/locking etc. can be disabled for rendering (read-only)
    - 💬 See the flags in `qgis-js.cpp`

### UI/UX

- [x] **OpenLayers**
  - [x] 🟢 Check with the OL devs, if the current layer implementations are well designed
    - 💬 Checked with [Andreas Hocevar](https://github.com/ahocevar) and created this follow up issues:
      - https://github.com/qgis/qgis-js/issues/40 , https://github.com/qgis/qgis-js/issues/41 , https://github.com/qgis/qgis-js/issues/42 , https://github.com/qgis/qgis-js/issues/16
  - [x] 🟢 Cancellation of pending render jobs
    - 💬 Implemented with the `QgisJobDataSource`
  - [x] 🟢 Find a way to cache the results in "canvas" mode (like in XYZ mode)
    - 💬 Not possible at the moment (only with tiling like here: https://openlayers.org/en/latest/examples/wms-custom-proj.html)
  - [x] 🟡 Is it possible to display immediate results (progressive rendering), before the job finishes
    - 💬 Implemented with the `QgisJobDataSource`
- [ ] 🔴 **QML**
  - Compare the performance of `QgsQuickMapCanvasMap` with the performance and UX of the current OL solution

### QGIS Project

- [x] 🟢 Optimize the project settings for fastest possible rendering
  - 💬 This doesn't help much. See [`aos-baseline`](https://github.com/boardend/qgis-js-projects/tree/main/performance/aos-baseline) vs [`aos-playground`](https://github.com/boardend/qgis-js-projects/tree/main/performance/aos-playground) when running in local dev mode.
- [x] 🟢 Ensure indices are created and used for all vector layers
  - 💬 This was already in place for the checked vector and raster layers ([`aos-baseline`](https://github.com/boardend/qgis-js-projects/tree/main/performance/aos-baseline))
- [ ] 🟡 Check if there are faster data formats than GeoPackage (e.g. FlatGeobuf)


================================================
FILE: docs/profiling.md
================================================
# Profiling

## qgis-js Performance Measurement Tool

The [qgis-js Performance Measurement Tool](../sites/performance/) can be used to measure the performance of the qgis-js application in a reproducible way:

```
cd sites/performance
npm run dev
```

1. Boot the runtime
2. Load a project
3. Render a first dummy frame
4. Start the performance test

## Browsers

### Chrome

The Performance tab of the Chrome DevTools can be used to profile the performance of the qgis-js application. See the [official documentation](https://developer.chrome.com/docs/devtools/evaluate-performance/) for more information.

![Firefox Profiler](https://developer.chrome.com/static/docs/devtools/performance/image/the-results-the-profile-5d830d01508e2_2880.png)

- 💡 In order to get useful results (e.g. function names in the `.wasm` module), the [build type](../README.md#build-types) has to be set to `Dev` or `Debug`
  - ⚠️ Note that the `Debug` build type is significantly slower than the `Dev` build type

### Firefox

The [Firefox Profiler⁩](https://profiler.firefox.com/) can be used to profile the performance of the qgis-js application.

![Firefox Profiler](https://profiler.firefox.com/b45b29da558efa211628.jpg)

- 💡 In order to get useful results (e.g. function names in the `.wasm` module), the [build type](../README.md#build-types) has to be set to `Dev` or `Debug`
  - ⚠️ Note that the `Debug` build type is significantly slower than the `Dev` build type

## Emscripten

- Emscripten provies a [profiling guide](https://emscripten.org/docs/optimizing/Optimizing-Code.html#profiling) and some embeddable tools to profile the application:
  - `--cpuprofiler`
  - `--memoryprofiler`
  - `--threadprofiler`

## QGIS profiling

- It would be nice to extend the qgis-js API in order to retrieve profiling information from the QGIS core
  - ⚠️ This is not yet implemented


================================================
FILE: package.json
================================================
{
  "name": "qgis-js-repo",
  "private": true,
  "version": "4.0.0",
  "license": "GPL-2.0-or-later",
  "homepage": "https://qgis.github.io/qgis-js/",
  "repository": "github:qgis/qgis-js",
  "bugs": {
    "url": "https://github.com/qgis/qgis-js/issues"
  },
  "packageManager": "pnpm@10.32.1",
  "engines": {
    "node": "22.16.0",
    "pnpm": "10.32.1",
    "emsdk": "5.0.2",
    "vcpkg": "2025.12.12",
    "qt": "6.10.1",
    "qgis": "4.0.0"
  },
  "type": "module",
  "scripts": {
    "postinstall": "./qgis-js.ts -v install",
    "update": "pnpm update -r --ignore-scripts",
    "clean": "./qgis-js.ts -v clean",
    "compile": "pnpm run compile:dev",
    "compile:dev": "./qgis-js.ts compile",
    "compile:debug": "./qgis-js.ts compile -t Debug",
    "compile:release": "./qgis-js.ts compile -t Release",
    "build": "pnpm -r --filter=./packages/** run build",
    "dev": "pnpm --filter @qgis-js/dev dev",
    "dev:build": "pnpm --filter @qgis-js/dev build",
    "dev:preview": "npm run dev:build && pnpm --filter @qgis-js/dev preview",
    "site": "vite dev",
    "deploy": "pnpm -r --filter=./sites/** run deploy",
    "publish": "pnpm publish -r --filter=./packages/qgis-js** --access=public",
    "lint": "npm run lint:prettier && npm run lint:clang-format",
    "lint:prettier": "npx prettier . --write",
    "lint:pretty-quick": "pretty-quick --staged",
    "lint:clang-format": "clang-format -i \"--glob=src/**/*.{cpp,hpp}\""
  },
  "devDependencies": {
    "pnpm": "10.32.1",
    "vite": "8.0.0",
    "vite-node": "6.0.0",
    "zx": "8.8.5",
    "@rushstack/ts-command-line": "5.3.3",
    "ts-markdown": "1.3.0",
    "@microsoft/api-extractor": "7.57.7",
    "@microsoft/api-documenter": "7.29.7",
    "prettier": "3.8.1",
    "pretty-quick": "4.2.2",
    "clang-format": "1.8.0"
  }
}


================================================
FILE: packages/qgis-js/README.md
================================================
# qgis-js

**QGIS core ported to WebAssembly to run it on the web platform**

Version: `4.0.0` (based on QGIS 4.0.0)

[qgis-js Repository](https://github.com/qgis/qgis-js) | [qgis-js Website](https://qgis.github.io/qgis-js) | ["`qgis-js`" package source](https://github.com/qgis/qgis-js/tree/main/packages/qgis-js)

[![qgis-js on npm](https://img.shields.io/npm/v/qgis-js)](https://www.npmjs.com/package/qgis-js)

> ⚠️🧪 **Work in progress**! Currently this project is in public beta

## Description

QGIS core compiled to WebAssembly to run it on the web platform. This package provides the WebAssembly module and JavaScript/TypeScript API to load the runtime and interact with QGIS.

See the [qgis-js repository](https://github.com/qgis/qgis-js) for more information about the project.

## Installation

```bash
npm install -S qgis-js
```

## Usage

```js
import { qgis } from "qgis-js";

const { api } = await qgis();

const rect = new api.QgsRectangle(1, 2, 3, 4);
rect.scale(5);
const center = rect.center();
console.log(center.x, center.y);
```

> 💡 Have a look at the [Integration packages](#integration-packages) to load QGIS projects and display them on a map

> ⚠️ It must be ensured that...
>
> - the clients meets the [compatibility requirements](https://github.com/qgis/qgis-js/blob/main/docs/compatibility.md)
> - that the webserver is configured to [allow cross-origin requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
> - that if you are using a bundler, it is [configured to load the qgis-js assets](https://github.com/qgis/qgis-js/blob/main/docs/bundling.md)

## Integration packages

| Package                                                  | Description                                                           | npm                                                                                                                   |
| -------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| **[@qgis-js/ol](./packages/qgis-js-ol/README.md)**       | [OpenLayers](https://openlayers.org/) sources to display qgis-js maps | [![@qgis-js/ol on npm](https://img.shields.io/npm/v/@qgis-js/ol)](https://www.npmjs.com/package/@qgis-js/ol)          |
| **[@qgis-js/utils](./packages/qgis-js-utils/README.md)** | Utilities to integrate qgis-js into web applications                  | [![@qgis-js/utils on npm](https://img.shields.io/npm/v/@qgis-js/utils)](https://www.npmjs.com/package/@qgis-js/utils) |

## WebAssembly module

### Size

<!--NOTE: this can be generated with "./qgis-js.ts size -o markdown"-->

The size of the package is **`77.06 MB`** (uncompressed) or `18.74 MB` Brotli compressed (76% space saving) / `22.08 MB` Gzip compressed (71% space saving)

It consists of the following files:

| File name                  | Size (uncompressed) | Size (Brotli compressed)      | Size (Gzip compressed)        |
| -------------------------- | ------------------- | ----------------------------- | ----------------------------- |
| `qgis.js`                  | `5.19 kB`           | `1.87 kB` (64% space saving)  | `1.97 kB` (62% space saving)  |
| `assets/wasm/qgis-js.js`   | `276.92 kB`         | `61.28 kB` (78% space saving) | `65.16 kB` (76% space saving) |
| `assets/wasm/qgis-js.data` | `13.4 MB`           | `2.01 MB` (85% space saving)  | `2.62 MB` (80% space saving)  |
| `assets/wasm/qgis-js.wasm` | `63.39 MB`          | `16.67 MB` (74% space saving) | `19.4 MB` (69% space saving)  |

### Libraries

<!--NOTE: this can be generated with "./qgis-js.ts libs -o markdown"-->

| Library                                                                                                                                                                                                                                                                                                                                                 | License                      | Links                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **abseil** (20260107.1)<br /><div style="max-width:30em">_Abseil is an open-source collection of C++ library code designed to augment the C++ standard library. The Abseil library code is collected from Google's own C++ code base, has been extensively tested and used in production, and is the same code we depend on in our daily coding lives._ | Apache-2.0                   | [Website](https://github.com/abseil/abseil-cpp) - [Source code](https://github.com/abseil/abseil-cpp)                       |
| **double-conversion** (3.4.0)<br /><div style="max-width:30em">_Efficient binary-decimal and decimal-binary conversion routines for IEEE doubles._                                                                                                                                                                                                      |                              | [Website](https://github.com/google/double-conversion) - [Source code](https://github.com/google/double-conversion)         |
| **egl-registry** (2025-05-27)<br /><div style="max-width:30em">_EGL API and Extension Registry_                                                                                                                                                                                                                                                         |                              | [Website](https://github.com/KhronosGroup/EGL-Registry) - [Source code](https://github.com/KhronosGroup/EGL-Registry)       |
| **exiv2** (0.28.8)<br /><div style="max-width:30em">_Image metadata library and tools_                                                                                                                                                                                                                                                                  | GPL-2.0-or-later             | [Website](https://exiv2.org) - [Source code](https://github.com/Exiv2/exiv2)                                                |
| **expat** (2.7.4)<br /><div style="max-width:30em">_XML parser library written in C_                                                                                                                                                                                                                                                                    | MIT                          | [Website](https://github.com/libexpat/libexpat) - [Source code](https://github.com/libexpat/libexpat)                       |
| **freetype** (2.13.3)<br /><div style="max-width:30em">_A library to render fonts._                                                                                                                                                                                                                                                                     | FTL OR GPL-2.0-or-later      | [Website](https://www.freetype.org/) - [Source code](https://gitlab.freedesktop.org/freetype/freetype)                      |
| **gdal** (3.12.2)<br /><div style="max-width:30em">_The Geographic Data Abstraction Library for reading and writing geospatial raster and vector data_                                                                                                                                                                                                  |                              | [Website](https://gdal.org) - [Source code](https://github.com/OSGeo/gdal)                                                  |
| **geos** (3.14.1)<br /><div style="max-width:30em">_Geometry Engine Open Source_                                                                                                                                                                                                                                                                        | LGPL-2.1-only                | [Website](https://libgeos.org/)                                                                                             |
| **inih** (62)<br /><div style="max-width:30em">_Simple .INI file parser_                                                                                                                                                                                                                                                                                | BSD-3-Clause                 | [Website](https://github.com/benhoyt/inih) - [Source code](https://github.com/benhoyt/inih)                                 |
| **json-c** (0.18-20240915)<br /><div style="max-width:30em">_A JSON implementation in C_                                                                                                                                                                                                                                                                | MIT                          | [Website](https://github.com/json-c/json-c) - [Source code](https://github.com/json-c/json-c)                               |
| **libb2** (0.98.1)<br /><div style="max-width:30em">_C library providing BLAKE2b, BLAKE2s, BLAKE2bp, BLAKE2sp_                                                                                                                                                                                                                                          |                              | [Website](https://github.com/BLAKE2/libb2) - [Source code](https://github.com/BLAKE2/libb2)                                 |
| **libgeotiff** (1.7.4)<br /><div style="max-width:30em">_Libgeotiff is an open source library on top of libtiff for reading and writing GeoTIFF information tags._                                                                                                                                                                                      | MIT                          | [Website](https://github.com/OSGeo/libgeotiff)                                                                              |
| **libiconv** (1.18)<br /><div style="max-width:30em">_iconv() text conversion._                                                                                                                                                                                                                                                                         |                              | [Website](https://www.gnu.org/software/libiconv/)                                                                           |
| **libjpeg-turbo** (3.1.3)<br /><div style="max-width:30em">_libjpeg-turbo is a JPEG image codec that uses SIMD instructions (MMX, SSE2, NEON, AltiVec) to accelerate baseline JPEG compression and decompression on x86, x86-64, ARM, and PowerPC systems._                                                                                             | BSD-3-Clause                 | [Website](https://github.com/libjpeg-turbo/libjpeg-turbo)                                                                   |
| **liblzma** (5.8.2)<br /><div style="max-width:30em">_Compression library with an API similar to that of zlib._                                                                                                                                                                                                                                         |                              | [Website](https://tukaani.org/xz/)                                                                                          |
| **libpng** (1.6.55)<br /><div style="max-width:30em">_libpng is a library implementing an interface for reading and writing PNG (Portable Network Graphics) format files_                                                                                                                                                                               | libpng-2.0                   | [Website](https://github.com/pnggroup/libpng)                                                                               |
| **libspatialindex** (2.0.0)<br /><div style="max-width:30em">_C++ implementation of R\*-tree, an MVR-tree and a TPR-tree with C API._                                                                                                                                                                                                                   | MIT                          | [Website](http://libspatialindex.github.com)                                                                                |
| **libzip** (1.11.4)<br /><div style="max-width:30em">_A C library for reading, creating, and modifying zip archives._                                                                                                                                                                                                                                   | BSD-3-Clause                 | [Website](https://github.com/nih-at/libzip)                                                                                 |
| **md4c** (0.5.2)<br /><div style="max-width:30em">_MD4C is a C library providing a Markdown parser._                                                                                                                                                                                                                                                    | MIT                          | [Website](https://github.com/mity/md4c) - [Source code](https://github.com/mity/md4c)                                       |
| **nlohmann-json** (3.12.0)<br /><div style="max-width:30em">_JSON for Modern C++_                                                                                                                                                                                                                                                                       | MIT                          | [Website](https://github.com/nlohmann/json) - [Source code](https://github.com/nlohmann/json)                               |
| **opengl-registry** (2026-01-26)<br /><div style="max-width:30em">_OpenGL, OpenGL ES, and OpenGL ES-SC API and Extension Registry_                                                                                                                                                                                                                      |                              | [Website](https://github.com/KhronosGroup/OpenGL-Registry) - [Source code](https://github.com/KhronosGroup/OpenGL-Registry) |
| **opengl** (2022-12-04)<br /><div style="max-width:30em">_Open Graphics Library (OpenGL)[3][4][5] is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics._                                                                                                                                 |                              |                                                                                                                             |
| **pcre2** (10.47)<br /><div style="max-width:30em">_Regular Expression pattern matching using the same syntax and semantics as Perl 5._                                                                                                                                                                                                                 | BSD-3-Clause                 | [Website](https://github.com/PCRE2Project/pcre2)                                                                            |
| **proj** (9.7.1)<br /><div style="max-width:30em">_PROJ library for cartographic projections_                                                                                                                                                                                                                                                           | MIT                          | [Website](https://proj.org/) - [Source code](https://github.com/OSGeo/PROJ)                                                 |
| **protobuf** (6.33.4)<br /><div style="max-width:30em">_Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data._                                                                                                                                                                                             | BSD-3-Clause                 | [Website](https://github.com/protocolbuffers/protobuf) - [Source code](https://github.com/protocolbuffers/protobuf)         |
| **qgis** (4.0.0)<br /><div style="max-width:30em">_QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)_                                                                                                                                                                                                     | GPL-2.0                      | [Website](https://www.qgis.org/) - [Source code](https://github.com/qgis/QGIS)                                              |
| **qt5compat** (6.10.1)<br /><div style="max-width:30em">_The Qt 5 Core Compat module contains the Qt 5 Core APIs that were removed in Qt 6. The module facilitates the transition to Qt 6._                                                                                                                                                             |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtbase** (6.10.1)<br /><div style="max-width:30em">_Qt Base (Core, Gui, Widgets, Network, ...)_                                                                                                                                                                                                                                                       |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtkeychain** (0.14.3)<br /><div style="max-width:30em">_(Unaffiliated with Qt) Platform-independent Qt6 API for storing passwords securely_                                                                                                                                                                                                           | BSD-3-Clause                 | [Website](https://github.com/frankosterfeld/qtkeychain) - [Source code](https://github.com/frankosterfeld/qtkeychain)       |
| **qtmultimedia** (6.10.1)<br /><div style="max-width:30em">_Qt Multimedia is an add-on module that provides a rich set of QML types and C++ classes to handle multimedia content._                                                                                                                                                                      |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtshadertools** (6.10.1)<br /><div style="max-width:30em">_The Qt Shader Tools module is designed to provide a set of tools and utilities to work with graphics shaders._                                                                                                                                                                             |                              | [Website](https://www.qt.io/)                                                                                               |
| **qtsvg** (6.10.1)<br /><div style="max-width:30em">_Qt SVG provides classes for rendering and displaying SVG drawings in widgets and on other paint devices._                                                                                                                                                                                          |                              | [Website](https://www.qt.io/)                                                                                               |
| **qttools** (6.10.1)<br /><div style="max-width:30em">_A collection of tools and utilities that come with the Qt framework to assist developers in the creation, management, and deployment of Qt applications._                                                                                                                                        |                              | [Website](https://www.qt.io/)                                                                                               |
| **sqlite3** (3.51.2)<br /><div style="max-width:30em">_SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine._                                                                                                                                                               | blessing                     | [Website](https://sqlite.org/)                                                                                              |
| **tiff** (4.7.1)<br /><div style="max-width:30em">_A library that supports the manipulation of TIFF image files_                                                                                                                                                                                                                                        | libtiff                      | [Website](https://libtiff.gitlab.io/libtiff/) - [Source code](https://gitlab.com/libtiff/libtiff)                           |
| **utf8-range** (6.33.4)<br /><div style="max-width:30em">_Fast UTF-8 validation with Range algorithm (NEON+SSE4+AVX2)_                                                                                                                                                                                                                                  | MIT                          | [Website](https://github.com/protocolbuffers/protobuf) - [Source code](https://github.com/protocolbuffers/protobuf)         |
| **zlib** (1.3.1)<br /><div style="max-width:30em">_A compression library_                                                                                                                                                                                                                                                                               | Zlib                         | [Website](https://www.zlib.net/) - [Source code](https://github.com/madler/zlib)                                            |
| **zstd** (1.5.7)<br /><div style="max-width:30em">_Zstandard - Fast real-time compression algorithm_                                                                                                                                                                                                                                                    | BSD-3-Clause OR GPL-2.0-only | [Website](https://facebook.github.io/zstd/) - [Source code](https://github.com/facebook/zstd)                               |

## Versioning

This package uses [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/qgis/qgis-js/tags).

## License

[GNU General Public License v2.0](https://github.com/qgis/qgis-js/blob/main/LICENSE)


================================================
FILE: packages/qgis-js/package.json
================================================
{
  "name": "qgis-js",
  "version": "4.0.0",
  "description": "QGIS core ported to WebAssembly to run it on the web platform",
  "license": "GPL-2.0-or-later",
  "homepage": "https://qgis.github.io/qgis-js/",
  "repository": {
    "type": "git",
    "url": "https://github.com/qgis/qgis-js",
    "directory": "packages/qgis-js"
  },
  "bugs": {
    "url": "https://github.com/qgis/qgis-js/issues"
  },
  "type": "module",
  "main": "dist/qgis.js",
  "types": "dist/qgis.d.ts",
  "files": [
    "dist/**/*"
  ],
  "scripts": {
    "build": "vite build"
  },
  "dependencies": {
    "@types/emscripten": "1.41.5",
    "p-limit": "7.3.0"
  },
  "devDependencies": {
    "@types/node": "^22.19.15",
    "typescript": "5.8.2",
    "vite": "8.0.0",
    "vite-plugin-dts": "4.5.4"
  },
  "keywords": [
    "qgis",
    "qgisjs",
    "qgis-js",
    "qgiswasm",
    "qgis-wasm",
    "webassembly",
    "wasm",
    "geo",
    "geospatial"
  ]
}


================================================
FILE: packages/qgis-js/src/QgisApiAdapter.ts
================================================
import {
  InternalQgisApi,
  QgisApi,
  QgisApiAdapter,
} from "../../../src/api/QgisApi";
import { QgsRectangle } from "../../../src/api/QgisModel";

import { threadPoolSize } from "./runtime";

import pLimit from "p-limit";
import type { LimitFunction } from "p-limit";

export class QgisApiAdapterImplementation implements QgisApiAdapter {
  private readonly _api: InternalQgisApi;
  private readonly _threadPoolSize: number;
  private readonly _limit: LimitFunction;

  constructor(api: InternalQgisApi) {
    this._api = api;
    this._threadPoolSize = threadPoolSize();
    this._limit = pLimit(this._threadPoolSize);
  }

  protected runLimited<T>(fn: () => Promise<T>): Promise<T> {
    return this._limit(fn);
  }

  renderImage(
    srid: string,
    extent: QgsRectangle,
    width: number,
    height: number,
    pixelRatio: number = window?.devicePixelRatio || 1,
    layerIds?: string[],
  ): Promise<ImageData> {
    return this.runLimited(() => {
      return new Promise((resolve) => {
        this._api.renderImage(
          srid,
          extent,
          width,
          height,
          pixelRatio,
          (tileData) => {
            const data = new Uint8ClampedArray(
              tileData,
            ) as Uint8ClampedArray<ArrayBuffer>;
            const imageData = new ImageData(data, width, height);
            resolve(imageData);
          },
          layerIds,
        );
      });
    });
  }
  renderXYZTile(
    x: number,
    y: number,
    z: number,
    tileSize: number = 256,
    pixelRatio: number = window?.devicePixelRatio || 1,
    extentBuffer: number = 0,
    layerIds?: string[],
  ): Promise<ImageData> {
    return this.runLimited(() => {
      return new Promise((resolve) => {
        this._api.renderXYZTile(
          x,
          y,
          z,
          tileSize,
          pixelRatio,
          extentBuffer,
          (tileData) => {
            const data = new Uint8ClampedArray(
              tileData,
            ) as Uint8ClampedArray<ArrayBuffer>;
            const imageData = new ImageData(data, tileSize, tileSize);
            resolve(imageData);
          },
          layerIds,
        );
      });
    });
  }

  mapThemes(): readonly string[] {
    const mapLayersRaw = this._api.mapThemes();
    const result = new Array<string>(mapLayersRaw.size());
    for (let i = 0; i < mapLayersRaw.size(); i++) {
      result[i] = mapLayersRaw.get(i);
    }
    return result;
  }
}

export function getQgisApiProxy(api: InternalQgisApi): QgisApi {
  const adapter = new QgisApiAdapterImplementation(api);

  return new Proxy<QgisApi>(
    // @ts-ignore
    {},
    {
      has(_target, property) {
        return property in adapter || property in api;
      },
      get(_target, property) {
        if (property in adapter) {
          // @ts-ignore
          return adapter[property];
        } else if (property in api) {
          // @ts-ignore
          return api[property];
        }
      },
    },
  );
}


================================================
FILE: packages/qgis-js/src/emscripten.ts
================================================
/// <reference types="emscripten" />

/**
 * Extension of a EmscriptenModule that adds additional properties
 */
export interface EmscriptenRuntimeModule extends EmscriptenModule {
  [x: string]: any;
}

/**
 * Emscripten file system
 *
 * {@link https://emscripten.org/docs/api_reference/Filesystem-API.html}
 */
export type EmscriptenFS = typeof FS;


================================================
FILE: packages/qgis-js/src/index.ts
================================================
/**
 * The version of qgis-js.
 */
export const QGIS_JS_VERSION: string =
  //@ts-ignore (will be defined by vite)
  __QGIS_JS_VERSION;

export type {
  QgisApi,
  QgisApiAdapter,
  CommonQgisApi,
  InternalQgisApi,
  LayerDefinitionResult,
} from "../../../src/api/QgisApi";

export type { EmscriptenFS } from "./emscripten";

export type {
  QgsPointXY,
  QgsRectangle,
  QgsLayerTreeModelLegendNode,
  QgsLayerTreeNode,
  QgsLayerTreeGroup,
  QgsLayerTreeLayer,
  QgsMapLayer,
  QgsVectorLayer,
} from "../../../src/api/QgisModel";

export { LayerType, NodeType } from "../../../src/api/QgisModel";

export { qgis } from "./loader";


================================================
FILE: packages/qgis-js/src/loader.ts
================================================
import { getQgisApiProxy } from "./QgisApiAdapter";

import { threadPoolSize } from "./runtime";
import type {
  QgisRuntime,
  QgisRuntimeConfig,
  QgisRuntimeModule,
} from "./runtime";

import type { EmscriptenRuntimeModule } from "./emscripten";

/**
 * Emscripten module configuration
 */
interface QtAppConfig {}

/**
 * Interface for a Emscripten module that creates a Qt app instance.
 */
interface QtRuntimeFactory {
  createQtAppInstance(config: QtAppConfig): Promise<QgisRuntimeModule>;
}

/**
 * Loads the QtRuntimeFactory Emscripten module with the given prefix.
 *
 * @param mainScriptPath - The import path of the main script
 * @returns A promise that resolves with the QtRuntimeFactory module.
 */
function loadModule(mainScriptPath: string): Promise<QtRuntimeFactory> {
  return new Promise(async (resolve, reject) => {
    try {
      // hack to import es module without vite knowing about it
      const createQtAppInstance = (
        await new Function(`return import("${mainScriptPath}")`)()
      ).default;
      resolve({
        createQtAppInstance,
      });
    } catch (error) {
      reject(error);
    }
  });
}

/**
 * Load and initialize a new qgis-js runtime.
 *
 * @param config The {@link QgisRuntimeConfig} that will be taken into account during loading and initialization.
 * @returns A promise that resolves to a {@link QgisRuntime}.
 */
export async function qgis(
  config: QgisRuntimeConfig = {},
): Promise<QgisRuntime> {
  return new Promise(async (resolve, reject) => {
    let prefix: string | undefined = undefined;
    if (config.prefix) {
      prefix = config.prefix;
    } else {
      const url = import.meta.url;
      if (/.*\/src\/loader\.[ts|js]\?*[^/]*$/.test(url)) {
        console.warn(
          [
            `qgis-js loader is running in development mode and no "prefix" seems to be configured.`,
            ` - Consider adding the QgisRuntimePlugin when bundling with Vite.`,
            ` - For more information see: https://github.com/qgis/qgis-js/blob/main/docs/bundling.md`,
          ].join("\n"),
        );
        if (typeof window !== "undefined") {
          prefix = new URL("assets/wasm", window.location.href).pathname;
        }
      } else {
        prefix = new URL(/* @vite-ignore */ "assets/wasm", import.meta.url)
          .href;
      }
    }

    if (!prefix) {
      prefix = "assets/wasm";
    } else {
      prefix = prefix.replace(/\/$/, ""); // ensure no trailing slash
    }

    let qtRuntimeFactory: QtRuntimeFactory | undefined = undefined;
    try {
      const mainScriptPath = `${prefix}/qgis-js.js`;
      qtRuntimeFactory = await loadModule(mainScriptPath);
    } catch (error) {
      reject(
        new Error(`Unable to load the qgis-js.js script`, { cause: error }),
      );
      return;
    }

    const { createQtAppInstance } = qtRuntimeFactory!;

    let canvas: HTMLDivElement | undefined = undefined;
    if (typeof document !== "undefined") {
      canvas = document?.querySelector("#screen") as HTMLDivElement;
    }

    const runtimePromise = createQtAppInstance({
      locateFile: (path: string) => `${prefix}/` + path,
      preRun: [
        function (module: any) {
          module.qtContainerElements = canvas ? [canvas] : [];
          module.qtFontDpi = 96;
          module.qgisJsMaxThreads = threadPoolSize();
        },
      ],
      postRun: [
        async function () {
          const runtime = await runtimePromise;
          resolve({
            api: getQgisApiProxy(runtime),
            module: runtime as EmscriptenRuntimeModule,
            fs: runtime.FS,
          });
        },
      ],
      ...(config.onStatus ? { setStatus: config.onStatus } : {}),
    });
  });
}


================================================
FILE: packages/qgis-js/src/runtime.ts
================================================
import { EmscriptenRuntimeModule, EmscriptenFS } from "./emscripten";

import { QgisApi, InternalQgisApi } from "../../../src/api/QgisApi";

/**
 * Qt emscripten runtime module that exposes the QgisInternalApi
 */
export interface QgisRuntimeModule
  extends EmscriptenRuntimeModule, InternalQgisApi {}

/**
 * Boot configuration options for the QGIS runtime.
 */
export interface QgisRuntimeConfig {
  /**
   * The prefix to use for the {@link EmscriptenRuntimeModule} path.
   */
  prefix?: string;
  /**
   * A callback function that will be called when the runtime status changes.
   */
  onStatus?: (status: string) => void;
}

/**
 * Wraps the {@link EmscriptenRuntimeModule} and exposes the {@link QgisApi} and {@link EmscriptenFS}
 */
export interface QgisRuntime {
  api: QgisApi;
  module: EmscriptenRuntimeModule;
  fs: EmscriptenFS;
}

/**
 * Returns the thread pool size based on the hardware concurrency of the user's device.
 * The thread pool size is capped between a minium of 4 and a maximum 16 threads.
 *
 * @privateRemarks This needs to be in sync with PTHREAD_POOL_SIZE in CMakeLists.txt
 *
 * @returns The thread pool size of the qgis-js runtime
 */
export function threadPoolSize() {
  const MINIMAL_THREAD_POOL_SIZE = 4;
  const MAXIMAL_THREAD_POOL_SIZE = 16;
  return Math.min(
    Math.max(
      navigator?.hardwareConcurrency || MINIMAL_THREAD_POOL_SIZE,
      MINIMAL_THREAD_POOL_SIZE,
    ),
    MAXIMAL_THREAD_POOL_SIZE,
  );
}


================================================
FILE: packages/qgis-js/tsconfig.json
================================================
{
  "extends": "../../tsconfig.json",
  "include": ["src"]
}


================================================
FILE: packages/qgis-js/vite.config.ts
================================================
import { resolve } from "path";
import { defineConfig } from "vite";

import QgisRuntimePlugin from "../../build/vite/QgisRuntimePlugin";

import dts from "vite-plugin-dts";

import packageJson from "./package.json";

export default defineConfig({
  define: {
    __QGIS_JS_VERSION: JSON.stringify(packageJson.version),
  },
  plugins: [
    QgisRuntimePlugin({
      name: "qgis-js",
      outputDir: "build/wasm",
    }),
    dts({
      copyDtsFiles: true,

      staticImport: true,
      // insertTypesEntry: true,
      compilerOptions: {
        declarationMap: true,
      },

      rollupTypes: true,
      entryRoot: "src",
      rollupConfig: {
        docModel: {
          enabled: true,
          apiJsonFilePath: resolve(__dirname, "etc/qgis-js.api.json"),
        },
      },
      async afterBuild() {
        // remove empty export statement
        const fs = await import("fs");
        const path = await import("path");
        const dtsFile = path.join(__dirname, "dist", "qgis.d.ts");
        let content = fs.readFileSync(dtsFile, "utf-8");
        content = content.replace("export { }", "");

        // format file with prettier
        const prettier = await import("prettier");
        const prettierConfig = await prettier.resolveConfig(
          path.join(__dirname, "../..", ".prettierrc.json"),
        );

        content = await prettier.format(content, {
          ...prettierConfig,
          parser: "typescript",
        });

        fs.writeFileSync(dtsFile, content);
        return;
      },
    }),
  ],
  build: {
    lib: {
      entry: [resolve(__dirname, "src/index.ts")],
      name: "qgis-js",
      formats: ["es"],
      fileName: "qgis",
    },
  },
});


================================================
FILE: packages/qgis-js-ol/README.md
================================================
# @qgis-js/ol

**OpenLayers sources for [qgis-js](https://github.com/qgis/qgis-js)**

[qgis-js Repository](https://github.com/qgis/qgis-js) | [qgis-js Website](https://qgis.github.io/qgis-js) | ["`@qgis-js/ol`" package source](https://github.com/qgis/qgis-js/tree/main/packages/qgis-js-ol)

[![@qgis-js/ol on npm](https://img.shields.io/npm/v/@qgis-js/ol)](https://www.npmjs.com/package/@qgis-js/ol)

> ⚠️🧪 **Work in progress**! Currently this project is in public beta

## Prerequisites

- This package requires **[OpenLayers](https://openlayers.org) `>=8`** to be installed as a peer dependency

- The [qgis-js](https://www.npmjs.com/package/@qgis-js/ol) package is also required as a direct dependency of this package
  - An instance of the qgis-js runtime has to be created at runtime and its API must be passed to the OpenLayers source constructor

## Installation

```bash
npm install -S @qgis-js/ol
```

## Usage

### QgisCanvasDataSource

[OpenLayers](https://openlayers.org) source for rendering a single tile with the size and pixel ratio of the ol map canvas. This is useful for rendering in the projection of the QGIS project, both in the OpenLayers view and in the qgis-js runtime.

> See [QgisCanvasDataSource.ts](https://github.com/qgis/qgis-js/blob/main/packages/qgis-js-ol/src/QgisCanvasDataSource.ts) for the implementation.

### QgisXYZDataSource

[OpenLayers](https://openlayers.org) source to render a QGIS project in the common Web Mercator projection (EPSG:3857) addressed with the xyz tile scheme. This makes it convenient to mix the QGIS layer with other layer sources provided by OpenLayers.

> See [QgisXYZDataSource.ts](https://github.com/qgis/qgis-js/blob/main/packages/qgis-js-ol/src/QgisXYZDataSource.ts) for the implementation.

## Versioning

This package uses [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/qgis/qgis-js/tags).

## License

[GNU General Public License v2.0](https://github.com/qgis/qgis-js/blob/main/LICENSE)


================================================
FILE: packages/qgis-js-ol/package.json
================================================
{
  "name": "@qgis-js/ol",
  "version": "4.0.0",
  "description": "OpenLayers sources for qgis-js",
  "license": "GPL-2.0-or-later",
  "homepage": "https://qgis.github.io/qgis-js/",
  "repository": {
    "type": "git",
    "url": "https://github.com/qgis/qgis-js",
    "directory": "packages/qgis-js-ol"
  },
  "bugs": {
    "url": "https://github.com/qgis/qgis-js/issues"
  },
  "type": "module",
  "main": "dist/qgis-js-ol.js",
  "types": "dist/qgis-js-ol.d.ts",
  "files": [
    "dist/**/*"
  ],
  "scripts": {
    "build": "vite build"
  },
  "dependencies": {
    "qgis-js": "workspace:*"
  },
  "peerDependencies": {
    "ol": "^10.8.0"
  },
  "devDependencies": {
    "@types/node": "^22.19.15",
    "typescript": "5.8.2",
    "vite": "8.0.0",
    "vite-plugin-dts": "4.5.4"
  }
}


================================================
FILE: packages/qgis-js-ol/src/QgisCanvasDataSource.ts
================================================
import type { QgisApi } from "qgis-js";

import ImageSource, { Options } from "ol/source/Image";
import { getWidth, getHeight } from "ol/extent";

export interface QgisCanvasDataSourceOptions extends Options {
  layerIds?: string[];
  renderFunction?: QgisCanvasRenderFunction;
}

export type QgisCanvasRenderFunction = (
  api: QgisApi,
  srid: string,
  xMin: number,
  yMin: number,
  xMax: number,
  yMax: number,
  width: number,
  height: number,
  pixelRatio: number,
  layerIds?: string[],
) => Promise<ImageData>;

export class QgisCanvasDataSource extends ImageSource {
  protected api: QgisApi;

  protected static DEFAULT_RENDERFUNCTION: QgisCanvasRenderFunction = (
    api: QgisApi,
    srid: string,
    xMin: number,
    yMin: number,
    xMax: number,
    yMax: number,
    width: number,
    height: number,
    pixelRatio: number,
    layerIds?: string[],
  ) => {
    return api.renderImage(
      srid,
      new api.QgsRectangle(xMin, yMin, xMax, yMax),
      width,
      height,
      pixelRatio,
      layerIds,
    );
  };

  protected renderFunction: QgisCanvasRenderFunction | undefined;
  protected layerIds: string[] | undefined;

  protected getrenderFunction(): QgisCanvasRenderFunction {
    return this.renderFunction || QgisCanvasDataSource.DEFAULT_RENDERFUNCTION;
  }

  constructor(api: QgisApi, options: QgisCanvasDataSourceOptions = {}) {
    super({
      loader: (extent, resolution, requestPixelRatio) => {
        return new Promise(async (resolve) => {
          // note: requestPixelRatio is managed by ol and will not change on zoom
          const pixelRatio = requestPixelRatio || window?.devicePixelRatio || 1;
          const imageResolution = resolution / pixelRatio;
          const width = Math.round(getWidth(extent) / imageResolution);
          const height = Math.round(getHeight(extent) / imageResolution);

          const renderFunction = this.getrenderFunction();
          const imageData = await renderFunction(
            this.api,
            this.getProjection()?.getCode() || "EPSG:3857",
            extent[0],
            extent[1],
            extent[2],
            extent[3],
            width,
            height,
            pixelRatio,
            this.layerIds,
          );

          resolve(createImageBitmap(imageData));
        });
      },
      ...options,
    });

    this.api = api;
    this.renderFunction = options.renderFunction;
    this.layerIds = options.layerIds;
  }
}


================================================
FILE: packages/qgis-js-ol/src/QgisJobDataSource.ts
================================================
import type { QgisApi, QgsMapRendererJob } from "qgis-js";

import ImageSource, { Options } from "ol/source/Image";
import ImageWrapper from "ol/Image";
import { getWidth, getHeight, getCenter, containsExtent } from "ol/extent";
import {
  create as createTransform,
  compose as composeTransform,
} from "ol/transform";

export interface QgisJobDataSourceOptions extends Options {
  /**
   * Optional array of layer IDs to render. If omitted, renders all visible layers.
   */
  layerIds?: string[];

  /**
   * Specifies whether to enable preview mode.
   * (default: true)
   */
  preview?: boolean;

  /**
   * Specifies the timeout to wait before rendering the next preview (in milliseconds).
   * (default: 200)
   */
  previewTimeout?: number;

  /**
   * Specifies whether to enable overlay of the last fully rendered image on top of the previews.
   * (default: true)
   */
  previewOverlay?: boolean;
}

export class QgisJobDataSource extends ImageSource {
  protected api: QgisApi;
  protected layerIds: string[] | undefined;

  protected preview: boolean;
  protected previewTimeout: number;
  protected previewOverlay: boolean;

  private lastImage: ImageWrapper | null = null;

  private jobs: QgsMapRendererJob[] = [];

  constructor(api: QgisApi, options: QgisJobDataSourceOptions = {}) {
    super({
      loader: (extent, resolution, requestPixelRatio) => {
        return new Promise(async (resolve) => {
          this.dispatchEvent("jobstart");

          this.killPendingJobs();

          const pixelRatio = requestPixelRatio || window?.devicePixelRatio || 1;
          const imageResolution = resolution / pixelRatio;
          const width = Math.round(getWidth(extent) / imageResolution);
          const height = Math.round(getHeight(extent) / imageResolution);

          const canvas = document.createElement("canvas");

          canvas.width = width;
          canvas.height = height;
          const ctx = canvas.getContext("2d");

          const job = api.renderJob(
            this.getProjection()?.getCode() || "EPSG:3857",
            new api.QgsRectangle(extent[0], extent[1], extent[2], extent[3]),
            width,
            height,
            pixelRatio,
            this.layerIds,
          );
          this.jobs.push(job);

          const putRenderedImage = () => {
            const data = new Uint8ClampedArray(
              job.renderedImage(),
            ) as Uint8ClampedArray<ArrayBuffer>;
            const imageData = new ImageData(data, width, height);
            ctx!.putImageData(imageData, 0, 0);
          };

          if (this.preview) {
            const schedulePreivew = () => {
              requestAnimationFrame(() => {
                if (job.isActive()) {
                  // if the preview will be entirely overlaid (e.g. on zooming in), we can skip preview rendering
                  const skipPreviewRendering =
                    this.previewOverlay &&
                    this.lastImage &&
                    containsExtent(this.lastImage.getExtent(), extent);

                  if (!skipPreviewRendering) {
                    putRenderedImage();
                  }

                  if (this.previewOverlay && this.lastImage) {
                    const lastImageToDraw = this.lastImage.getImage();
                    if (lastImageToDraw) {
                      const imageExtent = this.lastImage.getExtent();
                      const imageResolution = this.lastImage.getResolution();
                      const [imageResolutionX, imageResolutionY] =
                        Array.isArray(imageResolution)
                          ? imageResolution
                          : [imageResolution, imageResolution];
                      const imagePixelRatio = this.lastImage.getPixelRatio();

                      const viewCenter = getCenter(extent);

                      const scaleX =
                        (pixelRatio * imageResolutionX) /
                        (resolution * imagePixelRatio);
                      const scaleY =
                        (pixelRatio * imageResolutionY) /
                        (resolution * imagePixelRatio);

                      const tempTransform = createTransform();
                      const transform = composeTransform(
                        tempTransform,
                        width / 2,
                        height / 2,
                        scaleX,
                        scaleY,
                        0,
                        (imagePixelRatio * (imageExtent[0] - viewCenter[0])) /
                          imageResolutionX,
                        (imagePixelRatio * (viewCenter[1] - imageExtent[3])) /
                          imageResolutionY,
                      );

                      const dw = lastImageToDraw.width * transform[0];
                      const dh = lastImageToDraw.height * transform[3];

                      const dx = transform[4];
                      const dy = transform[5];

                      ctx!.drawImage(
                        lastImageToDraw,
                        0,
                        0,
                        +lastImageToDraw.width,
                        +lastImageToDraw.height,
                        dx,
                        dy,
                        dw,
                        dh,
                      );
                    }
                  }

                  this.changed();

                  resolve(canvas); // will have no effect if the canvas is already resolved

                  // schedule the next preview if necessary and the job is still active
                  if (!skipPreviewRendering && job.isActive()) {
                    setTimeout(schedulePreivew, this.previewTimeout);
                  }
                }
              });
            };
            schedulePreivew();
          }

          job.finished(() => {
            putRenderedImage();

            // store the current canvas to reuse it in upcoming previews
            if (this.preview && this.previewOverlay) {
              this.lastImage = this.image;
            }

            this.changed();

            resolve(canvas); // will have no effect if the canvas is already resolved

            // remove the job from the list of pending jobs
            this.jobs = this.jobs.filter((j) => j !== job);

            this.dispatchEvent("jobend");
          });
        });
      },
      ...options,
    });

    this.api = api;
    this.layerIds = options.layerIds;
    this.preview =
      typeof options.preview !== "undefined" ? options.preview : true;
    this.previewTimeout =
      typeof options.previewTimeout !== "undefined"
        ? options.previewTimeout
        : 200;
    this.previewOverlay =
      typeof options.previewOverlay !== "undefined"
        ? options.previewOverlay
        : true;
  }

  public killPendingJobs() {
    while (this.jobs.length > 0) {
      const job = this.jobs.pop();
      if (job && job.isActive()) {
        new Promise((resolve) => {
          job.cancelWithoutBlocking();
          resolve(null);
        });
      }
    }
  }
}


================================================
FILE: packages/qgis-js-ol/src/QgisXYZDataSource.ts
================================================
import type { QgisApi } from "qgis-js";

import XYZ, { Options } from "ol/source/XYZ";

import { createCanvasContext2D } from "ol/dom";
import { toSize } from "ol/size";

import type { TileCoord } from "ol/tilecoord";
import ImageTile from "ol/ImageTile";

export interface QgisXYZDataSourceOptions extends Options {
  extentBufferFactor?: number | (() => number);
  layerIds?: string[];
  renderFunction?: QgisXYZRenderFunction;
  debug?: boolean;
}

export type QgisXYZRenderFunction = (
  api: QgisApi,
  tileCoord: TileCoord,
  tileSize: number,
  pixelRatio: number,
  extentBufferFactor: number,
  layerIds?: string[],
) => Promise<ImageData>;

export class QgisXYZDataSource extends XYZ {
  protected api: QgisApi;

  protected static DEFAULT_RENDERFUNCTION: QgisXYZRenderFunction = (
    api: QgisApi,
    tileCoord: TileCoord,
    tileSize: number,
    devicePixelRatio: number,
    extentBufferFactor: number,
    layerIds?: string[],
  ) => {
    return api.renderXYZTile(
      tileCoord[1],
      tileCoord[2],
      tileCoord[0],
      tileSize,
      devicePixelRatio,
      extentBufferFactor,
      layerIds,
    );
  };

  protected renderFunction: QgisXYZRenderFunction | undefined;
  protected layerIds: string[] | undefined;

  protected getrenderFunction(): QgisXYZRenderFunction {
    return this.renderFunction || QgisXYZDataSource.DEFAULT_RENDERFUNCTION;
  }

  protected static DEFAULT_EXTENTBUFFERFACTOR = 0;

  protected extentBufferFactor: number | number | (() => number) | undefined;

  protected getextentBufferFactor(): number {
    return typeof this.extentBufferFactor === "function"
      ? this.extentBufferFactor()
      : this.extentBufferFactor || QgisXYZDataSource.DEFAULT_EXTENTBUFFERFACTOR;
  }

  constructor(api: QgisApi, options: QgisXYZDataSourceOptions = {}) {
    super({
      tileUrlFunction: (tileCoord, pixelRatio) => {
        const tileSize = (
          toSize(this.tileGrid!.getTileSize(tileCoord[0])) as [number, number]
        )[0];
        return `${tileSize * (pixelRatio || 1)}`;
      },
      tileLoadFunction: async (tile, text) => {
        const renderFunction = this.getrenderFunction();
        if (this.tileGrid && renderFunction) {
          console.assert(tile instanceof ImageTile);
          const imageTile = tile as ImageTile;

          const tileSize = parseInt(text);
          const pixelRatio = Math.round(tileSize / 256);

          const context = createCanvasContext2D(tileSize, tileSize);

          const imageData = await renderFunction(
            this.api,
            tile.getTileCoord(),
            tileSize,
            pixelRatio,
            this.getextentBufferFactor(),
            this.layerIds,
          );
          context.putImageData(imageData, 0, 0);

          if (options.debug) {
            context.strokeStyle = "grey";
            context.strokeRect(0.5, 0.5, tileSize + 0.5, tileSize + 0.5);

            context.fillStyle = "darkgrey";
            context.strokeStyle = "black";
            context.textAlign = "center";
            context.textBaseline = "middle";
            context.font = "20px sans-serif";
            context.lineWidth = 2;
            context.strokeText(text, tileSize / 2, tileSize / 2, tileSize);
            context.fillText(text, tileSize / 2, tileSize / 2, tileSize);
          }

          imageTile.setImage(context.canvas);
        }
      },
      ...options,
    });

    this.api = api;
    this.renderFunction = options.renderFunction;
    this.extentBufferFactor = options.extentBufferFactor;
    this.layerIds = options.layerIds;
  }
}


================================================
FILE: packages/qgis-js-ol/src/index.ts
================================================
export { QgisCanvasDataSource } from "./QgisCanvasDataSource";
export { QgisXYZDataSource } from "./QgisXYZDataSource";
export { QgisJobDataSource } from "./QgisJobDataSource";


================================================
FILE: packages/qgis-js-ol/tsconfig.json
================================================
{
  "extends": "../../tsconfig.json",
  "include": ["src"]
}


================================================
FILE: packages/qgis-js-ol/vite.config.ts
================================================
import { resolve } from "path";
import { defineConfig } from "vite";

import dts from "vite-plugin-dts";

export default defineConfig({
  plugins: [
    dts({
      rollupTypes: true,
      entryRoot: "src",
    }),
  ],
  build: {
    lib: {
      entry: [resolve(__dirname, "src/index.ts")],
      name: "qgis-js-ol",
      formats: ["es"],
      fileName: "qgis-js-ol",
    },
    rollupOptions: {
      external: ["qgis-js", "ol", new RegExp("^ol/*")],
    },
  },
});


================================================
FILE: packages/qgis-js-utils/README.md
================================================
# @qgis-js/utils

**Utilities to integrate [qgis-js](https://github.com/qgis/qgis-js) into web applications**

[qgis-js Repository](https://github.com/qgis/qgis-js) | [qgis-js Website](https://qgis.github.io/qgis-js) | ["`@qgis-js/utils`" package source](https://github.com/qgis/qgis-js/tree/main/packages/qgis-js-ol)

[![@qgis-js/utils on npm](https://img.shields.io/npm/v/@qgis-js/utils)](https://www.npmjs.com/package/@qgis-js/utils)

> ⚠️🧪 **Work in progress**! Currently this project is in public beta

## Installation

```bash
npm install -S @qgis-js/utils
```

## Usage

### `useProjects`

Provides an abstraction to load QGIS projects from various sources.

```js
import { qgis } from "qgis-js";
import { useProjects } from "@qgis-js/utils";

const { api, fs } = await qgis();
const {
  openProject,
  loadLocalProject,
  loadGithubProjects,
  loadRemoteProjects,
} = useProjects(fs, (projectPath: string) => {
  api.loadProject(projectPath);
});
```

The following project sources are supported:

#### LocalProject

Loads QGIS projects from the user's file system with the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API)

```js
await openProject(await loadLocalProject());
```

#### GithubProject

Loads QGIS projects from a GitHub repository with the [GitHub API](https://docs.github.com/en/rest)

#### RemoteProject

Fetches QGIS projects from a remote server with the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)

- If `loadRemoteProjects` is invoked with a string as, it is assumed to be the URL of a JSON file with the following structure:

```json
{
  "name": "projects",
  "path": "projects",
  "type": "Folder",
  "entries": [
    {
      "name": "village",
      "path": "projects/village",
      "type": "Folder",
      "entries": [
        {
          "name": "project.qgs",
          "path": "projects/village/project.qgs",
          "type": "File"
        },
        {
          "name": "rgb.tif",
          "path": "projects/village/rgb.tif",
          "type": "File"
        }
      ]
    }
  ]
}
```

- Otherwise a `Folder` object can also be passed directly to `loadRemoteProjects`, see [FileSystem.ts](./src/fs/FileSystem.ts)

## Versioning

This package uses [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/qgis/qgis-js/tags).

## License

[GNU General Public License v2.0](https://github.com/qgis/qgis-js/blob/main/LICENSE)


================================================
FILE: packages/qgis-js-utils/package.json
================================================
{
  "name": "@qgis-js/utils",
  "version": "4.0.0",
  "description": "Utilities to integrate qgis-js into web applications",
  "license": "GPL-2.0-or-later",
  "homepage": "https://qgis.github.io/qgis-js/",
  "repository": {
    "type": "git",
    "url": "https://github.com/qgis/qgis-js",
    "directory": "packages/qgis-js-utils"
  },
  "bugs": {
    "url": "https://github.com/qgis/qgis-js/issues"
  },
  "type": "module",
  "main": "dist/qgis-js-utils.js",
  "types": "dist/qgis-js-utils.d.ts",
  "files": [
    "dist/**/*"
  ],
  "scripts": {
    "build": "vite build"
  },
  "dependencies": {
    "@types/emscripten": "1.41.5",
    "browser-fs-access": "0.38.0",
    "qgis-js": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^22.19.15",
    "typescript": "5.8.2",
    "vite": "8.0.0",
    "vite-plugin-dts": "4.5.4"
  }
}


================================================
FILE: packages/qgis-js-utils/src/fs/FileSystem.ts
================================================
export type FileSystemEntryType = "File" | "Folder";

export interface FileSystemEntry {
  name: string;
  path: string;
  type: FileSystemEntryType;
}

export interface File extends FileSystemEntry {}

export interface Folder extends FileSystemEntry {
  entries: FolderEntries;
}

export type FolderEntries = Array<File | Folder>;

/**
 * Flattens the "Folder" hierarchy and returns an array of folder paths.
 *
 * @param entries - The entries of the root folder.
 * @param basePath - The base path of the root folder.
 * @returns An array of folder paths.
 */
export function flatFolders(
  entries: Folder["entries"],
  basePath: string,
): string[] {
  return entries.reduce<string[]>((acc, entry) => {
    if (entry.type === "Folder") {
      return acc.concat([
        basePath + "/" + entry.name,
        ...flatFolders((entry as Folder).entries, basePath + "/" + entry.name),
      ]);
    } else {
      return acc;
    }
  }, []);
}

/**
 * Flattens the files in a "Folder" hierarchy and returns an array of file paths.
 *
 * @param entries - The entries of the root folder.
 * @param basePath - The base path of the root folder.
 * @returns An array of file paths.
 */
export function flatFiles(
  entries: Folder["entries"],
  basePath: string,
): string[] {
  return entries.reduce<string[]>((acc, entry) => {
    if (entry.type === "Folder") {
      return acc.concat(
        flatFiles((entry as Folder).entries, basePath + "/" + entry.name),
      );
    } else {
      return acc.concat(basePath + "/" + entry.name);
    }
  }, []);
}


================================================
FILE: packages/qgis-js-utils/src/fs/GithubProject.ts
================================================
import type { EmscriptenFS } from "qgis-js";

import { Project, PROJECTS_UPLOAD_DIR } from "./Project";

import { Folder, flatFolders, flatFiles } from "./FileSystem";

export const GIT_DEFAULT_BRANCH = "main";

/**
 * Fetches the contents of a directory in a GitHub repository via the GitHub REST API.
 *
 * Be aware of the GitHub API rate limits: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api
 *
 * @param owner The owner of the repository.
 * @param repo The name of the repository.
 * @param path The path to the directory to fetch. Defaults to the root directory.
 * @param branch The branch to fetch the directory from. Defaults to the default branch of the repository.
 * @returns A Promise that resolves to an array of objects representing the files and directories in the specified directory.
 */
export async function fetchGithubDirectory(
  owner: string,
  repo: string,
  path: string = "/",
  branch: string = GIT_DEFAULT_BRANCH,
) {
  const url = `https://api.github.com/repos/${owner}/${repo}/contents${path}?ref=${branch}`;
  const response = await fetch(url);
  const content = (await response.json()) as Array<{
    type: "file" | "dir";
    name: string;
    path: string;
    sha: string;
  }>;
  return content;
}

/**
 * Fetches a list of relative file paths in a GitHub repository tree via the GitHub REST API.
 *
 * Be aware of the GitHub API rate limits: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api
 *
 * @param owner The owner of the repository.
 * @param repo The name of the repository.
 * @param sha The SHA of the tree to fetch the files from.
 * @returns A Promise that resolves to an array of relative file paths in the specified tree.
 */
export async function fetchGithubTreeFiles(
  owner: string,
  repo: string,
  sha: string,
) {
  const url = `https://api.github.com/repos/${owner}/${repo}/git/trees/${sha}?recursive=1`;
  const response = await fetch(url);
  const content = (await response.json()) as {
    tree: {
      type: "tree" | "blob";
      path: string;
    }[];
  };
  return content.tree
    .filter((entry) => entry.type === "blob")
    .map((entry) => entry.path);
}

/**
 * Fetches the content of a file from a GitHub repository via the GitHub REST API.
 *
 * Be aware of the GitHub API rate limits: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api
 *
 * @param owner The owner of the repository.
 * @param repo The name of the repository.
 * @param path The path to the file. Defaults to the root directory.
 * @param branch The branch to fetch the file from. Defaults to the default branch of the repository.
 * @returns A Promise that resolves to the file content as a buffer.
 * @throws An error if the file content is not found.
 */
export async function fetchGithubFileContent(
  owner: string,
  repo: string,
  path: string = "/",
  branch: string = GIT_DEFAULT_BRANCH,
) {
  const url = `https://api.github.com/repos/${owner}/${repo}/contents${path}?ref=${branch}`;
  const response = await fetch(url);
  const file = (await response.json()) as {
    type: "file" | "dir";
    content?: string;
    download_url?: string;
  };
  if (file.content && file.content !== "") {
    return Uint8Array.from(atob(file.content), (c) => c.charCodeAt(0)).buffer;
  } else if (file.download_url && file.download_url !== "") {
    const response = await fetch(file.download_url);
    return response.arrayBuffer();
  } else {
    throw new Error("File content of " + path + " not found.");
  }
}

/**
 * Maps an array of file paths to a "Folder" structure.
 *
 * @param name - The name of the root folder.
 * @param path - The path of the root folder.
 * @param files - An array of relative file paths.
 * @returns The root folder with the mapped "Folder" structure.
 */
export function mapFilesToFolder(
  name: string,
  path: string,
  files: string[],
): Folder {
  const root: File | Folder = { name, path, type: "Folder", entries: [] };
  files.sort().forEach((path) => {
    const parts = path.split("/");
    let current = root;
    for (let i = 0; i < parts.length - 1; i++) {
      const part = parts[i];
      const entry = current.entries.find((entry) => entry.name === part);
      if (entry) {
        current = entry as Folder;
      } else {
        const folder: Folder = {
          name: part,
          path: current.path + "/" + part,
          type: "Folder",
          entries: [],
        };
        current.entries.push(folder);
        current = folder;
      }
    }
    current.entries.push({
      name: parts[parts.length - 1],
      path,
      type: "File",
    });
  });
  return root;
}

export class GithubProject extends Project {
  protected folder: Folder;
  protected path: string;

  protected owner: string;
  protected repo: string;
  protected branch: string;

  constructor(
    FS: EmscriptenFS,
    projectFolder: Folder,
    owner: string,
    repo: string,
    branch: string = GIT_DEFAULT_BRANCH,
  ) {
    super(FS, "Github");

    this.folder = projectFolder;

    this.name = projectFolder.name;
    this.path = projectFolder.path;

    this.owner = owner;
    this.repo = repo;
    this.branch = branch;
  }

  getDirectories(): string[] {
    return [this.path, ...flatFolders(this.folder.entries, this.path)];
  }

  getFiles(): string[] {
    return flatFiles(this.folder.entries, this.path);
  }

  async uploadProject() {
    // prepare to download all files in parallel
    const downloads = Object.fromEntries(
      this.getFiles().map((file) => {
        return [
          file,
          new Promise<ArrayBuffer>(async (resolve, _reject) => {
            // note that we don't use fetchGithubFileContent here because of the GitHub API rate limits
            // instead we use the raw.githubusercontent.com URL directly
            const url = `https://raw.githubusercontent.com/${this.owner}/${this.repo}/${this.branch}/${file}`;
            const response = await fetch(url);
            resolve(response.arrayBuffer());
          }),
        ];
      }),
    );
    // wait for all the responses
    await Promise.all([Object.values(downloads)]);
    // ensure directories exist
    this.ensureDirectories();
    // write files to the runtime FS
    for (const file of this.getFiles()) {
      const data = new Uint8Array(await downloads[file]);
      this.FS.writeFile(PROJECTS_UPLOAD_DIR + "/" + file, data);
    }
  }
}


================================================
FILE: packages/qgis-js-utils/src/fs/LocalProject.ts
================================================
import type { EmscriptenFS } from "qgis-js";

import { Project, PROJECTS_UPLOAD_DIR } from "./Project";

import {
  directoryOpen,
  FileWithDirectoryAndFileHandle,
} from "browser-fs-access";

export type LocalEntries = Array<FileWithDirectoryAndFileHandle>;

export { directoryOpen as openLocalDirectory };

export class LocalProject extends Project {
  protected entries: LocalEntries;

  constructor(FS: EmscriptenFS, entries: LocalEntries) {
    super(FS, "Local");

    this.entries = entries;

    const possibleNames = entries
      .map((e) => e.webkitRelativePath)
      .filter((p) => p && p.length > 0 && p.includes("/"))
      .map((p) => p.split("/", 1)[0]);

    if (possibleNames.length < 1) {
      throw new Error("Could not determine project name");
    }
    // just use the first possible name as final project name
    this.name = possibleNames[0];
  }

  getDirectories(): string[] {
    const subFoldersToCreate = new Set<string>();
    subFoldersToCreate.add(this.name);
    for (const entry of this.entries) {
      const path = (entry as FileWithDirectoryAndFileHandle).webkitRelativePath;
      s
Download .txt
gitextract_sch8wsm5/

├── .clang-format
├── .editorconfig
├── .github/
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .gitmodules
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── .vscode/
│   ├── c_cpp_properties.json
│   └── settings.json
├── CHANGELOG.md
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build/
│   ├── actions/
│   │   ├── clean.ts
│   │   ├── compile.ts
│   │   ├── install.ts
│   │   ├── lib/
│   │   │   ├── BuildType.ts
│   │   │   └── QgisJsOptions.ts
│   │   ├── libs.ts
│   │   └── size.ts
│   ├── scripts/
│   │   ├── clean.sh
│   │   ├── compile.sh
│   │   └── install.sh
│   ├── vcpkg-ports/
│   │   ├── libspatialindex/
│   │   │   ├── portfile.cmake
│   │   │   └── vcpkg.json
│   │   └── qgis/
│   │       ├── portfile.cmake
│   │       └── vcpkg.json
│   ├── vcpkg-toolchains/
│   │   └── qgis-js.cmake
│   ├── vcpkg-triplets/
│   │   └── wasm32-emscripten-qt-threads.cmake
│   └── vite/
│       ├── CrossOriginIsolationPlugin.ts
│       ├── DirectoryListingPlugin.ts
│       └── QgisRuntimePlugin.ts
├── docs/
│   ├── architecture.md
│   ├── bundling.md
│   ├── ci.md
│   ├── compatibility.md
│   ├── debugging.md
│   ├── examples/
│   │   ├── qgis-js-example-api/
│   │   │   ├── index.html
│   │   │   ├── main.js
│   │   │   ├── package.json
│   │   │   └── vite.config.js
│   │   └── qgis-js-example-ol/
│   │       ├── index.html
│   │       ├── main.js
│   │       ├── package.json
│   │       ├── style.css
│   │       └── vite.config.js
│   ├── performance.md
│   └── profiling.md
├── package.json
├── packages/
│   ├── qgis-js/
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── QgisApiAdapter.ts
│   │   │   ├── emscripten.ts
│   │   │   ├── index.ts
│   │   │   ├── loader.ts
│   │   │   └── runtime.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   ├── qgis-js-ol/
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── QgisCanvasDataSource.ts
│   │   │   ├── QgisJobDataSource.ts
│   │   │   ├── QgisXYZDataSource.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   └── qgis-js-utils/
│       ├── README.md
│       ├── package.json
│       ├── src/
│       │   ├── fs/
│       │   │   ├── FileSystem.ts
│       │   │   ├── GithubProject.ts
│       │   │   ├── LocalProject.ts
│       │   │   ├── Project.ts
│       │   │   ├── RemoteProject.ts
│       │   │   └── index.ts
│       │   └── index.ts
│       ├── tsconfig.json
│       └── vite.config.ts
├── pnpm-workspace.yaml
├── qgis-js.ts
├── sites/
│   ├── dev/
│   │   ├── index.html
│   │   ├── package.json
│   │   ├── public/
│   │   │   └── projects/
│   │   │       └── village/
│   │   │           ├── buildings.dbf
│   │   │           ├── buildings.prj
│   │   │           ├── buildings.qpj
│   │   │           ├── buildings.shp
│   │   │           ├── buildings.shx
│   │   │           ├── project.qgs
│   │   │           └── rgb.tif
│   │   ├── src/
│   │   │   ├── demo.css
│   │   │   ├── index.ts
│   │   │   ├── js.ts
│   │   │   ├── layers.ts
│   │   │   └── ol.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   └── performance/
│       ├── .gitignore
│       ├── index.html
│       ├── package.json
│       ├── playwright.config.ts
│       ├── report.html
│       ├── src/
│       │   └── index.ts
│       ├── tests/
│       │   └── performance.spec.ts
│       ├── tsconfig.json
│       └── vite.config.ts
├── src/
│   ├── api/
│   │   ├── QgisApi.cpp
│   │   ├── QgisApi.ts
│   │   └── QgisModel.ts
│   ├── model/
│   │   ├── QgsLayerTreeGroup.hpp
│   │   ├── QgsLayerTreeGroup.ts
│   │   ├── QgsLayerTreeLayer.hpp
│   │   ├── QgsLayerTreeLayer.ts
│   │   ├── QgsLayerTreeModelLegendNode.hpp
│   │   ├── QgsLayerTreeModelLegendNode.ts
│   │   ├── QgsLayerTreeNode.hpp
│   │   ├── QgsLayerTreeNode.ts
│   │   ├── QgsMapLayer.hpp
│   │   ├── QgsMapLayer.ts
│   │   ├── QgsMapRendererJob.hpp
│   │   ├── QgsMapRendererJob.ts
│   │   ├── QgsMapRendererParallelJob.hpp
│   │   ├── QgsMapRendererParallelJob.ts
│   │   ├── QgsMapRendererQImageJob.hpp
│   │   ├── QgsMapRendererQImageJob.ts
│   │   ├── QgsPointXY.hpp
│   │   ├── QgsPointXY.ts
│   │   ├── QgsRectangle.hpp
│   │   └── QgsRectangle.ts
│   ├── qgis-js.cpp
│   └── qt.conf
├── tsconfig.json
└── vcpkg.json
Download .txt
SYMBOL INDEX (255 symbols across 58 files)

FILE: build/actions/clean.ts
  class CleanAction (line 7) | class CleanAction extends CommandLineAction {
    method constructor (line 10) | public constructor(options: QgisJsOptions) {
    method onExecuteAsync (line 19) | protected onExecuteAsync(): Promise<void> {

FILE: build/actions/compile.ts
  class CompileAction (line 17) | class CompileAction extends CommandLineAction {
    method constructor (line 23) | public constructor(options: QgisJsOptions) {
    method onExecuteAsync (line 49) | protected onExecuteAsync(): Promise<void> {
  function lastBuildType (line 125) | async function lastBuildType(): Promise<BuildType | undefined> {

FILE: build/actions/install.ts
  class InstallAction (line 7) | class InstallAction extends CommandLineAction {
    method constructor (line 10) | public constructor(options: QgisJsOptions) {
    method onExecuteAsync (line 19) | protected onExecuteAsync(): Promise<void> {

FILE: build/actions/lib/BuildType.ts
  type BuildType (line 1) | type BuildType = "Dev" | "Release" | "Debug";

FILE: build/actions/lib/QgisJsOptions.ts
  type QgisJsOptions (line 1) | interface QgisJsOptions {

FILE: build/actions/libs.ts
  class LibsAction (line 9) | class LibsAction extends CommandLineAction {
    method constructor (line 13) | public constructor(options: QgisJsOptions) {
    method onExecuteAsync (line 31) | protected onExecuteAsync(): Promise<void> {

FILE: build/actions/size.ts
  class SizeAction (line 12) | class SizeAction extends CommandLineAction {
    method constructor (line 16) | public constructor(options: QgisJsOptions) {
    method onExecuteAsync (line 34) | protected onExecuteAsync(): Promise<void> {

FILE: build/vite/CrossOriginIsolationPlugin.ts
  function CrossOriginIsolationPlugin (line 11) | function CrossOriginIsolationPlugin(): Plugin {

FILE: build/vite/DirectoryListingPlugin.ts
  constant FILTER_LIST (line 9) | const FILTER_LIST = [".DS_Store", ".git", ".gitignore", ".env"];
  constant DIRECTORY_LISTING_FILENAME (line 10) | const DIRECTORY_LISTING_FILENAME = "directory-listing.json";
  function DirectoryListingPlugin (line 14) | function DirectoryListingPlugin(
  function readFolder (line 83) | async function readFolder(folder: string): Promise<Dirent[]> {
  function createDirectoryListing (line 87) | async function createDirectoryListing(

FILE: build/vite/QgisRuntimePlugin.ts
  constant RUNTIME_JS (line 12) | const RUNTIME_JS = "js";
  constant RUNTIME_WASM (line 13) | const RUNTIME_WASM = "wasm";
  constant RUNTIME_WASM_MAP (line 14) | const RUNTIME_WASM_MAP = "wasm.map";
  constant RUNTIME_WASM_DEBUG (line 15) | const RUNTIME_WASM_DEBUG = "wasm.debug.wasm";
  constant RUNTIME_DATA (line 16) | const RUNTIME_DATA = "data";
  constant BASE_DIR (line 18) | const BASE_DIR = "wasm";
  type Runtime (line 20) | interface Runtime {
  function patchEmccJs (line 25) | function patchEmccJs(content: string): string {
  function QgisRuntimePlugin (line 35) | function QgisRuntimePlugin(_runtime: Runtime | null): Plugin {

FILE: docs/examples/qgis-js-example-api/main.js
  constant QGIS_JS_DIST (line 1) | const QGIS_JS_DIST = window.location.pathname + "node_modules/qgis-js/di...
  function printRect (line 27) | function printRect(rect) {

FILE: docs/examples/qgis-js-example-ol/main.js
  function createQgisLayer (line 40) | function createQgisLayer() {

FILE: docs/examples/qgis-js-example-ol/vite.config.js
  method configureServer (line 17) | configureServer(server) {

FILE: packages/qgis-js-ol/src/QgisCanvasDataSource.ts
  type QgisCanvasDataSourceOptions (line 6) | interface QgisCanvasDataSourceOptions extends Options {
  type QgisCanvasRenderFunction (line 11) | type QgisCanvasRenderFunction = (
  class QgisCanvasDataSource (line 24) | class QgisCanvasDataSource extends ImageSource {
    method getrenderFunction (line 52) | protected getrenderFunction(): QgisCanvasRenderFunction {
    method constructor (line 56) | constructor(api: QgisApi, options: QgisCanvasDataSourceOptions = {}) {

FILE: packages/qgis-js-ol/src/QgisJobDataSource.ts
  type QgisJobDataSourceOptions (line 11) | interface QgisJobDataSourceOptions extends Options {
  class QgisJobDataSource (line 36) | class QgisJobDataSource extends ImageSource {
    method constructor (line 48) | constructor(api: QgisApi, options: QgisJobDataSourceOptions = {}) {
    method killPendingJobs (line 203) | public killPendingJobs() {

FILE: packages/qgis-js-ol/src/QgisXYZDataSource.ts
  type QgisXYZDataSourceOptions (line 11) | interface QgisXYZDataSourceOptions extends Options {
  type QgisXYZRenderFunction (line 18) | type QgisXYZRenderFunction = (
  class QgisXYZDataSource (line 27) | class QgisXYZDataSource extends XYZ {
    method getrenderFunction (line 52) | protected getrenderFunction(): QgisXYZRenderFunction {
    method getextentBufferFactor (line 60) | protected getextentBufferFactor(): number {
    method constructor (line 66) | constructor(api: QgisApi, options: QgisXYZDataSourceOptions = {}) {

FILE: packages/qgis-js-utils/src/fs/FileSystem.ts
  type FileSystemEntryType (line 1) | type FileSystemEntryType = "File" | "Folder";
  type FileSystemEntry (line 3) | interface FileSystemEntry {
  type File (line 9) | interface File extends FileSystemEntry {}
  type Folder (line 11) | interface Folder extends FileSystemEntry {
  type FolderEntries (line 15) | type FolderEntries = Array<File | Folder>;
  function flatFolders (line 24) | function flatFolders(
  function flatFiles (line 47) | function flatFiles(

FILE: packages/qgis-js-utils/src/fs/GithubProject.ts
  constant GIT_DEFAULT_BRANCH (line 7) | const GIT_DEFAULT_BRANCH = "main";
  function fetchGithubDirectory (line 20) | async function fetchGithubDirectory(
  function fetchGithubTreeFiles (line 47) | async function fetchGithubTreeFiles(
  function fetchGithubFileContent (line 77) | async function fetchGithubFileContent(
  function mapFilesToFolder (line 108) | function mapFilesToFolder(
  class GithubProject (line 142) | class GithubProject extends Project {
    method constructor (line 150) | constructor(
    method getDirectories (line 169) | getDirectories(): string[] {
    method getFiles (line 173) | getFiles(): string[] {
    method uploadProject (line 177) | async uploadProject() {

FILE: packages/qgis-js-utils/src/fs/LocalProject.ts
  type LocalEntries (line 10) | type LocalEntries = Array<FileWithDirectoryAndFileHandle>;
  class LocalProject (line 14) | class LocalProject extends Project {
    method constructor (line 17) | constructor(FS: EmscriptenFS, entries: LocalEntries) {
    method getDirectories (line 34) | getDirectories(): string[] {
    method getFiles (line 44) | getFiles(): string[] {
    method uploadProject (line 48) | async uploadProject(): Promise<void> {

FILE: packages/qgis-js-utils/src/fs/Project.ts
  type ProjectType (line 3) | type ProjectType = "Remote" | "Local" | "Github";
  constant PROJECTS_UPLOAD_DIR (line 5) | const PROJECTS_UPLOAD_DIR = "/upload/projects";
  method constructor (line 12) | constructor(FS: EmscriptenFS, type: ProjectType) {
  method isValid (line 22) | isValid(): boolean {
  method getProjectFile (line 26) | getProjectFile(): string | undefined {
  method getDirectoriesToCreate (line 40) | getDirectoriesToCreate(): string[] {
  method ensureDirectories (line 52) | ensureDirectories() {
  method isProjectUploaded (line 65) | isProjectUploaded() {

FILE: packages/qgis-js-utils/src/fs/RemoteProject.ts
  class RemoteProject (line 7) | class RemoteProject extends Project {
    method constructor (line 13) | constructor(FS: EmscriptenFS, basePath: string, projectFolder: Folder) {
    method getDirectories (line 25) | getDirectories(): string[] {
    method getFiles (line 29) | getFiles(): string[] {
    method uploadProject (line 33) | async uploadProject() {

FILE: packages/qgis-js-utils/src/fs/index.ts
  function useProjects (line 15) | function useProjects(

FILE: packages/qgis-js/src/QgisApiAdapter.ts
  class QgisApiAdapterImplementation (line 13) | class QgisApiAdapterImplementation implements QgisApiAdapter {
    method constructor (line 18) | constructor(api: InternalQgisApi) {
    method runLimited (line 24) | protected runLimited<T>(fn: () => Promise<T>): Promise<T> {
    method renderImage (line 28) | renderImage(
    method renderXYZTile (line 56) | renderXYZTile(
    method mapThemes (line 87) | mapThemes(): readonly string[] {
  function getQgisApiProxy (line 97) | function getQgisApiProxy(api: InternalQgisApi): QgisApi {

FILE: packages/qgis-js/src/emscripten.ts
  type EmscriptenRuntimeModule (line 6) | interface EmscriptenRuntimeModule extends EmscriptenModule {
  type EmscriptenFS (line 15) | type EmscriptenFS = typeof FS;

FILE: packages/qgis-js/src/index.ts
  constant QGIS_JS_VERSION (line 4) | const QGIS_JS_VERSION: string =

FILE: packages/qgis-js/src/loader.ts
  type QtAppConfig (line 15) | interface QtAppConfig {}
  type QtRuntimeFactory (line 20) | interface QtRuntimeFactory {
  function loadModule (line 30) | function loadModule(mainScriptPath: string): Promise<QtRuntimeFactory> {
  function qgis (line 52) | async function qgis(

FILE: packages/qgis-js/src/runtime.ts
  type QgisRuntimeModule (line 8) | interface QgisRuntimeModule
  type QgisRuntimeConfig (line 14) | interface QgisRuntimeConfig {
  type QgisRuntime (line 28) | interface QgisRuntime {
  function threadPoolSize (line 42) | function threadPoolSize() {

FILE: packages/qgis-js/vite.config.ts
  method afterBuild (line 36) | async afterBuild() {

FILE: qgis-js.ts
  class QgisJsCommandLine (line 23) | class QgisJsCommandLine extends CommandLineParser {
    method constructor (line 24) | public constructor() {
    method onExecuteAsync (line 42) | protected onExecuteAsync(): Promise<void> {

FILE: sites/dev/src/index.ts
  function isDev (line 17) | function isDev() {
  constant GITHUB_REPOS (line 30) | const GITHUB_REPOS: Array<{
  function testApi (line 43) | function testApi(api: QgisApi) {
  function initDemo (line 56) | async function initDemo() {

FILE: sites/dev/src/js.ts
  function jsDemo (line 8) | function jsDemo(

FILE: sites/dev/src/layers.ts
  function renderNode (line 9) | function renderNode(
  function layersControl (line 162) | function layersControl(

FILE: sites/dev/src/ol.ts
  function olDemoXYZ (line 32) | function olDemoXYZ(
  function olDemoCanvas (line 141) | function olDemoCanvas(
  function olPreview (line 230) | function olPreview(

FILE: sites/performance/src/index.ts
  constant API (line 7) | let API: QgisApi;
  constant GITHUB_REPOS (line 18) | const GITHUB_REPOS: Array<{
  constant PROJECTS (line 26) | const PROJECTS = new Map<string, () => Project | Promise<Project>>();
  constant OPEN_PROJECT (line 27) | let OPEN_PROJECT: any;
  function init (line 29) | async function init() {
  function bootRuntime (line 57) | async function bootRuntime(_eClick: Event) {
  function loadProject (line 141) | async function loadProject(_eClick: Event) {
  function renderFirstFrame (line 152) | async function renderFirstFrame(_eClick: Event) {
  function startPerformanceTest (line 170) | async function startPerformanceTest(_eClick: Event) {
  function measureStart (line 327) | function measureStart(name: string) {
  function measureEnd (line 331) | function measureEnd(name: string) {
  function tryCatch (line 345) | function tryCatch(handler: (event: Event) => void) {
  function reportError (line 356) | function reportError(error: any) {

FILE: src/api/QgisApi.cpp
  function QgisApi_allLayers (line 30) | QList<QgsMapLayer *> QgisApi_allLayers() {
  function QgisApi_visibleLayers (line 34) | QList<QgsMapLayer *> QgisApi_visibleLayers() {
  function resolveLayers (line 47) | QList<QgsMapLayer *> resolveLayers(std::optional<emscripten::val> layerI...
  function QgisApi_loadProject (line 60) | bool QgisApi_loadProject(std::string filename) {
  function QgsRectangle (line 70) | QgsRectangle QgisApi_fullExtent(std::optional<emscripten::val> layerIds) {
  function QgisApi_srid (line 77) | std::string QgisApi_srid() {
  function QgisApi_renderXYZTile (line 81) | void QgisApi_renderXYZTile(
  function QgisApi_renderImage (line 130) | void QgisApi_renderImage(
  function QgsMapRendererParallelJob (line 183) | QgsMapRendererParallelJob *QgisApi_renderJob(
  function QgsRectangle (line 230) | const QgsRectangle QgisApi_transformRectangle(
  function LayerTreeGroup (line 239) | LayerTreeGroup QgisApi_layerTreeRoot() {
  function QgisApi_mapThemes (line 243) | const std::vector<std::string> QgisApi_mapThemes() {
  function QgisApi_getMapTheme (line 251) | const std::string QgisApi_getMapTheme() {
  function QgisApi_setMapTheme (line 264) | const bool QgisApi_setMapTheme(std::string themeName) {
  function QgisApi_globalVariables (line 277) | emscripten::val QgisApi_globalVariables() {
  function QgisApi_projectVariables (line 286) | emscripten::val QgisApi_projectVariables() {
  function QgisApi_setGlobalVariables (line 296) | void QgisApi_setGlobalVariables(emscripten::val variables) {
  function QgisApi_setProjectVariables (line 308) | void QgisApi_setProjectVariables(emscripten::val variables) {
  function QgisApi_renderLegend (line 320) | std::string QgisApi_renderLegend(float dpi, std::optional<emscripten::va...
  type LayerDefinitionResult (line 334) | struct LayerDefinitionResult {
  function LayerDefinitionResult (line 339) | LayerDefinitionResult
  function EMSCRIPTEN_BINDINGS (line 351) | EMSCRIPTEN_BINDINGS(QgisApi) {

FILE: src/api/QgisApi.ts
  type LayerDefinitionResult (line 8) | interface LayerDefinitionResult {
  type CommonQgisApi (line 16) | interface CommonQgisApi extends QgisModelConstructors {
  type QgisApiAdapter (line 159) | interface QgisApiAdapter {
  type QgisApi (line 212) | interface QgisApi extends CommonQgisApi, QgisApiAdapter {}
  type InternalQgisApi (line 219) | interface InternalQgisApi extends CommonQgisApi {

FILE: src/api/QgisModel.ts
  type QgisModelConstructors (line 37) | interface QgisModelConstructors

FILE: src/model/QgsLayerTreeGroup.hpp
  class LayerTreeGroup (line 10) | class LayerTreeGroup : public LayerTreeNode {
    method LayerTreeGroup (line 12) | LayerTreeGroup() : LayerTreeNode(nullptr) {}
    method LayerTreeGroup (line 13) | LayerTreeGroup(QgsLayerTreeGroup *group) : LayerTreeNode(group) {}
    method findLayers (line 15) | emscripten::val findLayers() const {
    method findGroup (line 25) | emscripten::val findGroup(std::string name) const {
    method findLayer (line 33) | emscripten::val findLayer(std::string layerId) const {
    method isMutuallyExclusive (line 41) | bool isMutuallyExclusive() const {
    method setIsMutuallyExclusive (line 46) | void setIsMutuallyExclusive(bool exclusive) {
    method renderLegend (line 50) | std::string renderLegend(float dpi) const {
    method QgsLayerTreeGroup (line 59) | QgsLayerTreeGroup *asGroup() const {
  function EMSCRIPTEN_BINDINGS (line 65) | EMSCRIPTEN_BINDINGS(QgsLayerTreeGroup) {

FILE: src/model/QgsLayerTreeGroup.ts
  type QgsLayerTreeGroup (line 4) | interface QgsLayerTreeGroup extends QgsLayerTreeNode {

FILE: src/model/QgsLayerTreeLayer.hpp
  class LayerTreeLayer (line 11) | class LayerTreeLayer : public LayerTreeNode {
    method LayerTreeLayer (line 13) | LayerTreeLayer() : LayerTreeNode(nullptr) {}
    method LayerTreeLayer (line 14) | LayerTreeLayer(QgsLayerTreeLayer *layer) : LayerTreeNode(layer) {}
    method layerId (line 16) | std::string layerId() const {
    method layer (line 21) | emscripten::val layer() const {
    method renderLegend (line 26) | std::string renderLegend(float dpi) const {
    method legendNodes (line 34) | emscripten::val legendNodes() const {
    method QgsLayerTreeLayer (line 47) | QgsLayerTreeLayer *asLayer() const {
  function wrapNode (line 53) | inline emscripten::val wrapNode(QgsLayerTreeNode *node) {
  function EMSCRIPTEN_BINDINGS (line 60) | EMSCRIPTEN_BINDINGS(QgsLayerTreeLayer) {

FILE: src/model/QgsLayerTreeLayer.ts
  type QgsLayerTreeLayer (line 5) | interface QgsLayerTreeLayer extends QgsLayerTreeNode {

FILE: src/model/QgsLayerTreeModelLegendNode.hpp
  function imageToDataUrl (line 29) | inline std::string imageToDataUrl(const QImage &image) {
  function renderLegendForTree (line 38) | inline std::string renderLegendForTree(QgsLayerTree *tree, float dpi) {
  class LegendNode (line 72) | class LegendNode {
    method LegendNode (line 74) | LegendNode() : _node(nullptr) {}
    method LegendNode (line 75) | LegendNode(QgsLayerTreeModelLegendNode *node) : _node(node) {}
    method isValid (line 77) | bool isValid() const {
    method label (line 81) | std::string label() const {
    method symbolImage (line 86) | std::string symbolImage(int size) const {
  function EMSCRIPTEN_BINDINGS (line 107) | EMSCRIPTEN_BINDINGS(QgsLayerTreeModelLegendNode) {

FILE: src/model/QgsLayerTreeModelLegendNode.ts
  type QgsLayerTreeModelLegendNode (line 1) | interface QgsLayerTreeModelLegendNode {

FILE: src/model/QgsLayerTreeNode.hpp
  class LayerTreeGroup (line 10) | class LayerTreeGroup
  class LayerTreeLayer (line 11) | class LayerTreeLayer
  class LayerTreeNode (line 15) | class LayerTreeNode {
    method LayerTreeNode (line 17) | LayerTreeNode() : _node(nullptr) {}
    method LayerTreeNode (line 18) | LayerTreeNode(QgsLayerTreeNode *node) : _node(node) {}
    method isValid (line 21) | bool isValid() const {
    method nodeType (line 25) | int nodeType() const {
    method isGroup (line 30) | bool isGroup() const {
    method isLayer (line 34) | bool isLayer() const {
    method name (line 38) | std::string name() const {
    method setName (line 43) | void setName(std::string name) {
    method isVisible (line 47) | bool isVisible() const {
    method itemVisibilityChecked (line 52) | bool itemVisibilityChecked() const {
    method setItemVisibilityChecked (line 57) | void setItemVisibilityChecked(bool checked) {
    method isExpanded (line 61) | bool isExpanded() const {
    method setExpanded (line 66) | void setExpanded(bool expanded) {
    method children (line 70) | emscripten::val children() const {
    method QgsLayerTreeNode (line 79) | QgsLayerTreeNode *nativeNode() const {
  function EMSCRIPTEN_BINDINGS (line 87) | EMSCRIPTEN_BINDINGS(QgsLayerTreeNode) {

FILE: src/model/QgsLayerTreeNode.ts
  type NodeType (line 4) | enum NodeType {
  type QgsLayerTreeNode (line 9) | interface QgsLayerTreeNode {

FILE: src/model/QgsMapLayer.hpp
  class VectorLayer (line 11) | class VectorLayer
    method VectorLayer (line 59) | VectorLayer() : MapLayer(nullptr) {}
    method VectorLayer (line 60) | VectorLayer(QgsVectorLayer *layer) : MapLayer(layer) {}
    method subsetString (line 62) | std::string subsetString() const {
    method setSubsetString (line 67) | bool setSubsetString(std::string subset) {
    method QgsVectorLayer (line 73) | QgsVectorLayer *asVectorLayer() const {
  class MapLayer (line 15) | class MapLayer {
    method MapLayer (line 17) | MapLayer() : _layer(nullptr) {}
    method MapLayer (line 18) | MapLayer(QgsMapLayer *layer) : _layer(layer) {}
    method isValid (line 21) | bool isValid() const {
    method type (line 25) | int type() const {
    method id (line 30) | std::string id() const {
    method name (line 35) | std::string name() const {
    method setName (line 40) | void setName(std::string name) {
    method opacity (line 44) | double opacity() const {
    method setOpacity (line 49) | void setOpacity(double opacity) {
  class VectorLayer (line 57) | class VectorLayer : public MapLayer {
    method VectorLayer (line 59) | VectorLayer() : MapLayer(nullptr) {}
    method VectorLayer (line 60) | VectorLayer(QgsVectorLayer *layer) : MapLayer(layer) {}
    method subsetString (line 62) | std::string subsetString() const {
    method setSubsetString (line 67) | bool setSubsetString(std::string subset) {
    method QgsVectorLayer (line 73) | QgsVectorLayer *asVectorLayer() const {
  function wrapLayer (line 79) | inline emscripten::val wrapLayer(QgsMapLayer *layer) {
  function EMSCRIPTEN_BINDINGS (line 86) | EMSCRIPTEN_BINDINGS(QgsMapLayer) {

FILE: src/model/QgsMapLayer.ts
  type LayerType (line 1) | enum LayerType {
  type QgsMapLayer (line 13) | interface QgsMapLayer {
  type QgsVectorLayer (line 22) | interface QgsVectorLayer extends QgsMapLayer {

FILE: src/model/QgsMapRendererJob.hpp
  function QgsMapRendererJob_finished (line 7) | static void QgsMapRendererJob_finished(QgsMapRendererJob &job, emscripte...
  function EMSCRIPTEN_BINDINGS (line 11) | EMSCRIPTEN_BINDINGS(QgsMapRendererJob) {

FILE: src/model/QgsMapRendererJob.ts
  type QgsMapRendererJob (line 6) | interface QgsMapRendererJob {

FILE: src/model/QgsMapRendererParallelJob.hpp
  function EMSCRIPTEN_BINDINGS (line 9) | EMSCRIPTEN_BINDINGS(QgsMapRendererParallelJob) {

FILE: src/model/QgsMapRendererParallelJob.ts
  type QgsMapRendererParallelJob (line 8) | interface QgsMapRendererParallelJob extends QgsMapRendererQImageJob {}

FILE: src/model/QgsMapRendererQImageJob.hpp
  function QgsMapRendererQImageJob_renderedImage (line 9) | static emscripten::val QgsMapRendererQImageJob_renderedImage(QgsMapRende...
  function EMSCRIPTEN_BINDINGS (line 15) | EMSCRIPTEN_BINDINGS(QgsMapRendererQImageJob) {

FILE: src/model/QgsMapRendererQImageJob.ts
  type QgsMapRendererQImageJob (line 8) | interface QgsMapRendererQImageJob extends QgsMapRendererJob {

FILE: src/model/QgsPointXY.hpp
  function EMSCRIPTEN_BINDINGS (line 6) | EMSCRIPTEN_BINDINGS(QgsPointXY) {

FILE: src/model/QgsPointXY.ts
  type QgsPointXY (line 6) | interface QgsPointXY {
  type QgsPointXYConstructors (line 14) | interface QgsPointXYConstructors {

FILE: src/model/QgsRectangle.hpp
  function QgsRectangle_scale (line 6) | static void QgsRectangle_scale(QgsRectangle &r, double f) {
  function QgsRectangle_move (line 10) | static void QgsRectangle_move(QgsRectangle &r, double dx, double dy) {
  function EMSCRIPTEN_BINDINGS (line 14) | EMSCRIPTEN_BINDINGS(QgsRectangle) {

FILE: src/model/QgsRectangle.ts
  type QgsRectangle (line 8) | interface QgsRectangle {
  type QgsRectangleConstructors (line 22) | interface QgsRectangleConstructors {

FILE: src/qgis-js.cpp
  function main (line 37) | int main(int argc, char *argv[]) {
Condensed preview — 133 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (385K chars).
[
  {
    "path": ".clang-format",
    "chars": 983,
    "preview": "#see https://clang.llvm.org/docs/ClangFormatStyleOptions.html\nLanguage: Cpp\nColumnLimit: 100\nContinuationIndentWidth: 2\n"
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 2727,
    "preview": "name: Build\n\non:\n  workflow_dispatch\n  # push:\n  #   branches:\n  #     - main\n  # pull_request:\n  #   branches:\n  #     "
  },
  {
    "path": ".gitignore",
    "chars": 125,
    "preview": "package-lock.json\nCMakeLists.txt.user\nbuild/wasm\nnode_modules\ndist\npackages/*/dist\npackages/*/etc\nbin/act\ndocs/api\n.DS_S"
  },
  {
    "path": ".gitmodules",
    "chars": 190,
    "preview": "[submodule \"build/vcpkg\"]\n\tpath = build/vcpkg\n\turl = https://github.com/microsoft/vcpkg.git\n[submodule \"build/emsdk\"]\n\tp"
  },
  {
    "path": ".nvmrc",
    "chars": 9,
    "preview": "v22.16.0\n"
  },
  {
    "path": ".prettierignore",
    "chars": 154,
    "preview": "public\ndist\nnode_modules\nbuild/wasm\nbuild/emsdk\nbuild/vcpkg\nbuild/vcpkg-ports\nbuild/vcpkg-toolchains\nbuild/vcpkg-triplet"
  },
  {
    "path": ".prettierrc.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": ".vscode/c_cpp_properties.json",
    "chars": 535,
    "preview": "{\n  \"configurations\": [\n    {\n      \"name\": \"Linux\",\n      \"includePath\": [\n        \"${workspaceFolder}/build/wasm/vcpkg"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1952,
    "preview": "{\n  \"workbench.editor.enablePreview\": false,\n  \"editor.tabSize\": 2,\n  \"editor.formatOnSave\": true,\n  \"files.insertFinalN"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1992,
    "preview": "This document describes changes between tagged qgis-js versions\n\n## 4.1.0 (in development)\n\n- Replaced `mapLayers()` wit"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 6340,
    "preview": "cmake_minimum_required(VERSION 3.15)\n\nproject(qgis-js CXX)\n\nset(CMAKE_CXX_STANDARD 17)\n\nfind_package(unofficial-sqlite3 "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1544,
    "preview": "# Contributing to qgis-js\n\nThank you for considering contributing to qgis-js! We welcome all contributions, big or small"
  },
  {
    "path": "LICENSE",
    "chars": 18801,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
  },
  {
    "path": "README.md",
    "chars": 28337,
    "preview": "# qgis-js\n\n**QGIS core ported to WebAssembly to run it on the web platform**\n\nVersion: `4.0.0` (based on QGIS 4.0.0)\n\n[q"
  },
  {
    "path": "build/actions/clean.ts",
    "chars": 1049,
    "preview": "import { CommandLineAction } from \"@rushstack/ts-command-line\";\n\nimport { QgisJsOptions } from \"./lib/QgisJsOptions\";\n\ni"
  },
  {
    "path": "build/actions/compile.ts",
    "chars": 4215,
    "preview": "import { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\n\nimport {\n  CommandLineAction,\n  CommandLineC"
  },
  {
    "path": "build/actions/install.ts",
    "chars": 1809,
    "preview": "import { CommandLineAction } from \"@rushstack/ts-command-line\";\n\nimport { QgisJsOptions } from \"./lib/QgisJsOptions\";\n\ni"
  },
  {
    "path": "build/actions/lib/BuildType.ts",
    "chars": 53,
    "preview": "export type BuildType = \"Dev\" | \"Release\" | \"Debug\";\n"
  },
  {
    "path": "build/actions/lib/QgisJsOptions.ts",
    "chars": 55,
    "preview": "export interface QgisJsOptions {\n  verbose: boolean;\n}\n"
  },
  {
    "path": "build/actions/libs.ts",
    "chars": 4806,
    "preview": "import {\n  CommandLineAction,\n  CommandLineChoiceParameter,\n} from \"@rushstack/ts-command-line\";\n\nimport \"zx/globals\";\ni"
  },
  {
    "path": "build/actions/size.ts",
    "chars": 5726,
    "preview": "import type { BrotliCompress, Gzip } from \"zlib\";\n\nimport {\n  CommandLineAction,\n  CommandLineChoiceParameter,\n} from \"@"
  },
  {
    "path": "build/scripts/clean.sh",
    "chars": 136,
    "preview": "#!/bin/bash\nset -eo pipefail\n\n(cd build/emsdk; git clean -xfd)\n\n(cd build/vcpkg; git clean -xfd)\n\nrm -rf build/wasm && m"
  },
  {
    "path": "build/scripts/compile.sh",
    "chars": 558,
    "preview": "#!/bin/bash\nset -eo pipefail\n\n# if vcpkg has installed its own cmake, use that, otherwise use the system cmake\n$(find . "
  },
  {
    "path": "build/scripts/install.sh",
    "chars": 362,
    "preview": "#!/bin/bash\nset -eo pipefail\n\ngit submodule update --init build/emsdk\nbuild/emsdk/emsdk install 5.0.2;\nbuild/emsdk/emsdk"
  },
  {
    "path": "build/vcpkg-ports/libspatialindex/portfile.cmake",
    "chars": 758,
    "preview": "vcpkg_from_github(\n    OUT_SOURCE_PATH SOURCE_PATH\n    REPO libspatialindex/libspatialindex\n    REF \"${VERSION}\"\n    SHA"
  },
  {
    "path": "build/vcpkg-ports/libspatialindex/vcpkg.json",
    "chars": 380,
    "preview": "{\n  \"name\": \"libspatialindex\",\n  \"version\": \"2.0.0\",\n  \"description\": \"C++ implementation of R*-tree, an MVR-tree and a "
  },
  {
    "path": "build/vcpkg-ports/qgis/portfile.cmake",
    "chars": 4203,
    "preview": "vcpkg_from_github(\n    OUT_SOURCE_PATH SOURCE_PATH\n    REPO qgis/QGIS\n    REF 8d368f3efd5be7a2321ada8cb5663d8f478f7d0f\n "
  },
  {
    "path": "build/vcpkg-ports/qgis/vcpkg.json",
    "chars": 1460,
    "preview": "{\n  \"name\": \"qgis\",\n  \"version\": \"4.0.0\",\n  \"port-version\": 1,\n  \"homepage\": \"https://qgis.org\",\n  \"description\": \"QGIS "
  },
  {
    "path": "build/vcpkg-toolchains/qgis-js.cmake",
    "chars": 1446,
    "preview": "\nmessage(STATUS \"Using 'qgis-js' toolchain\")\n\n# Setup EMSDK/EMSCRIPTEN_ROOT\nif(NOT DEFINED ENV{EMSDK})\n  get_filename_co"
  },
  {
    "path": "build/vcpkg-triplets/wasm32-emscripten-qt-threads.cmake",
    "chars": 972,
    "preview": "message(STATUS \"Using 'wasm32-emscripten-qt-threads' triplet\")\n\nset(VCPKG_TARGET_ARCHITECTURE wasm32)\nset(VCPKG_CRT_LINK"
  },
  {
    "path": "build/vite/CrossOriginIsolationPlugin.ts",
    "chars": 680,
    "preview": "import type { Plugin } from \"vite\";\n\nexport const CrossOriginIsolationResponseHeaders = {\n  \"Cross-Origin-Opener-Policy\""
  },
  {
    "path": "build/vite/DirectoryListingPlugin.ts",
    "chars": 3781,
    "preview": "import { File, Folder } from \"../../packages/qgis-js-utils\";\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\n\nimpor"
  },
  {
    "path": "build/vite/QgisRuntimePlugin.ts",
    "chars": 4358,
    "preview": "import { join, resolve, basename } from \"path\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname } from \""
  },
  {
    "path": "docs/architecture.md",
    "chars": 76,
    "preview": "# Architecture\n\n## qgis-js Repository\n\n## qgis-js Build\n\n## qgis-js Runtime\n"
  },
  {
    "path": "docs/bundling.md",
    "chars": 2859,
    "preview": "# Bundling\n\nqgis-js consists of two parts: The runtime generated by Emscripten and the TypeScript/JavaScript API, that c"
  },
  {
    "path": "docs/ci.md",
    "chars": 8,
    "preview": "# CI/CD\n"
  },
  {
    "path": "docs/compatibility.md",
    "chars": 1763,
    "preview": "# Compatibility\n\n## Features\n\nqgis-js uses the following features which have to be supported by the JavaScript/WebAssemb"
  },
  {
    "path": "docs/debugging.md",
    "chars": 951,
    "preview": "# Debugging\n\n## Setup\n\n- Make sure that you have compiled qgis-js with the [`Debug` build type](../README.md#build-types"
  },
  {
    "path": "docs/examples/qgis-js-example-api/index.html",
    "chars": 450,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>qgis-js-example-api</title>\n    <link "
  },
  {
    "path": "docs/examples/qgis-js-example-api/main.js",
    "chars": 953,
    "preview": "const QGIS_JS_DIST = window.location.pathname + \"node_modules/qgis-js/dist\";\n\n// loading the qgis-js library\nconst { qgi"
  },
  {
    "path": "docs/examples/qgis-js-example-api/package.json",
    "chars": 231,
    "preview": "{\n  \"name\": \"qgis-js-example-api\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"start\": \"vite preview --ou"
  },
  {
    "path": "docs/examples/qgis-js-example-api/vite.config.js",
    "chars": 217,
    "preview": "import { defineConfig } from \"vite\";\n\nexport default defineConfig({\n  preview: {\n    headers: {\n      \"Cross-Origin-Open"
  },
  {
    "path": "docs/examples/qgis-js-example-ol/index.html",
    "chars": 349,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>qgis-js-example-ol</title>\n    <link r"
  },
  {
    "path": "docs/examples/qgis-js-example-ol/main.js",
    "chars": 2397,
    "preview": "import { qgis } from \"qgis-js\";\n\nimport { QgisCanvasDataSource } from \"@qgis-js/ol\";\n\nimport { Map, View } from \"ol\";\n\ni"
  },
  {
    "path": "docs/examples/qgis-js-example-ol/package.json",
    "chars": 363,
    "preview": "{\n  \"name\": \"qgis-js-example-ol\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"start\": \"vite dev\",\n    \"bu"
  },
  {
    "path": "docs/examples/qgis-js-example-ol/style.css",
    "chars": 170,
    "preview": "@import \"node_modules/ol/ol.css\";\n\nbody {\n  font-family: sans-serif;\n  margin: 1em;\n  padding: 1em;\n}\n#map {\n  width: 80"
  },
  {
    "path": "docs/examples/qgis-js-example-ol/vite.config.js",
    "chars": 817,
    "preview": "import { defineConfig } from \"vite\";\n\nimport { viteStaticCopy } from \"vite-plugin-static-copy\";\n\nconst headers = {\n  \"Cr"
  },
  {
    "path": "docs/performance.md",
    "chars": 8951,
    "preview": "# Performance and Optimization\n\n## Preface\n\nIn Feb-June 2024, we will conduct a performance test to measure the performa"
  },
  {
    "path": "docs/profiling.md",
    "chars": 1862,
    "preview": "# Profiling\n\n## qgis-js Performance Measurement Tool\n\nThe [qgis-js Performance Measurement Tool](../sites/performance/) "
  },
  {
    "path": "package.json",
    "chars": 1802,
    "preview": "{\n  \"name\": \"qgis-js-repo\",\n  \"private\": true,\n  \"version\": \"4.0.0\",\n  \"license\": \"GPL-2.0-or-later\",\n  \"homepage\": \"htt"
  },
  {
    "path": "packages/qgis-js/README.md",
    "chars": 24200,
    "preview": "# qgis-js\n\n**QGIS core ported to WebAssembly to run it on the web platform**\n\nVersion: `4.0.0` (based on QGIS 4.0.0)\n\n[q"
  },
  {
    "path": "packages/qgis-js/package.json",
    "chars": 934,
    "preview": "{\n  \"name\": \"qgis-js\",\n  \"version\": \"4.0.0\",\n  \"description\": \"QGIS core ported to WebAssembly to run it on the web plat"
  },
  {
    "path": "packages/qgis-js/src/QgisApiAdapter.ts",
    "chars": 2992,
    "preview": "import {\n  InternalQgisApi,\n  QgisApi,\n  QgisApiAdapter,\n} from \"../../../src/api/QgisApi\";\nimport { QgsRectangle } from"
  },
  {
    "path": "packages/qgis-js/src/emscripten.ts",
    "chars": 352,
    "preview": "/// <reference types=\"emscripten\" />\n\n/**\n * Extension of a EmscriptenModule that adds additional properties\n */\nexport "
  },
  {
    "path": "packages/qgis-js/src/index.ts",
    "chars": 636,
    "preview": "/**\n * The version of qgis-js.\n */\nexport const QGIS_JS_VERSION: string =\n  //@ts-ignore (will be defined by vite)\n  __Q"
  },
  {
    "path": "packages/qgis-js/src/loader.ts",
    "chars": 3716,
    "preview": "import { getQgisApiProxy } from \"./QgisApiAdapter\";\n\nimport { threadPoolSize } from \"./runtime\";\nimport type {\n  QgisRun"
  },
  {
    "path": "packages/qgis-js/src/runtime.ts",
    "chars": 1460,
    "preview": "import { EmscriptenRuntimeModule, EmscriptenFS } from \"./emscripten\";\n\nimport { QgisApi, InternalQgisApi } from \"../../."
  },
  {
    "path": "packages/qgis-js/tsconfig.json",
    "chars": 61,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/qgis-js/vite.config.ts",
    "chars": 1708,
    "preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\n\nimport QgisRuntimePlugin from \"../../build/vite/Qg"
  },
  {
    "path": "packages/qgis-js-ol/README.md",
    "chars": 2038,
    "preview": "# @qgis-js/ol\n\n**OpenLayers sources for [qgis-js](https://github.com/qgis/qgis-js)**\n\n[qgis-js Repository](https://githu"
  },
  {
    "path": "packages/qgis-js-ol/package.json",
    "chars": 788,
    "preview": "{\n  \"name\": \"@qgis-js/ol\",\n  \"version\": \"4.0.0\",\n  \"description\": \"OpenLayers sources for qgis-js\",\n  \"license\": \"GPL-2."
  },
  {
    "path": "packages/qgis-js-ol/src/QgisCanvasDataSource.ts",
    "chars": 2464,
    "preview": "import type { QgisApi } from \"qgis-js\";\n\nimport ImageSource, { Options } from \"ol/source/Image\";\nimport { getWidth, getH"
  },
  {
    "path": "packages/qgis-js-ol/src/QgisJobDataSource.ts",
    "chars": 7124,
    "preview": "import type { QgisApi, QgsMapRendererJob } from \"qgis-js\";\n\nimport ImageSource, { Options } from \"ol/source/Image\";\nimpo"
  },
  {
    "path": "packages/qgis-js-ol/src/QgisXYZDataSource.ts",
    "chars": 3589,
    "preview": "import type { QgisApi } from \"qgis-js\";\n\nimport XYZ, { Options } from \"ol/source/XYZ\";\n\nimport { createCanvasContext2D }"
  },
  {
    "path": "packages/qgis-js-ol/src/index.ts",
    "chars": 177,
    "preview": "export { QgisCanvasDataSource } from \"./QgisCanvasDataSource\";\nexport { QgisXYZDataSource } from \"./QgisXYZDataSource\";\n"
  },
  {
    "path": "packages/qgis-js-ol/tsconfig.json",
    "chars": 61,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/qgis-js-ol/vite.config.ts",
    "chars": 473,
    "preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\n\nimport dts from \"vite-plugin-dts\";\n\nexport default"
  },
  {
    "path": "packages/qgis-js-utils/README.md",
    "chars": 2494,
    "preview": "# @qgis-js/utils\n\n**Utilities to integrate [qgis-js](https://github.com/qgis/qgis-js) into web applications**\n\n[qgis-js "
  },
  {
    "path": "packages/qgis-js-utils/package.json",
    "chars": 843,
    "preview": "{\n  \"name\": \"@qgis-js/utils\",\n  \"version\": \"4.0.0\",\n  \"description\": \"Utilities to integrate qgis-js into web applicatio"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/FileSystem.ts",
    "chars": 1553,
    "preview": "export type FileSystemEntryType = \"File\" | \"Folder\";\n\nexport interface FileSystemEntry {\n  name: string;\n  path: string;"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/GithubProject.ts",
    "chars": 6413,
    "preview": "import type { EmscriptenFS } from \"qgis-js\";\n\nimport { Project, PROJECTS_UPLOAD_DIR } from \"./Project\";\n\nimport { Folder"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/LocalProject.ts",
    "chars": 1667,
    "preview": "import type { EmscriptenFS } from \"qgis-js\";\n\nimport { Project, PROJECTS_UPLOAD_DIR } from \"./Project\";\n\nimport {\n  dire"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/Project.ts",
    "chars": 1936,
    "preview": "import type { EmscriptenFS } from \"qgis-js\";\n\nexport type ProjectType = \"Remote\" | \"Local\" | \"Github\";\n\nexport const PRO"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/RemoteProject.ts",
    "chars": 1720,
    "preview": "import type { EmscriptenFS } from \"qgis-js\";\n\nimport { Project, PROJECTS_UPLOAD_DIR } from \"./Project\";\n\nimport { Folder"
  },
  {
    "path": "packages/qgis-js-utils/src/fs/index.ts",
    "chars": 4650,
    "preview": "import type { EmscriptenFS } from \"qgis-js\";\n\nimport { Folder } from \"./FileSystem\";\n\nimport { PROJECTS_UPLOAD_DIR, Proj"
  },
  {
    "path": "packages/qgis-js-utils/src/index.ts",
    "chars": 152,
    "preview": "export { useProjects } from \"./fs\";\n\nexport type { Project } from \"./fs/Project\";\nexport type { FileSystemEntry, File, F"
  },
  {
    "path": "packages/qgis-js-utils/tsconfig.json",
    "chars": 61,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/qgis-js-utils/vite.config.ts",
    "chars": 471,
    "preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\n\nimport dts from \"vite-plugin-dts\";\n\nexport default"
  },
  {
    "path": "pnpm-workspace.yaml",
    "chars": 72,
    "preview": "packages:\n  - packages/*\n  - sites/*\nonlyBuiltDependencies:\n  - esbuild\n"
  },
  {
    "path": "qgis-js.ts",
    "chars": 1497,
    "preview": "#!/usr/bin/env -S node_modules/.bin/vite-node --script\n\n/*\n * qgis-js CLI\n *\n * Will help you to build qgis-js and gener"
  },
  {
    "path": "sites/dev/index.html",
    "chars": 9841,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>qgis-js</title>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" h"
  },
  {
    "path": "sites/dev/package.json",
    "chars": 678,
    "preview": "{\n  \"name\": \"@qgis-js/dev\",\n  \"version\": \"4.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite"
  },
  {
    "path": "sites/dev/public/projects/village/buildings.prj",
    "chars": 417,
    "preview": "PROJCS[\"OSGB_1936_British_National_Grid\",GEOGCS[\"GCS_OSGB 1936\",DATUM[\"D_OSGB_1936\",SPHEROID[\"Airy_1830\",6377563.396,299"
  },
  {
    "path": "sites/dev/public/projects/village/buildings.qpj",
    "chars": 690,
    "preview": "PROJCS[\"OSGB 1936 / British National Grid\",GEOGCS[\"OSGB 1936\",DATUM[\"OSGB_1936\",SPHEROID[\"Airy 1830\",6377563.396,299.324"
  },
  {
    "path": "sites/dev/public/projects/village/project.qgs",
    "chars": 37226,
    "preview": "<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>\n<qgis saveUserFull=\"Martin\" saveUser=\"martin\" saveDateTime=\"2"
  },
  {
    "path": "sites/dev/src/demo.css",
    "chars": 11140,
    "preview": "body {\n  font:\n    16px/1.5em \"Overpass\",\n    \"Open Sans\",\n    Helvetica,\n    sans-serif;\n  color: #333;\n}\n\nh1 {\n  color"
  },
  {
    "path": "sites/dev/src/index.ts",
    "chars": 7356,
    "preview": "import { QGIS_JS_VERSION, qgis } from \"qgis-js\";\n\nimport { QgisApi } from \"qgis-js\";\n\nimport { useProjects } from \"@qgis"
  },
  {
    "path": "sites/dev/src/js.ts",
    "chars": 2669,
    "preview": "import { QgisApi } from \"qgis-js\";\n\nimport type { QgsRectangle } from \"qgis-js\";\n\nconst mapScaleFactor = 1.5;\nconst mapM"
  },
  {
    "path": "sites/dev/src/layers.ts",
    "chars": 7790,
    "preview": "import type {\n  QgisApi,\n  QgsLayerTreeNode,\n  QgsLayerTreeLayer,\n  QgsVectorLayer,\n} from \"qgis-js\";\nimport { LayerType"
  },
  {
    "path": "sites/dev/src/ol.ts",
    "chars": 8716,
    "preview": "import { QgisApi } from \"qgis-js\";\n\nimport {\n  QgisJobDataSource,\n  QgisCanvasDataSource,\n  QgisXYZDataSource,\n} from \"@"
  },
  {
    "path": "sites/dev/tsconfig.json",
    "chars": 61,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "sites/dev/vite.config.ts",
    "chars": 1597,
    "preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\n\nimport QgisRuntimePlugin from \"../../build/vite/Qg"
  },
  {
    "path": "sites/performance/.gitignore",
    "chars": 292,
    "preview": "/test-results/\n/playwright-report/\n/blob-report/\n/playwright/.cache/\n/test-results/\n/playwright-report/\n/blob-report/\n/p"
  },
  {
    "path": "sites/performance/index.html",
    "chars": 5592,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>qgis-js-performance</title>\n    <meta charset=\"UTF-8\" />\n    <link "
  },
  {
    "path": "sites/performance/package.json",
    "chars": 502,
    "preview": "{\n  \"name\": \"@qgis-js/performance\",\n  \"version\": \"4.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev"
  },
  {
    "path": "sites/performance/playwright.config.ts",
    "chars": 736,
    "preview": "import { defineConfig, devices } from \"@playwright/test\";\n\nexport default defineConfig({\n  testDir: \"./tests\",\n  retries"
  },
  {
    "path": "sites/performance/report.html",
    "chars": 1194,
    "preview": "<html>\n  <head>\n    <title>Performance Report</title>\n    <style>\n      body {\n        margin: 1em;\n        font-family:"
  },
  {
    "path": "sites/performance/src/index.ts",
    "chars": 10347,
    "preview": "import { qgis, QGIS_JS_VERSION } from \"qgis-js\";\nimport type { QgisApi, EmscriptenFS } from \"qgis-js\";\n\nimport { useProj"
  },
  {
    "path": "sites/performance/tests/performance.spec.ts",
    "chars": 2589,
    "preview": "import { test, expect } from \"@playwright/test\";\n\nimport { join, dirname } from \"path\";\nimport { readdirSync } from \"fs\""
  },
  {
    "path": "sites/performance/tsconfig.json",
    "chars": 61,
    "preview": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "sites/performance/vite.config.ts",
    "chars": 775,
    "preview": "import { defineConfig } from \"vite\";\n\nimport QgisRuntimePlugin from \"../../build/vite/QgisRuntimePlugin\";\nimport Directo"
  },
  {
    "path": "src/api/QgisApi.cpp",
    "chars": 13991,
    "preview": "#include <memory>\n#include <optional>\n#include <string>\n\n#include <qgsexpressioncontextutils.h>\n#include <qgslayerdefini"
  },
  {
    "path": "src/api/QgisApi.ts",
    "chars": 7185,
    "preview": "import {\n  QgisModelConstructors,\n  QgsMapRendererParallelJob,\n  QgsRectangle,\n  QgsLayerTreeGroup,\n} from \"./QgisModel\""
  },
  {
    "path": "src/api/QgisModel.ts",
    "chars": 1351,
    "preview": "import type { QgsPointXY, QgsPointXYConstructors } from \"../model/QgsPointXY\";\nimport type {\n  QgsRectangle,\n  QgsRectan"
  },
  {
    "path": "src/model/QgsLayerTreeGroup.hpp",
    "chars": 2276,
    "preview": "#pragma once\n\n#include <string>\n\n#include <qgslayertreegroup.h>\n\n#include \"./QgsLayerTreeModelLegendNode.hpp\"\n#include \""
  },
  {
    "path": "src/model/QgsLayerTreeGroup.ts",
    "chars": 403,
    "preview": "import type { QgsLayerTreeNode } from \"./QgsLayerTreeNode\";\nimport type { QgsLayerTreeLayer } from \"./QgsLayerTreeLayer\""
  },
  {
    "path": "src/model/QgsLayerTreeLayer.hpp",
    "chars": 2177,
    "preview": "#pragma once\n\n#include <string>\n\n#include <qgslayertree.h>\n#include <qgslayertreelayer.h>\n\n#include \"./QgsLayerTreeGroup"
  },
  {
    "path": "src/model/QgsLayerTreeLayer.ts",
    "chars": 394,
    "preview": "import type { QgsLayerTreeModelLegendNode } from \"./QgsLayerTreeModelLegendNode\";\nimport type { QgsLayerTreeNode } from "
  },
  {
    "path": "src/model/QgsLayerTreeModelLegendNode.hpp",
    "chars": 3212,
    "preview": "#pragma once\n\n#include <cmath>\n#include <string>\n\n#include <qgslayertreemodel.h>\n#include <qgslayertreemodellegendnode.h"
  },
  {
    "path": "src/model/QgsLayerTreeModelLegendNode.ts",
    "chars": 127,
    "preview": "export interface QgsLayerTreeModelLegendNode {\n  isValid(): boolean;\n  label(): string;\n  symbolImage(size: number): str"
  },
  {
    "path": "src/model/QgsLayerTreeNode.hpp",
    "chars": 2529,
    "preview": "#pragma once\n\n#include <string>\n\n#include <qgslayertree.h>\n\n#include <emscripten/bind.h>\n#include <emscripten/val.h>\n\ncl"
  },
  {
    "path": "src/model/QgsLayerTreeNode.ts",
    "chars": 466,
    "preview": "import type { QgsLayerTreeGroup } from \"./QgsLayerTreeGroup\";\nimport type { QgsLayerTreeLayer } from \"./QgsLayerTreeLaye"
  },
  {
    "path": "src/model/QgsMapLayer.hpp",
    "chars": 2482,
    "preview": "#pragma once\n\n#include <string>\n\n#include <qgsmaplayer.h>\n#include <qgsvectorlayer.h>\n\n#include <emscripten/bind.h>\n#inc"
  },
  {
    "path": "src/model/QgsMapLayer.ts",
    "chars": 421,
    "preview": "export enum LayerType {\n  Vector = 0,\n  Raster = 1,\n  Plugin = 2,\n  Mesh = 3,\n  VectorTile = 4,\n  Annotation = 5,\n  Poin"
  },
  {
    "path": "src/model/QgsMapRendererJob.hpp",
    "chars": 685,
    "preview": "#pragma once\n\n#include <qgsmaprendererjob.h>\n\n#include <emscripten/bind.h>\n\nstatic void QgsMapRendererJob_finished(QgsMa"
  },
  {
    "path": "src/model/QgsMapRendererJob.ts",
    "chars": 914,
    "preview": "/**\n * Abstract base class for map rendering implementations\n *\n * {@link https://api.qgis.org/api/classQgsMapRendererJo"
  },
  {
    "path": "src/model/QgsMapRendererParallelJob.hpp",
    "chars": 591,
    "preview": "#pragma once\n\n#include <qgsmaprendererparalleljob.h>\n\n#include <emscripten/bind.h>\n\n#include \"./QgsMapRendererQImageJob."
  },
  {
    "path": "src/model/QgsMapRendererParallelJob.ts",
    "chars": 289,
    "preview": "import { QgsMapRendererQImageJob } from \"./QgsMapRendererQImageJob\";\n\n/**\n * Job implementation that renders all layers "
  },
  {
    "path": "src/model/QgsMapRendererQImageJob.hpp",
    "chars": 672,
    "preview": "#pragma once\n\n#include <qgsmaprendererjob.h>\n\n#include <emscripten/bind.h>\n\n#include \"./QgsMapRendererJob.hpp\"\n\nstatic e"
  },
  {
    "path": "src/model/QgsMapRendererQImageJob.ts",
    "chars": 489,
    "preview": "import { QgsMapRendererJob } from \"./QgsMapRendererJob\";\n\n/**\n * Intermediate base class adding functionality that allow"
  },
  {
    "path": "src/model/QgsPointXY.hpp",
    "chars": 269,
    "preview": "\n#include <qgspointxy.h>\n\n#include <emscripten/bind.h>\n\nEMSCRIPTEN_BINDINGS(QgsPointXY) {\n  emscripten::class_<QgsPointX"
  },
  {
    "path": "src/model/QgsPointXY.ts",
    "chars": 293,
    "preview": "/**\n * A class to represent a 2D point.\n *\n * {@link https://api.qgis.org/api/classQgsPointXY.html}\n */\nexport interface"
  },
  {
    "path": "src/model/QgsRectangle.hpp",
    "chars": 846,
    "preview": "\n#include <qgsrectangle.h>\n\n#include <emscripten/bind.h>\n\nstatic void QgsRectangle_scale(QgsRectangle &r, double f) {\n  "
  },
  {
    "path": "src/model/QgsRectangle.ts",
    "chars": 593,
    "preview": "import { QgsPointXY } from \"./QgsPointXY\";\n\n/**\n * A rectangle specified with double values.\n *\n * {@link https://api.qg"
  },
  {
    "path": "src/qgis-js.cpp",
    "chars": 4876,
    "preview": "\n#include \"cpl_conv.h\"\n#include <expat.h>\n#include <gdal.h>\n#include <geos_c.h>\n#include <proj.h>\n#include <qt6keychain/"
  },
  {
    "path": "src/qt.conf",
    "chars": 17,
    "preview": "[Paths]\nPrefix=/\n"
  },
  {
    "path": "tsconfig.json",
    "chars": 505,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"ESNext\",\n    \"lib\":"
  },
  {
    "path": "vcpkg.json",
    "chars": 1619,
    "preview": "{\n  \"name\": \"qgis-js\",\n  \"version\": \"0.1.0\",\n  \"vcpkg-configuration\": {\n    \"overlay-triplets\": [\"./build/vcpkg-triplets"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the qgis/qgis-js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 133 files (352.6 KB), approximately 92.5k tokens, and a symbol index with 255 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!