4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DuckDB-WASMでGeoParquetのビューワーを作ってみた

Posted at

これは DuckDB Advent Calendar 2025 の21日目の記事です。

概要

GeoParquetをブラウザ上で読み込み、DuckDB-WASMを使って動的にベクトルタイル(MVT)に変換してMapLibre GL JSで表示するビューワーを作成しました。
スクリーンショット 2025-12-21 15.53.02.png

上記のテストデータとして、FlateauのGeoParquetを利用していますので、試される場合はこちらを利用すると間違いないです。

構成

  • データソース: GeoParquetファイル(ローカルファイル または URL)
  • 処理エンジン: DuckDB-WASM
  • 表示: MapLibre GL JS

ポイント

ほぼ以下の記事の内容をそのまま使わせていただいております。
GeoParquet対応にしました、というくらい。

  1. DuckDB-WASMとSpatial拡張のセットアップ
    まずはDuckDBを初期化し、地理空間情報を扱うための spatial 拡張をロードします。

    import * as duckdb from '@duckdb/duckdb-wasm';
    // ... 初期化コード ...
    const conn = await db.connect();
    await conn.query(`INSTALL spatial; LOAD spatial;`);
    
  2. MapLibreのカスタムプロトコル (duckdb://)
    MapLibreには addProtocol という強力な機能があり、独自のURLスキームに対する処理を記述できます。これを利用して、duckdb://table_name/z/x/y というリクエストをインターセプトします。

    maplibregl.addProtocol('duckdb', async (params) => {
      // URLから z, x, y をパース
      const [layer, z, x, y] = parseUrl(params.url);
      
      // SQLを実行してMVTバイナリを取得
      const mvtBuffer = await generateMvt(layer, z, x, y);
      
      return { data: mvtBuffer };
    });
    
  3. SQLによる動的MVT生成
    DuckDBの ST_AsMVTST_AsMVTGeom 関数を使います。

    WITH mvt_data AS (
      SELECT
        ST_AsMVTGeom(
          ST_Transform(geom, 'EPSG:4326', 'EPSG:3857'), -- Webメルカトルへの変換
          ST_TileEnvelope(${z}, ${x}, ${y}),             -- タイル範囲
          4096, 0, true                                  -- バッファ設定など
        ) AS mvt_geom,
        prop1, prop2 -- 属性カラム
      FROM table_name
      WHERE
        -- タイル範囲に含まれるデータのみ抽出(インデックスが効く)
        ST_Intersects(
          ST_Transform(geom, 'EPSG:4326', 'EPSG:3857'),
          ST_TileEnvelope(${z}, ${x}, ${y})
        )
    )
    SELECT ST_AsMVT(mvt_data) FROM mvt_data;
    

躓いたところ

  1. "This geometry seems to be written with a newer version..."
    GeoParquetファイルを作成したライブラリ(GDALやPythonのpyarrow/duckdb等)と、ブラウザ上のDuckDB-WASMのSpatial拡張のバージョンが合わず、このエラーが出ました。
    解決策: @duckdb/duckdb-wasm のバージョンを最新の開発版に上げることで解決しました。今回は 1.33.1-dev4.0 を使用しています。
  2. MVT生成時の型エラー
    DATE 型や DECIMAL 型が含まれているとエラーになりました。
    解決策: SQL構築時にカラムの型をチェックし、MVTがサポートしていない型は明示的にキャストしました。
    -- 例: DATE型の場合は文字列に、DECIMAL型はDOUBLEに変換
    CASE 
      WHEN type = 'DATE' THEN CAST(col AS VARCHAR)
      WHEN type = 'DECIMAL' THEN CAST(col AS DOUBLE)
      ELSE col
    END
    
  3. 空のジオメトリによるエラー
    タイルの境界で切り取られた結果、ジオメトリが空(Empty)やNULLになることがあり、それが ST_AsMVT をクラッシュさせることがありました。
    解決策: ST_AsMVTGeom の後段で WHERE mvt_geom IS NOT NULL および NOT ST_IsEmpty(mvt_geom) のフィルタを入れることで安定しました。

まとめと感想

GeoParquetをクラウドのアクセスできるところに置いておくだけで、ベクトルタイルとしてMapビューワーに表示できるという手軽さが良いなと思いました。
ちなみに、ポリゴンデータだと動的なタイル生成に時間がかかりすぎるかなという印象です。このあたり、おそらく解消法がありそうなのですが、そこまで深く追求できませんでした。
ポリゴンデータじゃなくてポイントデータならもっと速いのかも?とは思いました。
まだできたばかりの機能ですので、今後良くなっていくだろうとは思います。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?