How to Perform CRS-Aware Reprojection with DuckDB-WASM in GeoLibre

GeoLibre performs browser-based CRS-aware reprojection using the DuckDB-WASM Spatial extension (PROJ) through the reprojectFeatureCollectionToWgs84 helper, which detects source CRS metadata, executes SQL transforms via ST_Transform, and returns EPSG:4326-compliant GeoJSON.

GeoLibre is an open-source desktop GIS application that processes geospatial data entirely in the browser using DuckDB-WASM. When loading vector data that carries a non-WGS84 coordinate reference system (CRS), the library automatically reprojects geometries to EPSG:4326 using PROJ-backed spatial functions. This article explains the internal mechanics and implementation patterns for CRS-aware reprojection with DuckDB-WASM in GeoLibre based on the actual source code.

How CRS Detection and Reprojection Works

The reprojection workflow centers on the reprojectFeatureCollectionToWgs84 function in apps/geolibre-desktop/src/lib/duckdb-vector-loader.ts. This utility orchestrates CRS detection, database initialization, and SQL-based transformation to WGS84.

Detecting the Source CRS from GeoJSON

GeoLibre first inspects input GeoJSON for legacy CRS metadata. The sourceCrsFromGeoJson function (lines 904-913) parses any top-level crs member and returns an AUTHORITY:CODE string such as EPSG:3857. If the data is already geographic (EPSG:4326 or CRS84), the function returns null to skip unnecessary reprojection.

Initializing the DuckDB-WASM Instance

Before executing transformations, getDatabase (lines 58-71) memoizes a shared AsyncDuckDB instance. This helper guarantees that the DuckDB Spatial extension is installed and ready, ensuring PROJ capabilities are available in the WASM environment before any reprojection query runs.

Registering GeoJSON as In-Memory Files

To process GeoJSON through DuckDB's SQL engine, the collection is stringified and registered via db.registerFileText. The helper stripAutoFidColumn removes synthetic OGC_FID columns that would cause duplicate-column errors during the re-read operation (lines 545-557).

Executing ST_Transform for Reprojection

The core reprojection logic uses DuckDB's spatial SQL functions. The query SELECT * FROM ST_Read(<sourceFile>) materializes features as GeoJSON. When a source CRS is detected, the geometryGeoJsonSql helper injects ST_Transform into the SELECT clause, projecting geometries to EPSG:4326 before returning the result (lines 936-978). The function finally strips the obsolete crs member and returns the clean FeatureCollection.

Implementing CRS Reprojection in Your Code

You can trigger reprojection manually or rely on automatic handling during file loading. Both approaches use the same underlying DuckDB-WASM infrastructure.

Reprojecting GeoJSON with Legacy CRS Metadata

Use reprojectFeatureCollectionToWgs84 when working with GeoJSON that contains a crs member:

import { reprojectFeatureCollectionToWgs84 } from "./duckdb-vector-loader";

async function demoReprojectGeoJson(fc: GeoJSON.FeatureCollection) {
  // Parses `crs` member, runs DuckDB-WASM → PROJ, returns EPSG:4326
  const reprojected = await reprojectFeatureCollectionToWgs84(fc);
  console.log("Reprojected FeatureCollection:", reprojected);
}

Loading Vector Files with Automatic Reprojection

For Shapefiles, GeoPackages, or other vector formats, loadDuckDbVectorFile handles CRS detection and reprojection automatically:

import { loadDuckDbVectorFile, type DuckDbVectorFile } from "./duckdb-vector-loader";

async function demoLoadVectorFile(file: DuckDbVectorFile) {
  // Reads via ST_Read, discovers CRS, calls reprojectFeatureCollectionToWgs84 internally
  const collection = await loadDuckDbVectorFile(file, {
    // Optional: force source CRS if file lacks metadata
    // overrideSourceCrs: "EPSG:3857",
  });
  console.log("Loaded & reprojected collection:", collection);
}

Explicitly Specifying a Source CRS

When input data lacks CRS metadata, provide the source CRS as the second argument:

async function demoExplicitCrs(fc: GeoJSON.FeatureCollection) {
  const sourceCrs = "EPSG:27700"; // British National Grid
  const reprojected = await reprojectFeatureCollectionToWgs84(fc, sourceCrs);
  console.log(reprojected);
}

Key Source Files for CRS Reprojection

Understanding the module structure helps when extending or debugging reprojection behavior:

Summary

  • CRS-aware reprojection with DuckDB-WASM in GeoLibre relies on the DuckDB Spatial extension (PROJ) running entirely in the browser.
  • The reprojectFeatureCollectionToWgs84 function in duckdb-vector-loader.ts orchestrates the detection, transformation, and cleanup workflow.
  • Source CRS detection parses legacy GeoJSON crs members via sourceCrsFromGeoJson or accepts explicit overrides as function arguments.
  • Data is registered as in-memory files using registerFileText, then processed with ST_Read and ST_Transform SQL functions to ensure EPSG:4326 output.
  • All vector loading through loadDuckDbVectorFile automatically invokes this reprojection pipeline when non-WGS84 data is detected.

Frequently Asked Questions

What is the primary function for reprojection in GeoLibre?

The reprojectFeatureCollectionToWgs84 function in apps/geolibre-desktop/src/lib/duckdb-vector-loader.ts serves as the main entry point. It detects the source CRS, initializes the DuckDB-WASM database with the spatial extension, and executes SQL queries with ST_Transform to convert geometries to EPSG:4326.

How does GeoLibre handle legacy GeoJSON CRS metadata?

GeoLibre uses the sourceCrsFromGeoJson helper (lines 904-913) to parse the deprecated crs member found in legacy GeoJSON specifications. If present, it extracts the AUTHORITY:CODE string (e.g., EPSG:3857); if absent or already geographic, it returns null to skip unnecessary transformation.

Can I specify a custom source CRS if the file lacks metadata?

Yes. When calling reprojectFeatureCollectionToWgs84, provide the source CRS as the second argument. Similarly, loadDuckDbVectorFile accepts an overrideSourceCrs option in its configuration object to force a specific projection when file metadata is missing or incorrect.

What spatial extension powers the reprojection?

The reprojection capabilities are provided by the DuckDB Spatial extension, which bundles PROJ for coordinate transformations. According to the GeoLibre source code, the getDatabase helper ensures this extension is loaded into the DuckDB-WASM instance before executing any spatial queries, enabling full client-side CRS transformations without server dependencies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →