DEV Community

Cover image for Querying Overture Maps GeoParquet Directly in the Browser with DuckDB WASM
Florent Gravin for Camptocamp Geospatial Solutions

Posted on • Edited on

5 1

Querying Overture Maps GeoParquet Directly in the Browser with DuckDB WASM

Overture Maps provides a rich, open and collaborative dataset of geospatial features that’s designed to power everything from routing to visualization. These datasets are distributed in GeoParquet format : cloud-native, efficient, columnar, and increasingly becoming a standard for geospatial data at scale.

But what if you could explore and query these massive datasets directly in your browser - without any server-side processing or backend setup? Thanks to DuckDB-WASM, you can.

In this post, we’ll walk through how to:

  • Load an Overture Maps GeoParquet file into the browser
  • Query it using SQL, right from the front-end
  • Visualize features on a map

Let’s dive in. 🦆


Tools and Technologies

  • DuckDB-WASM: A full DuckDB engine compiled to WebAssembly, enabling in-browser analytics.
  • GeoParquet: Parquet format with additional metadata to support geometry and spatial operations.
  • Overture Maps: A collaborative open map dataset from companies like Meta, Microsoft, Amazon, and TomTom.
  • MapLibre GL JS: For rendering geospatial features.

Step 1: Load DuckDB WASM

Include DuckDB-WASM in your HTML or install it via npm:

npm install @duckdb/duckdb-wasm
Enter fullscreen mode Exit fullscreen mode

And initialize it:

import * as duckdb from '@duckdb/duckdb-wasm'
import duckdb_wasm from '@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm?url'
import mvp_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js?url'
import duckdb_wasm_next from '@duckdb/duckdb-wasm/dist/duckdb-eh.wasm?url'
import eh_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js?url'

const MANUAL_BUNDLES = {
  mvp: {
    mainModule: duckdb_wasm,
    mainWorker: mvp_worker,
  },
  eh: {
    mainModule: duckdb_wasm_next,
    mainWorker: eh_worker,
  },
}

const bundle = await duckdb.selectBundle(MANUAL_BUNDLES)
const worker = new Worker(bundle.mainWorker)
const db = new duckdb.AsyncDuckDB(logger, worker)
await db.instantiate(bundle.mainModule, bundle.pthreadWorker)
Enter fullscreen mode Exit fullscreen mode

Step 2: Fetch and Load the GeoParquet File

Disclaimer : As DuckDB WASM does not support HTTPFS extension you can't directly hint the official Overture Maps GeoParquet releases, which provide root level S3 file system entry.
You should extract a subset of the datasets and host them in your web application, you could use the Python CLI for that.

Then, load the spatial extension to process the GeoParquet, and init your DuckDB connection.

conn.query('INSTALL spatial;LOAD spatial;')
conn.query('INSTALL h3 FROM community;LOAD h3;')
const conn = await db.connect()
Enter fullscreen mode Exit fullscreen mode

Step 3: Query Your Data

Run SQL queries directly in the browser:

const res = await conn.query(`
WITH areas AS (
  SELECT names.primary as name,
         geometry as area_geom
  FROM read_parquet('https://your.parquet.path/division_area.parquet', filename=true, hive_partitioning=1) 
  WHERE subtype = 'locality'
    AND region = 'US-UT'
),
schools AS (SELECT geometry as place_geom
  FROM read_parquet('https://your.parquet.path/places.parquet', filename=true, hive_partitioning=1) 
  WHERE categories.main = 'hotel'
)
SELECT name, ST_AsGeoJSON(area_geom) as geometry,
       CAST(count(place_geom) as INT) as count
FROM areas
       LEFT JOIN schools ON ST_Contains(area_geom, place_geom)
GROUP BY area_geom, name
`)
Enter fullscreen mode Exit fullscreen mode

This query fetches Overture division areas of Utah, and performs a spatial aggregation, it counts every hotel within each locality.


Step 4: Visualize on a Map

Convert geometries to GeoJSON and render them:

const data = {
  type: 'FeatureCollection',
  features: res.toArray().map((d: DuckResponseObject) => {
    const { geometry, ...properties } = d
    return {
      type: 'Feature',
      geometry: JSON.parse(geometry) as Polygon,
      properties,
    }
  }),
}

map.addSource('places-area-sources', {
  type: 'geojson',
  data,
})

map.addLayer({
  id: 'places-area',
  type: 'fill',
  source: 'places-area-sources',
  paint: {
    'fill-color': ['interpolate', ['linear'], ['get', 'count'], ...colorGradient],
    'fill-outline-color': 'grey',
    'fill-opacity': 0.8,
  },
})
Enter fullscreen mode Exit fullscreen mode

Where colorGradient is a computed classification depending on count distribution.

Et voila ! You have a beautiful choropleth map, from Overture Maps datasets, with no backend implied 👏

Hotel distribution per county in Utah, from Overture Maps division areas and places datasets

Heroku

Tired of jumping between terminals, dashboards, and code?

Check out this demo showcasing how tools like Cursor can connect to Heroku through the MCP, letting you trigger actions like deployments, scaling, or provisioning—all without leaving your editor.

Learn More

Top comments (0)

Feature flag article image

Create a feature flag in your IDE in 5 minutes with LaunchDarkly’s MCP server 🏁

How to create, evaluate, and modify flags from within your IDE or AI client using natural language with LaunchDarkly's new MCP server. Follow along with this tutorial for step by step instructions.

Read full post

👋 Kindness is contagious

Explore this practical breakdown on DEV’s open platform, where developers from every background come together to push boundaries. No matter your experience, your viewpoint enriches the conversation.

Dropping a simple “thank you” or question in the comments goes a long way in supporting authors—your feedback helps ideas evolve.

At DEV, shared discovery drives progress and builds lasting bonds. If this post resonated, a quick nod of appreciation can make all the difference.

Okay