{"id":50411551,"url":"https://github.com/opengeos/maplibre-gl-duckdb","last_synced_at":"2026-05-31T04:01:02.249Z","repository":{"id":361512978,"uuid":"1254742855","full_name":"opengeos/maplibre-gl-duckdb","owner":"opengeos","description":"A MapLibre GL JS plugin for query and visualizing geospatial data in a DuckDB database","archived":false,"fork":false,"pushed_at":"2026-05-31T02:23:48.000Z","size":24,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T02:25:30.979Z","etag":null,"topics":["duckdb","duckdb-spatial","geospatial","maplibre","maplibre-gl-js","maplibre-plugins"],"latest_commit_sha":null,"homepage":"http://opengeos.org/maplibre-gl-duckdb","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opengeos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-31T00:24:56.000Z","updated_at":"2026-05-31T02:20:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/opengeos/maplibre-gl-duckdb","commit_stats":null,"previous_names":["opengeos/maplibre-gl-duckdb"],"tags_count":1,"template":false,"template_full_name":"opengeos/maplibre-gl-plugin-template","purl":"pkg:github/opengeos/maplibre-gl-duckdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opengeos%2Fmaplibre-gl-duckdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opengeos%2Fmaplibre-gl-duckdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opengeos%2Fmaplibre-gl-duckdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opengeos%2Fmaplibre-gl-duckdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opengeos","download_url":"https://codeload.github.com/opengeos/maplibre-gl-duckdb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opengeos%2Fmaplibre-gl-duckdb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33718446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["duckdb","duckdb-spatial","geospatial","maplibre","maplibre-gl-js","maplibre-plugins"],"created_at":"2026-05-31T04:01:01.353Z","updated_at":"2026-05-31T04:01:02.238Z","avatar_url":"https://github.com/opengeos.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MapLibre GL DuckDB\n\nA MapLibre GL JS control for querying and visualizing geospatial data from DuckDB databases in the browser.\n\n[![npm version](https://img.shields.io/npm/v/maplibre-gl-duckdb.svg)](https://www.npmjs.com/package/maplibre-gl-duckdb)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?logo=codesandbox)](https://codesandbox.io/p/github/opengeos/maplibre-gl-duckdb)\n[![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?logo=stackblitz)](https://stackblitz.com/github/opengeos/maplibre-gl-duckdb)\n\n## Features\n\n- Browser DuckDB WASM runtime with the spatial extension.\n- Local `.duckdb` and `.db` database loading.\n- CORS-enabled remote database URL loading.\n- Table selector populated from attached DuckDB databases.\n- SQL query panel inside a collapsible MapLibre control.\n- Rendering for query results with `GEOMETRY`, WKB, or WKT columns.\n- deck.gl and GeoArrow rendering for points, lines, and polygons.\n- React wrapper and state hook.\n- Vite library build with ESM and CommonJS outputs.\n\n## Installation\n\n```bash\nnpm install maplibre-gl-duckdb\n```\n\n## Quick Start\n\n```typescript\nimport maplibregl from 'maplibre-gl';\nimport { DuckDBControl } from 'maplibre-gl-duckdb';\nimport 'maplibre-gl-duckdb/style.css';\nimport 'maplibre-gl/dist/maplibre-gl.css';\n\nconst map = new maplibregl.Map({\n  container: 'map',\n  style: 'https://tiles.openfreemap.org/styles/positron',\n  center: [0, 20],\n  zoom: 2,\n});\n\nmap.on('load', () =\u003e {\n  const control = new DuckDBControl({\n    title: 'DuckDB',\n    collapsed: false,\n    databaseUrl: 'https://data.source.coop/giswqs/opengeos/nyc_data.db',\n    sampleDatabaseUrl: 'https://data.source.coop/giswqs/opengeos/nyc_data.db',\n    initialQuery: `SELECT BORONAME, NAME, ST_Transform(geom, 'EPSG:32618', 'EPSG:4326', true) AS geom\nFROM data.main.nyc_neighborhoods\nLIMIT 1000`,\n    geometryColumn: 'geom',\n    sourceCrs: 'EPSG:32618',\n    geometryFormat: 'auto',\n  });\n\n  map.addControl(control, 'top-right');\n});\n```\n\nThe attached database is available through the schema alias `data`. For example, the local sample database can be queried as `data.main.nyc_neighborhoods`.\nAfter a database is loaded, use the table selector to populate the SQL editor for any discovered table, then run or edit the query.\nThe geometry column control is populated from the selected table's columns and defaults to `geom` or `geometry` when either name exists.\n\n## Rendering Pipeline\n\nThe control renders query results directly in the browser. It does not generate vector tiles and does not convert results to GeoJSON.\n\nThe pipeline is:\n\n```text\nDuckDB SQL -\u003e Arrow result -\u003e WKB geometry column -\u003e GeoArrow -\u003e deck.gl layers over MapLibre\n```\n\nDuckDB WASM executes the SQL and returns an Arrow table. The selected geometry column is converted to WKB with DuckDB spatial functions, then the WKB values are converted to GeoArrow tables. Those GeoArrow tables are rendered with deck.gl layers through `MapboxOverlay` on top of the MapLibre map.\n\n## React\n\n```tsx\nimport { useEffect, useRef, useState } from 'react';\nimport maplibregl, { Map } from 'maplibre-gl';\nimport { DuckDBControlReact, useDuckDBState } from 'maplibre-gl-duckdb/react';\nimport 'maplibre-gl-duckdb/style.css';\n\nfunction App() {\n  const mapContainer = useRef\u003cHTMLDivElement\u003e(null);\n  const [map, setMap] = useState\u003cMap | null\u003e(null);\n  const { state } = useDuckDBState({ collapsed: false });\n\n  useEffect(() =\u003e {\n    if (!mapContainer.current) return;\n    const mapInstance = new maplibregl.Map({\n      container: mapContainer.current,\n      style: 'https://tiles.openfreemap.org/styles/positron',\n      center: [0, 20],\n      zoom: 2,\n    });\n    mapInstance.on('load', () =\u003e setMap(mapInstance));\n    return () =\u003e mapInstance.remove();\n  }, []);\n\n  return (\n    \u003cdiv style={{ width: '100%', height: '100vh' }}\u003e\n      \u003cdiv ref={mapContainer} style={{ width: '100%', height: '100%' }} /\u003e\n      {map \u0026\u0026 (\n        \u003cDuckDBControlReact\n          map={map}\n          collapsed={state.collapsed}\n          databaseUrl=\"https://data.source.coop/giswqs/opengeos/nyc_data.db\"\n          sampleDatabaseUrl=\"https://data.source.coop/giswqs/opengeos/nyc_data.db\"\n          initialQuery=\"SELECT BORONAME, NAME, ST_Transform(geom, 'EPSG:32618', 'EPSG:4326', true) AS geom FROM data.main.nyc_neighborhoods LIMIT 1000\"\n          geometryColumn=\"geom\"\n          sourceCrs=\"EPSG:32618\"\n          onQuery={(newState) =\u003e console.log(newState)}\n        /\u003e\n      )}\n    \u003c/div\u003e\n  );\n}\n```\n\n## API\n\n### DuckDBControl Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `collapsed` | `boolean` | `true` | Whether the panel starts collapsed |\n| `position` | `string` | `'top-right'` | MapLibre control position |\n| `title` | `string` | `'DuckDB'` | Panel title |\n| `panelWidth` | `number` | `360` | Panel width in pixels |\n| `databaseUrl` | `string` | | CORS-enabled remote DuckDB database URL |\n| `sampleDatabaseUrl` | `string` | | URL shown in the input without auto-loading |\n| `initialQuery` | `string` | sample query | SQL query to run after loading a database |\n| `geometryColumn` | `string` | auto-detected | Geometry, WKB, or WKT column |\n| `geometryFormat` | `'auto' \\| 'geometry' \\| 'wkb' \\| 'wkt'` | `'auto'` | Geometry conversion mode |\n| `sourceCrs` | `string` | | Source CRS for generated table queries |\n| `targetCrs` | `string` | `'EPSG:4326'` | Target CRS for generated table queries |\n| `pageSize` | `number` | `10000` | Maximum rendered rows |\n| `fitBoundsOnLoad` | `boolean` | `true` | Fit the map to rendered results |\n| `allowLocalFiles` | `boolean` | `true` | Enable local database file input |\n| `allowRemoteUrls` | `boolean` | `true` | Enable remote URL input |\n| `pickable` | `boolean` | `true` | Enable feature click popups |\n| `layerName` | `string` | `'DuckDB query'` | Rendered layer name |\n| `beforeId` | `string` | | Map layer id for insertion order |\n| `interleaved` | `boolean` | `true` | deck.gl MapboxOverlay interleaving |\n\n### Methods\n\n- `loadUrl(url)` loads a remote database URL.\n- `loadFile(file)` loads a local browser `File`.\n- `executeQuery(sql?)` runs SQL and renders geospatial results.\n- `clear()` removes the database, rendered layer, and selection.\n- `setPickable(pickable)` enables or disables feature selection.\n- `toggle()`, `expand()`, and `collapse()` control the panel.\n- `getState()` returns the current `DuckDBState`.\n- `on(event, handler)` and `off(event, handler)` manage events.\n\n### Events\n\n`collapse`, `expand`, `statechange`, `loadstart`, `progress`, `load`, `query`, `error`, and `select`.\n\n## DuckDB Runtime Configuration\n\nBy default, DuckDB WASM is loaded from jsDelivr and the spatial extension is loaded from the official DuckDB extension repository. Use `configureDuckDB` before creating the first control to self-host runtime assets.\n\n```typescript\nimport { configureDuckDB } from 'maplibre-gl-duckdb';\n\nconfigureDuckDB({\n  extensionRepository: 'https://example.com/duckdb-extensions',\n});\n```\n\n## Development\n\n```bash\nnpm install\nnpm run dev\nnpm test\nnpm run build\nnpm run build:examples\n```\n\nThe examples use `https://data.source.coop/giswqs/opengeos/nyc_data.db` as the default DuckDB URL and `https://tiles.openfreemap.org/styles/positron` as the basemap. The sample tables use projected coordinates, so the examples transform `geom` from `EPSG:32618` to `EPSG:4326` before rendering.\n\n## Docker\n\n```bash\ndocker build -t maplibre-gl-duckdb .\ndocker run -p 8080:80 maplibre-gl-duckdb\n```\n\nOpen `http://localhost:8080/maplibre-gl-duckdb/`.\n\n## Notes\n\nRemote database loading requires browser-accessible URLs with CORS enabled. Large databases and broad queries may be limited by browser memory and DuckDB WASM file access constraints.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopengeos%2Fmaplibre-gl-duckdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopengeos%2Fmaplibre-gl-duckdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopengeos%2Fmaplibre-gl-duckdb/lists"}