🦆

DuckDB-Wasm + DuckDB-Spatial = Webブラウザ上で完結する地理空間情報分析

2024/10/13に公開

本日のデモ:
https://yuiseki.github.io/study-duckdb-wasm-spatial/#

Image from Gyazo

本日のソースコード:
https://github.com/yuiseki/study-duckdb-wasm-spatial

おっと、待たせたねぇ!今日は「DuckDB-Wasm」と「DuckDB-Spatial」についてご案内いたしやすよ!

この記事を読めば、ブラウザだけで地理空間情報の分析をサクッとこなせすための環境構築や、便利な関数を使った地理空間情報分析の基本がバッチリわかるって寸法よ!これを知っときゃ、サーバーいらずで本格的な地理空間情報分析ができちまうし、つまずきやすいポイントもスッキリ解消!あんたもいますぐ地理空間情報分析の達人になれるこたぁ間違いなしだ!さぁ、江戸っ子の心意気で、いますぐその手で試してみな!

Vite.js + React

でやんでぇ!まず、Vite.js + React + TypeScriptを開発環境の基板とするってのは、問答無用で決まりだ!ほかにゃ選択肢はねぇってわけよ。Viteの速さ、Reactの使いやすさと豊富なライブラリのエコシステム、TypeScriptの堅牢さ、全部揃えて最強の組み合わせってもんだ。無駄な時間なんざ使ってんじゃねぇ、これでさっさと始めるのが正解だぜ!

npm create vite@latest
  • Project name: study-duckdb-wasm-spatial
  • Select a framework: React
  • Select a variant: Typescript

DuckDB-Wasm

さて、ここからが本題だが、「DuckDB-Wasm」ってぇのは、まさに粋なやつだ!Webブラウザ上でバッチリ動くデータベースエンジンで、何がいいってサーバーなんぞ使わなくたって、ブラウザだけで大規模データをガツンと解析できるってんだから驚きだねぇ。ブラウザ上でSQLクエリをバリバリと実行できる上に、リモートのデータにもアクセスできるってんだから、これがまた便利だってんだ。

DuckDB-Wasmのドキュメント:
https://duckdb.org/docs/api/wasm/overview.html

DuckDB-Wasmのソースコード:
https://github.com/duckdb/duckdb-wasm

DuckDB-Wasm ハマりポイント

まず基本的に、DuckDBの公式ドキュメントに従ってりゃ大体はOKだ。ま、そりゃそうだ、ちゃんと書いてあんだからな。でも、「DuckDB-Wasm」とTypescriptの組み合わせに限っては、ちょっとクセがある部分があるから気をつけろって話よ。

一つ目のハマりポイントは「duckdb.AsyncDuckDBConnection.query」の返り値の型だ。こいつはな、apache-arrow.Table型で返ってくるんだよ。つまり、ただの配列か何かだとか思ってると、あれ?ってなっちまうんだ。apache-arrowの型だってことを覚えとけよ。

だから、環境構築の時点で、npm iでインストールする時に npm i @duckdb/duckdb-wasm apache-arrow ってコマンドを使ったほうが無難だぜ。どうして @duckdb/duckdb-wasm の依存関係として apache-arrow が一緒にインストールされないのか理解に苦しむくらいだが、頑なにTypescriptを使いたくない人への配慮とかかもな。とにかく、DuckDB-WasmとApache-Arrowを一緒にインストールしときゃ、変なトラブルに巻き込まれずにすむってわけさ​。

基本的な使い方はこうだ:

npm i @duckdb/duckdb-wasm apache-arrow
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_eh from "@duckdb/duckdb-wasm/dist/duckdb-eh.wasm?url";
import eh_worker from "@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js?url";

import type { Table, StructRowProxy } from "apache-arrow";

const MANUAL_BUNDLES: duckdb.DuckDBBundles = {
  mvp: {
    mainModule: duckdb_wasm,
    mainWorker: mvp_worker,
  },
  eh: {
    mainModule: duckdb_wasm_eh,
    mainWorker: eh_worker,
  },
};

const initDuckDB = async (
  setMyDuckDB: React.Dispatch<React.SetStateAction<duckdb.AsyncDuckDB | null>>
) => {
  // Select a bundle based on browser checks
  const bundle = await duckdb.selectBundle(MANUAL_BUNDLES);
  // Instantiate the asynchronous version of DuckDB-wasm
  const worker = new Worker(bundle.mainWorker!);
  const logger = new duckdb.ConsoleLogger();
  const db = new duckdb.AsyncDuckDB(logger, worker);
  await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
  setMyDuckDB(db);
};

DuckDB-Wasmの初期化は、上記の initDuckDB ような関数として切り出しておいて、Reactのルートコンポーネントが読み込まれたタイミングでuseEffectで実行するのがいい。何回も実行するもんじゃあないし、将来的にDuckDB-Wasmの仕様に変更があったとしても、ここだけ変更すればいいと信じたいよな。

DuckDB-Spatial

んで、お次は「DuckDB-Spatial」ってやつだが、これがまた風変わりな代物でさ、地理空間データを扱うDuckDBの達人級の拡張機能ってわけさ。ジオメトリ型のデータだとか、点、線、ポリゴンなんてぇデータもお手のもの。しかも、GDALだのPROJだのって、ややこしい座標系の変換までバッチリ対応してくれんだ。

DuckDB-Spatialのドキュメント:
https://duckdb.org/docs/extensions/spatial/overview.html

DuckDB-SpatialのFunctionのドキュメント:
https://duckdb.org/docs/extensions/spatial/functions

DuckDB-Spatialのソースコード:
https://github.com/duckdb/duckdb_spatial

DuckDB-Wasm上でDuckDB-Spatialを有効にしてみよう

ブラウザ上でDuckDB-Spatialを使うためには、DuckDB-Wasm上でDuckDB-Spatialを有効にしないとならねぇ。
難しいと思うかもしれないが、実は、たったのこれだけなんだ。てやんでぇ!

  const conn = await db.connect();
  await conn.query(`
    INSTALL json;
    INSTALL spatial;
  `);

気をつけてほしいのは、 INSTALL spatial; は一回だけでいいが、 LOAD spatial; はコネクションを初期化するたびに毎回忘れずに必要だってところだ。

ST_Readを使って、GeoGSONを読み込んでテーブルを作成してみよう

まず、「ST_Read」ってのがすげぇ便利なんだ。こいつを使えば、GeoJSON形式のデータをまるっとDuckDB-Wasmにテーブルとして読み込むことができるんだよ!たったのこれだけのコードでGeoGSONがSQLで扱えるテーブルになるっていうんだから、やるじゃねぇか。

LOAD json;
LOAD spatial;
CREATE TABLE countries AS SELECT * FROM ST_Read('http://localhost:5173/ne_110m_admin_0_countries.json');

これで、 countries というSQLテーブルが、ブラウザ上にできちまうんだ。本当だ。

ne_110m_admin_0_countries.json は、以下のURLからダウンロードできるから、 Vite.js で作ったプロジェクトの public ディレクトリ内に置いてくれ。

https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_110m_admin_0_countries.geojson

countriesを様々なSpatial Functionsで分析してみよう

次に、「ST_Area」だとか「ST_AsGeoJSON」なんかの関数もバッチリ使えるんだ。たとえば「ST_Area」を使えば、ジオメトリの面積をさくっと計算できるし、「ST_AsGeoJSON」を使えば、ジオメトリをGeoJSON形式にパッと変換してくれるんだ。こいつぁ、地図データのやりとりなんかには最高だぜ!​

ST_Areaを使って、countriesの面積順でソートしてみよう

LOAD json;
LOAD spatial;
SELECT name as name, ST_Area(geom) as area FROM countries ORDER BY area DESC LIMIT 5;

ST_AsGeoJSONを使って、結果をGeoGSONとして出力してみよう

LOAD json;
LOAD spatial;
SELECT name as name, ST_AsGeoJSON(geom) as geom FROM countries WHERE name = 'Japan';

DuckDB-Spatial まとめ

おい、そこのPostGIS使ってる連中!「そんなことやれて当たり前じゃねぇか」なんて思うかもしれねぇが、ちょっとまて。PostGISでやれていたことが、全部ブラウザ上だけやれちまうってのが、こいつのすげぇところなんだよ!データベースサーバーだのAPIだの、そんな面倒なセットアップはもう要らねぇ。ブラウザ一つで地理空間情報をガツンと扱えて、しかも速ぇ!これが新時代の地理空間情報分析ってもんだ!てやんでぇ、信じられねぇなら一度試してみな!

もちろん、他にも、PostGISを使ってる連中にならお馴染みの「ST_Transform」や「ST_Distance」、「ST_Contain」なんて関数も揃ってやがる。これを使えば、座標系の変換だとか、ジオメトリ間の距離の計算だとか、ジオメトリAがジオメトリBを含むかどうかなんていう、ややこしい地理空間情報に関する処理もサクサクいけるってもんよ。もうこれで、地理空間情報分析はお手のものだ​。

DuckDB-Wasm + DuckDB-Spatial

というわけで、この「DuckDB-Wasm」と「DuckDB-Spatial」を掛け合わせれば、地理空間情報の分析がまさにブラウザ内で完結するって寸法よ!これならもうサーバーなんぞいらねぇ、ブラウザだけでズバッとデータの解析からビジュアライゼーションまで、手早く済ませちまえるんだ。

まとめて言うと、「DuckDB-Wasm」+「DuckDB-Spatial」は、ブラウザで直接地理空間情報を扱うには最適だし、使い勝手が良くて江戸っ子も大満足のデータベースだ。これが未来の地理空間情報分析だぜ!

どうだい、このDuckDBの粋っぷり、てやんでぇ!

この令和の時代、江戸っ子なら、地理空間情報の分析なんてぇのはブラウザ上でパパッと終わらせちまうもんだ!データの解析はサクッとやって、さぁ、その後は何をするかって?蕎麦だ、蕎麦!江戸っ子たるもん、仕事を早く片付けて、ツルッと旨い蕎麦をすするのが粋ってもんよ!地理空間情報なんぞ、ブラウザ上でちゃっちゃと分析して、とっとと蕎麦を食いに行け!これが江戸っ子の粋な生き方ってわけさ!

Discussion