🌏

MapLibre GL JSとOPFSで、ブラウザのみで地図スタイルを編集する未来を切り開くでござる

2024/10/30に公開

本日のデモ:
https://yuiseki.github.io/charites-like-opfs/

Image from Gyazo

本日のソースコード:
https://github.com/yuiseki/charites-like-opfs


お待たせしたでござるな!本日拙者がお話いたすは、「MapLibre GL JS」と「OPFS」を用い、ブラウザのみで地図のスタイルを自在に操るというアプローチ、それを実際に実現したということについてでござる!これを名付けて charites-like-opfs と呼びまする!

まず、拙者が成し遂げたことを、ここにご報告いたす!

  • 一つ、YAMLを用いた地図スタイルファイルの編集とそのプレビューが、Webブラウザのみで完結し動作することを見事実証いたしたこと。
  • また一つ、YAMLにおける !!inc/file なる拡張記法が、同じくWebブラウザのみで完結して動くことを実証いたしたこと。

拙者が成し遂げたるこのアプローチにより、 地図スタイルファイルの編集やプレビューにおいて、もはやサーバーやDockerやNode.jsの環境は不要となることを示し申した! もしも疑念を抱かれるならば、ぜひともデモを実際に触わり、確かめてくだされ。この記事にては、技術的なコンセプトと実装の重要なポイントや工夫を余すところなくご紹介いたしまする!

この記事を読み終えたなら、そなたも ブラウザのみで即座にスタイルを編集できる地図を開発する侍 になれるでござる!そのための知識とやり方を、ここで詳しく書き記すゆえ、参考にされよ!

それでは、いざ、Web地図の未来へ、参ろうぞ!

主要概念、主要ライブラリの紹介

Web地図の世界には多くの概念やライブラリがあり、それぞれの役割を知ることが肝要にござる。まずは、以下に紹介するでござる。

  • ベクトルタイル地図
    • 地図データを小さな四角形「タイル」に分割し、データ量を抑えつつ、高速で表示できる仕組みでござる。これらのタイルはベクトル形式ゆえ、拡大縮小しても美しい地図を保てるのが特長でござる。
  • Mapbox GL JSMapLibre GL JS
    • 両者ともWebブラウザ上で地図を描画するためのライブラリでござるが、 Mapbox GL JS はMapbox社が提供するもので、 MapLibre GL JS はそのオープンソース版にござる。特にMapLibreはコミュニティ主導で進化を続けているのでござる。今回は MapLibre GL JS を使うでござる!
  • Mapbox Style SpecificationMapLibre Style Specification
    • ベクトルタイル地図のスタイル(表示する際の地物の色や形状、ラベルなど)を定義するための仕様にて候。MapboxとMapLibreそれぞれのスタイル仕様があるものの、基本的には似通っており、地図の見た目を細かくカスタマイズできるのが強みでござる。
    • これがどんなものかをシンプルに言うと、 バカみたいクソデカい、チューリング完全ではないものの地図スタイルの計算手順が記述された、JSONで表現された宣言的DSL でござる。
  • OPFS
    • 正式には「Origin Private File System」と申し、サーバーに頼らず、Webブラウザ内でファイルシステムを構築することができるという仕様にござる。今回最も重要な概念でござる!
  • charites
    • charites は、MapLibre Style Specificationに対応するベクトルタイル地図スタイルを、YAML形式で簡単かつ直感的に書き起こすための効率的なコマンドラインツールにござる。YAMLの読みやすさを活かし、JSONコンパイラやリント機能、さらにローカルサーバーでのスタイルプレビューも備えており、地図スタイルの調整作業を大いに簡素化できるでござる。
    • charites は、 国連ベクトルタイルツールキット(UNVT) の一部であり、国連の展開しているOSS活動の一つでござる。実は拙者もいちコントリビューターとして参加しているプロジェクトでござる。

https://github.com/unvt/charites

なぜ charites-like-opfs を開発したのか

charites を動かすにはNode.js環境が必要

  • charites はNode.js環境が前提とされており、動作にはNode.jsが必要でござる。
  • そのため、Node.jsのインストールや環境設定がユーザーに求められ、少々の手間がかかるのでござる。
  • Node.js環境のセットアップが難しい者にとって、利用の敷居が高く感じられる場合もあるのでござる。
  • また、動作している環境のファイルシステムの読み書きの権限も必要になるのでござる。
    • これはセキュリティ的に懸念が生じるところなのでござる。

charites (的な何か)を、Webブラウザのみで動かしたい

もしもそれができれば、

  • Node.js環境を用意せずとも地図スタイル編集が可能となることで、初心者でも簡単に利用できるようになるのでござる。
  • ローカル環境の状況に依存せず、ブラウザさえあれば地図スタイル編集が完結するため、デバイスやOSを問わずに利用できる柔軟性があるでござる。
  • これにより、地図スタイル開発者の作業効率が圧倒的に向上し、設定や管理の手間が省けるため、新しい地図スタイルを開発する試行錯誤のプロセスが、極めて素早くなるのでござる。
  • 地図スタイルを、様々な状況やニーズに応じて幅広い人々が素早く簡単に開発できるようにしたいのでござる。
  • また、サーバーやローカル環境のファイルシステムを触ることがないので、セキュリティ的に圧倒的に安心できるのでござる。

かような背景により、 charites-like-opfs を開発し、ブラウザのみでの地図スタイル編集を実現した次第にござる!

技術的なアプローチ

最初にプロジェクトの概要を説明するでござるよ!

いつも通り、 Vite.js + React + Typescript で、プロジェクトを初期化したでござる!

そして、以下のnpmライブラリをインストールしたでござる!

  • react-map-gl & maplibre-gl
    • 定番の、地図を表示するnpmライブラリでござる!
  • yaml
    • 一番アクティブにメンテナンスされているYAMLパーサーでござる!
  • @uiw/react-codemirror & @codemirror/lang-yaml
    • Reactで良い感じのシンタックスハイライトとエディターを実装できるnpmライブラリでござる!
    • 今回始めて触ったものの、非常に使い勝手が良かったでござる。きちんとメンテナンスされていて良い感じで、オススメでござるよ。実は このZennのエディターもCodeMirrorで実装されている らしいでござる。

さて、ライブラリの使い方は各ライブラリの公式ドキュメントを読んでいただくとして、ライブラリ以外で工夫したところを紹介していくでござる!

Typescriptにおける OPFS ハマりポイント

  • 一点だけ、 tsconfig.json (Vite.jsの場合は tsconfig.app.json ) で、 "lib": ["ES2020", "DOM", "DOM.Iterable", "DOM.AsyncIterable"], としないと、 FileSystemFileHandle の型が一生ズレた状態になるので、ここだけは気をつけるでござるよ!

charites の良いところだけを パクる リスペクトし、参考にする

  • まず、 charites が現れる以前、 Mapbox Style SpecificationMapLibre Style Specification での開発は、まさに苦行そのものだったでござる。
    • なにせ、数千行にもおよぶ巨大で深いネストを持つJSONを、エディターで編集するという、人知を超えた作業を強いられていたでござる……。
  • そこで charites が打ち出した一番の革新、それは、一つの巨大なJSONを扱うのではなく、「 地図スタイルを複数の小さなYAMLファイルに分割し、それらを結合する 」という発想にござる!
  • さらに charites には、YAMLファイルにおける「変数補間(variable interpolation)」も可能にしておるが、これとて、あのバカでかいJSONを分割して管理するという発想ほどの衝撃ではないと拙者は感じているでござる。
  • そこで、簡単のために、 charites の 「地図スタイルを複数の小さなYAMLファイルに分割し、それらを結合する」 という発想だけを参考にするでござる! charites のすべての能力を再現しているわけではないため、 charites-like- と名付けたのでござる。
  • 完璧を目指すより、世界を変えるほうが先でござるよ。

OPFSによるWebブラウザ上での簡易なファイルシステムを気合いと根性で実装

  • さて、要するに、 charites の最も重要な発想である「地図スタイルを複数の小さなYAMLファイルに分割し、それらを結合する」という仕組みだけを、なんとかWebブラウザ上で実現したい、ということでござる。
  • このためには、ファイルやディレクトリの階層構造をうまく表現できる、いわゆるファイルシステム的なものが必要になるでござる。
  • そこで OPFS を使うと基本的な仕組みは揃っているものの、これを React 上で扱うのはなかなか手強いものがあったでござる。

引用するには長すぎるゆえ、以下のコードを見てほしいでござる。ほとんど全部じゃねえか。

https://github.com/yuiseki/charites-like-opfs/blob/main/src/components/FileEditor.tsx

YAMLにおける !!inc/file 拡張記法を気合いと根性で実装

  • 今回、特に工夫が必要だったのがここなのでござる!!!!
  • 繰り返しになりまするが、 charites の一番大事な発想は 「地図スタイルを複数の小さなYAMLファイルに分割し、それらを結合する」という仕組みでござる。この仕組みは、 !!inc/file 拡張記法で実現されているのでござる!!
  • これは、 !!inc/file path/to/file.yml と書くことで、 path/to/file.yml の内容が、その場所にインデントも整えて注入されるという仕組みでござる。
  • しかし、この !!inc/file なる記法は本来の YAML 仕様には存在せず、 yaml npm ライブラリもこれをサポートしていないのが現実なのでござる。
  • そこでどうしたかと申せば、根気と気合いで、この機能を実装したのでござる!
  • 実のところ、ChatGPTと激論を交わしておったら、なんと動くものが出来上がってしまい申した。
  • 生成AI時代にプログラマーに必要なのは、まさに気合いと根性だけでござるよ。

!!inc/file 拡張記法を実現するYAMLパーサー

というわけで、ChatGPTに

  • !!inc/file layers/background.yml と書かれている場合にはOPFS上のlayers/background.ymlを注入して、その結果をJSONとして出力したいです。考察、計画、提案という形で、ステップバイステップで考えてください。
  • yaml.parseを実行するよりも前にYAMLを注入しないとダメそうです。parseYamlWithIncludesを修正してください。
  • あくまでもYAMLとして注入しておいて、最後にyaml.parseすればいいはずです。下手に再帰にこだわらないでください。
  • YAMLにおいて最も重要なインデントが失われています。!!inc/fileが使われている時点でのインデントを認識して、注入するYAMLテキストはそのぶんインデントしなければ、期待通りの結果にならないと思われます。修正してください
  • たぶん正規表現が間違っています。修正してください
  • いや、間違った正規表現ですよ。- !!inc/file <path> のように、配列の場合が考慮できてないですよね。
  • まだ間違っています。修正してください
  • 配列の中のキーには-をつけるべきではなく、-のぶんもインデントをすればよいのです。言っていることの意味がわかりますか?修正してください
  • やればできるじゃん!!

のような、パワハラじみたダメ出しを延々と続けていたらついに出来上がったのが、以下のコードでござる。

https://github.com/yuiseki/charites-like-opfs/blob/main/src/utils/parseYaml.ts

まとめ

  • いやはや、今回は言いたいことを好き放題に書き綴ったゆえ、満足したと同時にだいぶ疲れ申した。拙者、そろそろ休んでもよいでござるか?
  • それでも最後に一つだけ申しておきたいことがあるでござる。地図スタイルファイルを編集するたびに Docker や Node.js を起動するの、正直なところ、めっちゃダルいと思わぬか?
  • かような面倒を終わらせたいと願ってこそ、このような取り組みにチャレンジし、この記事を書いておる次第でござる。
  • とはいえ、油断は禁物、慢心は無用!ブラウザだけで地図スタイルを編集するという、この戦は、まだ始まったばかりでござるからな。
  • OPFSの仕様を見れば、ブラウザの外部ディレクトリを選んでインポートしたり、OPFS上のディレクトリをエクスポートすることも、技術的には可能でござる。
  • もし charites-like-opfs がまだまだ不完全と思うならば、そなたが実装に挑んでも一向に構わぬゆえ、どうぞ遠慮なく参られよ!

今後の展望

  • 拙者のことをご存知の方なら、拙者がすでに charites-ai なるプロジェクトを世に放っていることをご存じかもしれんでござるな。
  • charites-ai は、 charites に必要な地図スタイルの YAML ファイルを、自然言語による指示をもとに生成AIに編集させるという新たな試みでござる。

https://github.com/yuiseki/charites-ai

  • ただし、残念ながら charites-ai は、現時点ではまだ無念の、プロトタイプでござる。
    • charites-aicharites に依存しているため、Node.jsの実行環境とファイルシステムの読み書きの権限が必須ゆえ、限界に突き当たってしまったのでござる。
  • 前回の記事にも書き申したが、どのようなアイデアも、ソフトウェアプロダクトとして誰もが手軽に試せるようデプロイできてこそ、初めて世界を変える一歩となると拙者は考えて候。
  • さあ、想像してほしいのでござる。Webブラウザ上でサンドボックス化されたファイルシステムを利用できれば、 charites-ai もまた、Node.js実行環境なしで動かせるようになるやもしれぬ!
  • もう一度ここに来てほしいでござるよ。そのときには 本物の地図AI をお見せしようぞ。

拙者は、これからも引き続き、このような革新的なソフトウェアの開発に励んでまいる所存でござる!皆々が気軽に触れられるソフトウェアを作り上げることで、少しでも未来に貢献できれば、侍として誠に本望でござるよ。

もしこの記事が役に立ったならば、ぜひ「いいね」やフォローをしてくださると、拙者の励みになるでござる!今後のアップデートも楽しみにしていただければ嬉しい限りでござるよ。

それでは、拙者の記事を最後までご覧くださり感謝いたす。どうぞ素晴らしい一日をお過ごしくだされ!

Discussion