Docker + OpenLayers + Turf.js + FlatGeobuf + FastAPI + PostGIS + Celery で作る汎用WebGIS基盤 1 ー 全体構想
はじめに
GISというと、まずQGISやArcGIS ProのようなデスクトップGISを思い浮かべる人が多いと思います。
QGISやArcGIS Proは、PCにインストールして利用する高機能なGISソフトウェアです。地図表示、データ編集、空間解析、ラスタ処理、座標変換、地図レイアウト作成など、多くのGIS作業を一つのアプリケーション内で行えます。
GIS担当者が個別にデータを作成したり、複雑な解析を行ったりする用途では、非常に便利なツールです。
一方で、GISを業務システムとして複数人で利用しようとすると、少し事情が変わってきます。
例えば、
- 複数人で同じGISデータを利用したい
- 各PCへGISソフトをインストールしたくない
- ブラウザだけで地図を確認・編集したい
- 現場や別部署からも利用したい
- データを一か所で管理したい
- 編集内容をすぐに共有したい
- GISに詳しくない人にも使ってもらいたい
- GIS処理を業務システムの一部として組み込みたい
といった要件が出てきます。
こうした用途では、デスクトップGISだけで運用するよりも、WebGISとして仕組みを作った方が扱いやすくなる場合があります。
デスクトップGISとWebGISの違い
デスクトップGISでは、基本的に利用者のPC上でGISデータを開き、編集や解析を行います。
大きな違いは、GIS機能を一台のPCの中だけで完結させず、ブラウザ、API、データベース、バックグラウンド処理を組み合わせて、一つのシステムとして構成する点です。
データ管理の考え方も変わる
デスクトップGISでは、ShapefileやGeoPackage、GeoJSONなどのファイルを直接扱うことがよくあります。
data/
├── roads.shp
├── facilities.geojson
├── area.gpkg
└── survey.tif
少人数で作業する場合は、この方法でも十分です。
しかし、複数人で同じデータを編集するようになると、
roads_final.shp
roads_final2.shp
roads_final_new.shp
roads_final_new2.shp
のように、どれが最新なのか分かりにくくなることがあります。
そこで今回のWebGISでは、GISデータの正式な保存先をPostgreSQL / PostGISにまとめます。
利用者A ─┐
利用者B ─┼── WebGIS ── PostgreSQL / PostGIS
利用者C ─┘
利用者は同じデータベースを参照するため、データ共有や更新管理を行いやすくなります。
WebGISがデスクトップGISを置き換えるわけではない
今回のWebGISは、QGISやArcGIS Proを置き換えることが目的ではありません。
むしろ、両方を組み合わせて使うことを想定しています。
GIS担当者はQGISやArcGIS Proで高度な編集や解析を行い、一般利用者はWebGISからデータを確認・検索・編集する、といった使い分けができます。
イメージとしては、
QGIS / ArcGIS Proは、GIS技術者が高度な作業を行うためのツール
WebGISは、GISを業務や組織全体で利用するための仕組み
という関係です。
では、実際にWebGISを作る場合、どのような構成にすればよいのでしょうか。
地図を表示するだけなら比較的簡単ですが、実際の業務システムでは次のような機能が必要になることが多くあります。
- 地図上にGISデータを表示する
- 点・線・面を作成する
- 既存地物を編集する
- 属性情報を変更する
- 空間検索を行う
- 位置関係を判定する
- 大量のベクタデータを高速表示する
- 重いGIS処理をサーバ側で実行する
- バッチ処理や解析を非同期で実行する
- 処理履歴を保存する
- クラウド環境へ展開する
こうした機能を一つの技術だけで実現しようとすると、構成が複雑になります。
そこで今回は、
- OpenLayers
- Turf.js
- FlatGeobuf
- FastAPI
- PostgreSQL / PostGIS
- Celery
- Redis
- Nginx
- Docker / Docker Compose
を組み合わせた、汎用的なWebGIS基盤を考えます。
それぞれに役割を持たせ、
WebGIS基盤の役割分担
-
OpenLayers
地図表示・編集 -
Turf.js
ブラウザ側の簡易空間処理 -
FlatGeobuf
大量ベクタデータの高速表示 -
FastAPI
REST API・Validation・Job受付 -
PostgreSQL / PostGIS
GISデータ・業務データの正式な保存先 -
Redis
Celery用Message Broker -
Celery
重いGIS処理・非同期処理 -
Nginx
Webアプリ・静的GISデータ配信 -
Docker / Docker Compose
開発環境とクラウド環境の共通化
という形に分けます。
この記事では、実装へ入る前段階として、
「WebGIS全体をどのように構成し、それぞれの技術に何を担当させるか」
を整理していきます。
最終的な画面イメージ図はこんな感じでしょうか。
1. 全体構成
まずはシステム全体構成のイメージです。
ここで、特に重要なのは、データの取り扱いです。
PostgreSQL / PostGIS
↓
正式な保存先
FlatGeobuf
↓
表示用データ
と分けることです。
2. 各技術の役割
全体を整理すると、各技術の担当は次のようになります。
| 技術 | 主な役割 |
|---|---|
| OpenLayers | 地図表示、地物選択、作成、編集 |
| Turf.js | ブラウザ上の簡易空間処理 |
| FlatGeobuf | 大量ベクタデータの高速表示 |
| FastAPI | REST API、保存、検索、Validation、Job受付 |
| PostgreSQL / PostGIS | GISデータと業務データの正式な保存先 |
| Celery | 時間のかかるGIS処理やバッチ処理 |
| Redis | CeleryのMessage Broker |
| Nginx | Webアプリと静的ファイルの配信 |
| Docker | 開発環境とクラウド環境の共通化 |
それぞれの役割を明確にしておくと、機能追加や保守がしやすくなります。
3. PostgreSQL / PostGISを正式な保存先にする
GISシステムでは、データをどこに保存するかが重要です。
今回の構成では、PostgreSQL / PostGISをデータの正式な保存先として扱います。
例えば次のようなデータを保存します。
PostgreSQL + PostGIS
├── Pointデータ
├── LineStringデータ
├── Polygonデータ
├── 属性情報
├── レイヤ管理情報
├── ユーザー編集情報
├── Job情報
├── 処理履歴
└── 解析結果
PostGISを利用することで、一般的なRDBの機能に加えて、空間データを扱えます。
例えば、
- Point
- LineString
- Polygon
- MultiPoint
- MultiLineString
- MultiPolygon
などをGeometryとして保存できます。
また、
- 交差
- 包含
- 距離
- 面積
- 最近傍
- Buffer
- 空間検索
といったGIS処理もデータベース側で実行できます。
4. OpenLayersはWebGISの中心
ブラウザ側の地図表示にはOpenLayersを使います。
OpenLayersでは、背景地図やGISレイヤの表示だけでなく、編集機能も実装できます。
例えば、
OpenLayers
├── 背景地図
├── ベクタレイヤ
├── ラスタレイヤ
├── Select
├── Draw
├── Modify
├── Snap
├── Popup
├── Tooltip
└── 属性編集UI
といった機能を組み合わせられます。
用途としては、例えば次のようなシステムに使えます。
- 設備台帳
- 道路管理
- 河川管理
- 上下水道管理
- 防災GIS
- 農地管理
- 森林管理
- 都市計画
- 災害履歴管理
- 点検管理
- 地質・地盤情報管理
- 観測データ管理
OpenLayersを単なる地図ビューアではなく、WebGISエディタとして利用します。
5. Turf.jsはブラウザ側の軽量GIS処理
地図上で地物を編集したとき、ユーザーにすぐ結果を返したい処理があります。
そのような処理にはTurf.jsを使います。
例えば、
- 距離計算
- 面積計算
- Buffer
- 交差判定
- 包含判定
- 最近傍点
- 点と線の距離
- LineString分割
- Bounding Box取得
- Centroid計算
などです。
例えばユーザーがPolygonを作成した直後に、
面積: 12,500 m2
と表示したり、
既存エリアと重複しています
という警告を出したりできます。
ただし、Turf.jsだけを正式なValidationには使いません。
ブラウザ側の処理は、ユーザー操作に対する即時フィードバック用と考えます。
Turf.js
↓
画面上の簡易チェック
FastAPI + PostGIS
↓
保存前の正式チェック
という分担にします。
6. FlatGeobufは大量ベクタ表示に使う
WebGISでは、GeoJSONが使いやすい形式としてよく利用されます。
ただし、データ量が大きくなると、毎回すべてをGeoJSONで読み込む方法は効率が悪くなります。
そこで、表示用データとしてFlatGeobufを利用します。
例えば、
facilities.fgb
roads.fgb
areas.fgb
hazards.fgb
mesh.fgb
のようなファイルを用意します。
FlatGeobufは大容量ベクタデータの配信に向いており、表示範囲を使った部分取得も可能です。
基本的な流れは次のようになります。
FlatGeobufはあくまで表示用です。
データの正式な保存先はPostgreSQL / PostGISに置きます。
7. 閲覧と編集を分ける
WebGISでは、閲覧と編集の処理を分けた構成が分かりやすいです。
編集データだけAPI経由で保存します。
この構成にすると、大量の閲覧アクセスと編集処理を分離できます。
8. FastAPIはアプリケーションの窓口
FastAPIは、ブラウザとPostgreSQL / PostGISの間に入るAPI層として使います。
例えば次のようなAPIが考えられます。
GET /api/features
GET /api/features/{id}
POST /api/features
PUT /api/features/{id}
DELETE /api/features/{id}
POST /api/search
POST /api/validation
POST /api/jobs
GET /api/jobs/{id}
GET /api/results/{id}
もちろん実際のURL構成は対象システムによって変わります。
FastAPIの役割は、
- データ取得
- データ保存
- 属性更新
- 空間検索
- Validation
- 認証との連携
- Job登録
- 処理結果取得
などです。
9. 重い処理はCeleryへ分ける
すべての処理をFastAPIのHTTPリクエスト内で行うと、時間のかかる処理でユーザーを待たせることになります。
そこで、重い処理はCeleryへ渡します。
例えば、
- 大量GeometryのValidation
- 空間集計
- Buffer一括生成
- Intersection一括処理
- 座標変換
- FlatGeobuf生成
- GISファイル変換
- バッチ解析
- 帳票用データ生成
- シミュレーション
- 大規模統計処理
などです。
処理の流れは次のようになります。
10. Job状態はPostgreSQLで管理する
Celeryを使う場合でも、業務上のJob状態はPostgreSQLに保存しておく方が管理しやすくなります。
例えば、
queued
running
completed
failed
のような状態を持たせます。
処理開始時に、
status = queued
Celery Workerが開始したら、
status = running
終了したら、
status = completed
エラーの場合は、
status = failed
とします。
RedisはCeleryのBrokerとして利用しますが、正式な業務履歴はPostgreSQL側で管理します。
11. PostgreSQLへ処理結果も保存する
今回はデータベースをPostgreSQL / PostGISに一本化します。
そのため、処理結果や解析結果も必要に応じてPostgreSQLへ保存します。
例えば、
Job
↓
Result
↓
Result Detail
という構造です。
対象によっては、
- 集計結果
- 時系列結果
- Geometry処理結果
- 判定結果
- エラー情報
- ログ情報
などを保存できます。
ただし、大量の時系列や巨大な解析結果をすべてDBへ入れるべきかは、実際のデータ量を見て決めます。
これはシステムごとに異なるため、初期段階では固定しません。
12. Dockerを前提にする
今回はクラウド環境へ持っていくことを前提として、最初からDockerで構築します。
Docker Composeでは、ひとまず次のサービスを考えます。
webgis
├── frontend
│ ├── Nginx
│ ├── OpenLayers
│ └── Turf.js
│
├── api
│ └── FastAPI
│
├── worker
│ └── Celery
│
├── redis
│ └── Celery Broker
│
└── db
├── PostgreSQL
└── PostGIS
FlatGeobufはNginxから静的ファイルとして配信する構成を想定します。
13. Dockerネットワーク
コンテナ間の接続は次のようになります。
外部へ公開するのは、基本的にNginxだけです。
PostgreSQLやRedisを直接インターネットへ公開する構成にはしません。
14. ディレクトリ構成
汎用的なWebGISとしては、例えば次のような構成から始められます。
webgis-platform/
│
├── compose.yaml
├── .env
│
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ ├── src/
│ └── nginx/
│
├── backend/
│ ├── Dockerfile
│ ├── pyproject.toml
│ └── app/
│ ├── main.py
│ ├── api/
│ ├── models/
│ ├── schemas/
│ ├── services/
│ ├── repositories/
│ └── db/
│
├── worker/
│ ├── Dockerfile
│ └── app/
│ ├── celery_app.py
│ ├── tasks/
│ └── gis/
│
├── database/
│ ├── migrations/
│ └── init/
│
├── flatgeobuf/
│ └── data/
│
├── tests/
│ ├── frontend/
│ ├── backend/
│ └── integration/
│
└── scripts/
├── start.sh
├── stop.sh
├── test.sh
└── backup.sh
この構成は固定ではありません。
実装する機能に応じて、ディレクトリを増減させます。
15. 開発環境からクラウドへ
Dockerを使う理由の一つは、ローカルとクラウドの環境差を小さくできることです。
という形にできます。
イメージとしては、
最初は1台のLinuxサーバ上ですべて動かし、必要になった段階でサービスを分離できます。
16. 本番ではPostgreSQLを外部DBへ分離できる
初期段階では、
を一台で動かす構成でも問題ありません。
将来的に負荷が増えた場合は、
という構成に変更できます。
アプリケーション側では接続先を環境変数で管理します。
DATABASE_URL=postgresql://...
こうしておけば、Docker内のPostgreSQLからクラウドのPostgreSQLへ移行しても、アプリ本体への影響を小さくできます。
17. 開発用と本番用のComposeを分ける
最初から開発環境と本番環境を分けておくと便利です。
例えば、
compose.yaml
compose.dev.yaml
compose.prod.yaml
とします。
同じ構成をベースにしながら、環境ごとの差だけを上書きする考え方です。
18. PostgreSQLはDocker Volumeで永続化する
PostgreSQLのデータをコンテナ内部だけに保存すると、コンテナを削除したときにデータも失われます。
そのため、Docker Volumeを使います。
PostgreSQL Container
↓
Docker Volume
↓
Database Files
本番環境では、さらにバックアップ設計も必要です。
例えば、
- pg_dump
- 定期バックアップ
- 世代管理
- 外部ストレージ保存
- リストア確認
などを別途設計します。
19. システム内の主なデータフロー
処理を分けて考えると、全体が分かりやすくなります。
それぞれを別の経路として考えることで、システムが複雑になりにくくなります。
20. この構成で作れるもの
この構成は特定分野専用ではありません。
例えば次のようなWebGISに応用できます。
インフラ管理
- 道路
- 河川
- 橋梁
- トンネル
- 上下水道
- 電力設備
- 通信設備
防災
- ハザードマップ
- 避難所管理
- 災害履歴
- 土砂災害警戒区域
- 浸水想定区域
- 火山防災
- 被害状況管理
環境
- 森林
- 河川環境
- 動植物調査
- 水質
- 大気
- 騒音
- 土壌
行政
- 都市計画
- 固定資産
- 公共施設
- 農地
- 空き家
- 公園
- 道路占用
調査・点検
- 現地調査
- 写真管理
- 点検履歴
- 異常箇所
- 補修履歴
基本となる仕組みは共通です。
21. まだ決めていないこと
全体構成が決まっても、個別システムによって変わる部分があります。
この段階では無理に固定しません。
座標系
利用する地域や元データによって変わるため、PostGISのSRIDは後で決めます。
フロントエンドフレームワーク
OpenLayersとTurf.jsを利用することは決めても、
- React
- Vue
- TypeScript中心の構成
などは別途検討できます。
FlatGeobufの分割方法
- レイヤ単位
- 地域単位
- 業務単位
- データ量単位
など、実データを見て決めます。
FlatGeobufの再生成タイミング
- 編集直後
- 一定時間ごと
- 手動
- バッチ
- 更新件数が一定数を超えたとき
などの方法があります。
解析結果の保存方法
結果量が非常に大きい場合、すべてをPostgreSQLに保存するかどうかは改めて検討します。
22. 今回の基本方針
最後に、今回の構成を整理します。
この中で特に重要なのは、
データの正式な保存先をPostgreSQL / PostGISに集約する
という考え方です。
FlatGeobufやRedisは便利ですが、どちらも正式な保存先の代わりにはしません。
23. 次に考えること
全体構成が決まったら、次は共通データモデルを考えます。
例えば、
というように、特定業務に依存しない形で基本構造を作っておくと、あとから様々なWebGISへ展開しやすくなります。
次の段階では、
PostgreSQL / PostGISで汎用WebGISのデータモデルをどう設計するか
を検討していきます。
参考リンク集
- OpenLayers https://openlayers.org/
- Turf.js https://turfjs.org/
- FlatGeobuf https://flatgeobuf.org/
- FastAPI https://fastapi.tiangolo.com/
- PostgreSQL https://www.postgresql.org/
- PostGIS https://postgis.net/
- Celery https://docs.celeryq.dev/
- Redis https://redis.io/
- Docker Compose https://docs.docker.com/compose/
- Nginx https://nginx.org/















