DuckDB-Wasm + DuckDB-Spatial = Webブラウザ上で完結する地理空間情報分析
本日のデモ:
本日のソースコード:
おっと、待たせたねぇ!今日は「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のドキュメント:
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のドキュメント:
DuckDB-SpatialのFunctionのドキュメント:
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
ディレクトリ内に置いてくれ。
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