RustだけでMapboxGLアプリを作る
この記事はMapbox Advent Calendar 2022 1日目の記事です。
mapbox-gl-js
mapgox-gl-jsはMapboxとOSSコミュニティによって開発されているWebGLをベースにした美しい地図をブラウザ上で表示できるJavaScriptライブラリです。また、Mapboxが提供するWebサービスやMapbox Studioをmapbox-gl-jsに組み合わせれば、地図のデザインからデータ管理まで可能となります。今年の9月17日に行われたツールド東北2022でMapbox有志によって開発されたリアルタイムマップもmapbox-gl-jsをベースにフロントエンドを構築しています。
ツールド東北2022リアルタイムマップ
ツールド東北2022リアルタイムマップについては、本カレンダーの13日目、14日目に詳しく話すので、そちらを参照してみてください。
Rustとは?
Rustは安全性、生産性、効率性に特化したシステムプログラミング言語で、StackOverflowの開発者調査で7年連続で「最も愛される言語」に選ばれています。
また、日本のテック業界においてもZennやQiitaでの記事や事例が増えていることから、着々とユーザー数が増えていることを感じます。
GISの文脈だとGeoRustというOSSコミュニティによって、GDALのPROJのRustバインディングも開発されてきて、Geoな人たちもC++の代替としてRustに注目し始めていると思われます。
mapbox-gl-rs
動機
筆者はMapbox Japanで地図の広告配信システムのバックエンドを日々Rustで書いています。Rustでコードを書き始めてすでに5年経ちますが、Rustは最高の言語だと思います。地図広告配信システムなので地図上でどうなってるか確認したいことが多々ありMapboxGL使いたいな、Rustでできたらもっといいな、と思っていました。とあることをきっかけにwasm-bindgenのドキュメンテーションを見ていると、Rust側からJS側へのBindingが作れそうだったのでやってみました。wasm-bindgenについては後で詳しく説明します。
できたもの
mapbox-gl-js
のRust Bindingなのでmapbox-gl-rs
という名前にしました。クリックするとGithubのページへ遷移します。
mapbox-gl-rs
はRustのクレート(ライブラリ)になっていて、Rustのプロジェクトに組み込むとRustだけで以下のようなMapboxGLを使ったインタラクティブなWebアプリを作ることができます。
RustでWebアプリ?それって何が嬉しいの?🤔と思った方もいるでしょう。はい、そう思った人は正しいです。実はRust/C++でできたソフトウェアをJavaScriptからアクセスできるようにすることは既存資産の再利用や性能面の優位性があるんですが、その逆のJavaScriptで書かれたものをRustから呼び出すことにあまり優位性はありません。
Rustで作ったWebアプリは早いんじゃない?って思いますが現状のWASMランタイム性能ではそうともいえなさそうです。以下はReactとRust製のWebフロントエンドフレームワークYewのベンチマーク結果ですが、レイテンシーもメモリ使用量もReactに負けています。全ベンチマーク結果はこちら。まぁ、現状誰得状況ですが、自分が気持ちよくRustでMapboxGLアプリ作れればいいや、って気持ちで作っていますw 将来的にWASMのエコシステムが育ってきた頃にはRustでWebアプリを書く未来が来るかもしれません。
レイテンシー | メモリ使用量 |
---|---|
仕組み
mapbox-gl-rs
はwasm-bindgenというRustとJavaScriptの双方向のバインディングを可能にするツールキットを使って、Rust側からmapbox-gl-jsのクラスやファンクションへのFFIコードを全てRustで書いています。
mapbox-gl-rsを使ったRustがWASMにコンパイルされる流れ
Rustのプロジェクトからmapbox-gl-rsを使ったプロジェクトをtrunk等のビルドツールでにコンパイルすると、RustのコードはWASMにコンパイルされ、ブラウザ上で実行可能となります。
mapbox-gl-rsでアプリを作ってみる
mapbox-gl-rs
を使ってただMapboxの地図を表示するだけのWebアプリを作ってみます。
準備
- Rustのインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- wasm用のツールチェインのインストール
rustup target add wasm32-unknown-unknown
- trunkのインストール
cargo install --locked trunk
- Mapboxにサインアップしてアカウントページからアクセストークンを発行する
プロジェクト作成
Rustのプロジェクトを作ります。
cargo new simple
mapboxglをCargo.tomlに追加します。WebフロントエンドフレームワークはYewを使います。
[package]
name = "simple"
version = "0.1.0"
edition = "2021"
[dependencies]
yew = "0.19"
mapboxgl = "0.1"
この時点でビルドできるか試してみましょう。エラーが出なければOK。
cargo build
Yew + mapbox-gl-rs
細かく砕いて説明が難しそうだったので、コード全部載せちゃいます。
use mapboxgl::{LngLat, MapFactory, MapOptions};
use std::{cell::RefCell, rc::Rc};
use yew::{prelude::*, use_effect_with_deps, use_mut_ref};
// Mapを使うためのカスタムフック
fn use_map() -> Rc<RefCell<Option<MapFactory>>> {
// Mapを後で更新したいのでuse_mut_refを使う
let map = use_mut_ref(|| Option::<MapFactory>::None);
{
let mut map = map.clone();
use_effect_with_deps(
move |_| {
let mut m = create_map();
|| {}
},
(),
);
}
map
}
#[function_component(App)]
fn app() -> Html {
// use_mapを呼んでMapオブジェクトを作る
// EventListener追加とかしたい場合はここでする
let _map = use_map();
// Yewアプリを実行するためのHTMLコンテナ
// id="map"を指定した要素に地図が表示される
html! {
<div id="map" style="width: 100vw; height: 100vh;"></div>
}
}
/// Mapオブジェクトを作る
pub fn create_map() -> MapFactory {
let opts = MapOptions::new("取得したMAPBOX_TOKENを設定".into(), "map".into())
.center(LngLat::new(139.7647863, 35.6812373))
.zoom(15.0);
MapFactory::new(opts).unwrap()
}
fn main() {
yew::start_app::<App>();
}
Webアプリのhtmlを用意する
mapbox-gl-jsのJavaScriptファイルとCSSを指定します。
<html>
<head>
<meta charset="utf-8" />
<title>App</title>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.css' rel='stylesheet' />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.js"></script>
</head>
<body style="margin: 0; padding: 0">
</html>
ビルド・実行
ビルドに成功するとHTTPサーバーが立ち上がります。http://127.0.0.1:8080/ をブラウザで開くと以下のようなMapboxGLの地図が表示されます🎉
trunk serve
上手くいかなかった場合は、こちらに完全なコードがあるので参考にしてください。
終わりに
RustだけでMapboxGLアプリを作る方法を解説しました。mapbox-gl-rs
はまだ産まれたてのOSSなので、まだ機能、ドキュメンテーション、サンプル等が圧倒的に不足しています。今ならコントリビュートし放題なので、Rustで地図アプリを作ってみたい人やRustでOSSに関わってみたい人はぜひ触ってみてください。
2022年11月30日現在でサポートされている機能
- Map
- Markers and controls
- Geography and Geometry
- User interaction handlers
- Sources
- Events and event types
Discussion