
Docker と PostGIS で札幌駅から中島公園までの距離を空間関係関数で計算してみた
はじめに
業務で GIS の知識が必要になりました。まずはローカル環境で手を動かし、空間データベースの基礎を身につけるところから始めます。
Docker で PostGIS 環境を構築し、札幌のランドマークを題材に拠点間の距離を計算してみました。札幌駅や大通公園の緯度経度をデータベースに投入し、空間関係関数で距離を求めるまでをひととおり試した内容をまとめます。
結果早見
Docker Compose で PostGIS 環境を構築し、札幌のランドマーク 6 地点の緯度経度を登録しました。ST_DistanceSphere 関数で大通公園から各地点への距離を計算し、Google Maps の直線距離とほぼ一致することを確認できました。
- Docker で
postgis/postgisイメージを使えば PostGIS 環境を一瞬で構築できる ST_MakePointで緯度経度を登録し、ST_DistanceSphereで距離をメートル単位で取得できる- 計算結果は Google Maps の経路計算とほぼ一致し、答え合わせが簡単にできた
Docker Compose で PostGIS 環境を構築する
構成概要
今回は Apple Silicon(M4, arm64)の MacBook Air を使用しています。公式の postgis/postgis Docker イメージは amd64 向けに提供されています。そのため docker-compose.yml で platform: linux/amd64 を指定し、Rosetta 2 経由で動作させています。
| コンポーネント | バージョン | 用途 |
|---|---|---|
| PostgreSQL | 16(LTS) | リレーショナルDB |
| PostGIS | 3.5 | PostgreSQLの空間拡張 |
ファイル構成
learn-gis/
├── Dockerfile
├── docker-compose.yml
└── sql/
└── 00_init.sql # PostGIS拡張有効化(自動実行)
Dockerfile
公式の postgis/postgis イメージを使えば、PostgreSQL + PostGIS がセットで手に入ります。現時点で安定版の組み合わせにしました。
FROM postgis/postgis:16-3.5
docker-entrypoint-initdb.d/ に初期化 SQL を置くと、コンテナ初回起動時に自動実行されます。この仕組みを使って PostGIS 拡張を有効化します。
docker-compose.yml
services:
db:
build: .
platform: linux/amd64
container_name: postgis-handson
ports:
- "5432:5432"
environment:
POSTGRES_USER: gisuser
POSTGRES_PASSWORD: gispass
POSTGRES_DB: gis_db
volumes:
- postgis_data:/var/lib/postgresql/data
- ./sql/00_init.sql:/docker-entrypoint-initdb.d/00_init.sql
volumes:
postgis_data:
| ボリューム | 用途 |
|---|---|
postgis_data |
DBの永続化 |
./sql/00_init.sql:... |
PostGIS拡張有効化の初期化SQL |
初期化 SQL
CREATE EXTENSION IF NOT EXISTS postgis;
SELECT PostGIS_Full_Version();
起動と接続確認
# コンテナのビルドと起動
docker compose up -d
# psqlで接続
docker compose exec db psql -U gisuser -d gis_db
接続できたら、PostGIS のバージョンを確認します。
SELECT PostGIS_Full_Version();
以下のような出力が返れば成功です。
POSTGIS="3.5.2 ..." [EXTENSION] PGSQL="160" GEOS="3.9.0-CAPI-1.16.2" PROJ="7.2.1 ..." ...
ポイントテーブルの作成
GEOMETRY 型とは
PostGIS では通常のテーブルに GEOMETRY 型のカラムを追加することで、空間データを格納できます。
CREATE TABLE landmarks (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
geom GEOMETRY(Point, 4326) -- ← 空間カラム
);
GEOMETRY(Point, 4326) の意味は以下の通りです。
| 要素 | 説明 |
|---|---|
GEOMETRY |
PostGISの空間データ型 |
Point |
ジオメトリの種類(Point, LineString, Polygon等) |
4326 |
SRID = EPSG:4326(WGS84、GPS座標系) |
テーブル作成
psql に接続した状態で以下を実行します。
CREATE TABLE landmarks (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
geom GEOMETRY(Point, 4326)
);
geometry_columns ビューでメタデータ確認
PostGIS は空間カラムの情報を geometry_columns ビューに自動登録します。
SELECT
f_table_name AS table_name,
f_geometry_column AS geom_column,
srid,
type
FROM geometry_columns
WHERE f_table_schema = 'public';
table_name | geom_column | srid | type
------------+-------------+------+-------
landmarks | geom | 4326 | POINT
札幌のランドマークデータを投入する
ST_MakePoint でポイントを作成
地図上の地点を緯度・経度で表した座標をポイントといいます。ST_MakePoint と ST_SetSRID を組み合わせて、緯度経度からポイントデータを作成します。
INSERT INTO landmarks (name, category, geom) VALUES
('札幌駅', '駅',
ST_SetSRID(ST_MakePoint(141.3507, 43.0687), 4326));
-- ↑ 経度 ↑ 緯度 ↑ SRID
緯度経度は Google Maps から確認しました。

データ投入
札幌駅から南にある目ぼしいものの座標を調べたので投入します。
INSERT INTO landmarks (name, category, geom) VALUES
('札幌駅', '駅', ST_SetSRID(ST_MakePoint(141.3507, 43.0687), 4326)),
('札幌時計台', '観光', ST_SetSRID(ST_MakePoint(141.3536, 43.0625), 4326)),
('さっぽろテレビ塔', '観光', ST_SetSRID(ST_MakePoint(141.3568, 43.0611), 4326)),
('大通公園', '公園', ST_SetSRID(ST_MakePoint(141.3563, 43.0603), 4326)),
('すすきの交差点', '繁華街', ST_SetSRID(ST_MakePoint(141.3533, 43.0555), 4326)),
('中島公園', '公園', ST_SetSRID(ST_MakePoint(141.3549, 43.0487), 4326));
データ確認
投入したデータを確認します。ST_AsText でジオメトリを WKT(Well-Known Text)形式で表示できます。
SELECT name, category, ST_AsText(geom) AS wkt FROM landmarks;
name | category | wkt
------------------+----------+-------------------------
札幌駅 | 駅 | POINT(141.3507 43.0687)
札幌時計台 | 観光 | POINT(141.3536 43.0625)
さっぽろテレビ塔 | 観光 | POINT(141.3568 43.0611)
大通公園 | 公園 | POINT(141.3563 43.0603)
すすきの交差点 | 繁華街 | POINT(141.3533 43.0555)
中島公園 | 公園 | POINT(141.3549 43.0487)
POINT(経度 緯度) の形式で格納されていることがわかります。
ポイント間の距離を計測する
ST_DistanceSphere で 2 点間の距離を計算
ST_DistanceSphere は、地球を球体として近似し、2 点間の距離をメートル単位で返す関数です。
SELECT
a.name AS from_point,
b.name AS to_point,
ROUND(ST_DistanceSphere(a.geom, b.geom)::numeric, 0) AS distance_m
FROM landmarks a, landmarks b
WHERE a.name = '札幌駅' AND b.name = '中島公園';
from_point | to_point | distance_m
------------+----------+------------
札幌駅 | 中島公園 | 2250
札幌駅から中島公園まで約 2.2km であることがわかります。この値は Google Maps で同じ 2 点間の直線距離を測定した結果とほぼ一致します。

大通公園から全ランドマークの距離一覧
大通公園を起点に、すべてのランドマークまでの距離を近い順に表示します。
SELECT
b.name,
b.category,
ROUND(ST_DistanceSphere(a.geom, b.geom)::numeric, 0) AS distance_m
FROM landmarks a, landmarks b
WHERE a.name = '大通公園'
AND a.name != b.name
ORDER BY distance_m;
name | category | distance_m
------------------+----------+------------
さっぽろテレビ塔 | 観光 | 98
札幌時計台 | 観光 | 329
すすきの交差点 | 繁華街 | 587
札幌駅 | 駅 | 1039
中島公園 | 公園 | 1295
大通公園からもっとも遠い中島公園でも約 1.3km です。大通公園を歩いたあとに中島公園散策も十分可能ですので、ぜひ足を延ばしてみてください。
おわりに
PostGIS を使った空間データベースの第一歩として、ポイントデータの登録と距離計算を試しました。普段の SQL の延長で空間的な問いに答えられる点は、RDB を扱っているエンジニアにとって親しみやすいと感じました。
座標や、空間関係関数の計算結果を Google Maps で確認できるのは、初学者には大変ありがたかったです。座標の取得はもちろん、計算が正しいかどうかを直感的に確認できるのは助かった。ただ、札幌駅から直線で結べるところにある中島公園であったからではあるが。
今回は ST_DistanceSphere(球体近似)を使いましたが、より高精度な ST_DistanceSpheroid(楕円体計算)や、ラインやポリゴンを扱った空間クエリもあります。次のステップでは LineString(線)や Polygon(面)を使った空間検索に挑戦してみたいです。







