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

> Learn CRS-aware reprojection with DuckDB-WASM in GeoLibre. Effortlessly transform GeoJSON using ST_Transform and PROJ for accurate spatial data in your browser.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-22

---

**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`](https://github.com/opengeos/GeoLibre/blob/main/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:

```typescript
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:

```typescript
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:

```typescript
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:

- **[`apps/geolibre-desktop/src/lib/duckdb-vector-loader.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/duckdb-vector-loader.ts)** – Core implementation containing `reprojectFeatureCollectionToWgs84`, `sourceCrsFromGeoJson`, and database helpers.
- **[`apps/geolibre-desktop/src/lib/crs-utils.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/crs-utils.ts)** – Utilities including `isGeographicCrs` for identifying WGS84-type coordinate systems.
- **[`apps/geolibre-desktop/src/lib/duckdb-geometry.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/duckdb-geometry.ts)** – Generates the `geometryGeoJsonSql` fragment used for `ST_Transform` injection.
- **[`apps/geolibre-desktop/src/lib/spatial-extension-config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/spatial-extension-config.ts)** – Configures custom paths for the DuckDB Spatial extension via `VITE_DUCKDB_SPATIAL_EXTENSION_PATH`.
- **[`apps/geolibre-desktop/src/lib/duckdb-wasm-bundles.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/duckdb-wasm-bundles.ts)** – Manages DuckDB-WASM bundle selection and worker creation.

## 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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/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.