7
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

OSMデータをPostGISにインポートしてQGISで可視化する

備忘録のために投稿。
前提知識が乏しい中で試行錯誤しながら書いた記事なので一部間違った表現や解釈があるかもしれません。

前知識

OSM (OpenStreetMap)

誰でも自由に地図を使えるように、みんなでオープンデータの地理情報を作るプロジェクト。誰でも自由に参加し、地図を編集し、利用することができる

PostGIS

PostgreSQLデータベースに対して空間地図を扱うための拡張

QGIS

地理情報を閲覧、編集、分析を行うことができるGIS(Geographic Information System)ソフトウエア

OSMデータを取得する

OSMデータは以下のサイトから日本全体、または各地方毎にOSMデータをダウンロードすることができる

今回は、関西データを利用してみた。
Kansai region (a.k.a. Kinki region)
ダウンロードは複数選択肢があるが

  • osm.pbf, osm.bz2 はいわゆる osm データ であり圧縮形式の違いと思われる。
  • shp.zip は shape データ

であり、shapeデータをダウンロードしてQGISでそのまま可視化すれば目的はタイトルの目的は達することができるが、今回は OSM データにふれ合ってみたいという目的なので kansai-latest.osm.pbf (189MB (2020.12現在))をダウンロードする

さらに限定した地域のOSMデータを取得する場合

この先は、上記の関西データのOSMを利用する前提で記事は続くが、正直、非力なPCの場合、さまざまな処理で時間を要する。特に最後のQGISを利用した可視化の際にはデータ量が多すぎて操作にストレスに感じることがあると思う。
もっと小さなサイズ(地域を限定した)のOSMを扱えばこの問題は解消する。その方法も記載しておく。前知識のOSMで記載したサイトを開き、上部メニューの「エクスポート」を選択することでエリアを限定したosmを取得できる
image.png
ただし、少しでも広いエリアを選択すると You requested too many nodes (limit is 50000). Either request a smaller area, or use planet.osm とエラーになるので
都心部では現実的に徒歩数分範囲内ぐらいしか実質選択ができない。

上は京都駅北側のエリアを選択したがこのぐらいで上限を超えるギリギリの範囲内です。これでダウンロードしたosmファイルは非圧縮7MB。 非圧縮なので中身を確認することができる。中身はXMLデータであることがわかる。
image.png

PostGISの準備

今回、PostGISのサーバはDocker内で構築する。Dockerの基本を知りたい方は以前に書いた記事が参考になると思う。

PostGISは DockerHub にすでに準備されているのでこれを利用する

$ cd (適当なローカル作業ディレクトリ)

# PostGISデータのローカル保存用
$ mkdir postgis_data

# Docker起動. Postgresqlデータディレクトリはローカルにマウント. Portマッピングも. パスワードは設定しないと起動が失敗するので適当にセットしとく 
$ docker run -v `pwd`/postgis_data:/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_PASSWORD=password -d postgis/postgis

#起動確認
$ docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS          PORTS                    NAMES
07588ca07dd0   postgis/postgis   "docker-entrypoint.s…"   24 seconds ago   Up 23 seconds   0.0.0.0:5432->5432/tcp   objective_golick

# Postgresqlデータがローカルに保存されている
$ ls postgis_data/
PG_VERSION      pg_multixact        pg_tblspc
base            pg_notify       pg_twophase
global          pg_replslot     pg_wal
pg_commit_ts        pg_serial       pg_xact
pg_dynshmem     pg_snapshots        postgresql.auto.conf
pg_hba.conf     pg_stat         postgresql.conf
pg_ident.conf       pg_stat_tmp     postmaster.opts
pg_logical      pg_subtrans     postmaster.pid

#コンテナ内に侵入
$ docker exec -it 07588ca07dd0 /bin/bash
root@07588ca07dd0:/#

# PostGIS接続
root@07588ca07dd0:/# psql -U postgres
psql (12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.
postgres=#

# 動作確認
postgres=# select VERSION();
                                                     version

-----------------------------------------------------------------------------------------------
-------------------
 PostgreSQL 12.5 (Debian 12.5-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.
0-6) 8.3.0, 64-bit
(1 row)

# Postgresql切断
postgres=# \q
# Docker離脱 (ローカル復帰)
root@07588ca07dd0:/# exit
$

PostGISにOSMデータをインポート

osm2pgsqlの準備

インポートには osm2pgsql というソフトウエアを利用する

先に準備したPostGISのDockerはDebianベースであり、apt コマンドでインストール可能なのでPostGISコンテナ内にインストールして利用する

# コンテナ内に侵入
$ docker exec -it 07588ca07dd0 /bin/bash

# 以下で簡単に導入することができる
$ root@07588ca07dd0:/# apt update
$ root@07588ca07dd0:/# apt install osm2pgsql

# インストール完了
root@07588ca07dd0:/# which osm2pgsql
/usr/bin/osm2pgsql
root@07588ca07dd0:/# osm2pgsql --version
osm2pgsql version 0.96.0 (64 bit id space)

# Docker離脱 (ローカル復帰)
root@07588ca07dd0:/# exit
$

ここで一旦、osm2pgsqlが導入済みのPostGISというイメージを自作しておく

# dockerプロセス確認
$ docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS          PORTS                    NAMES
07588ca07dd0   postgis/postgis   "docker-entrypoint.s…"   21 minutes ago   Up 21 minutes   0.0.0.0:5432->5432/tcp   objective_golick

# docker停止
$ docker stop 07588ca07dd0
07588ca07dd0

# イメージの作成
$ docker commit 07588ca07dd0 postgis_with_osm2pgsql
sha256:63aa984c42a530615618d971d64bcc9a1c18fb91a4274325e747fcd5df1a09f3

# イメージ確認
$ docker images
REPOSITORY               TAG       IMAGE ID       CREATED          SIZE
postgis_with_osm2pgsql   latest    63aa984c42a5   14 seconds ago   513MB
...

# 起動中のコンテナは一旦破棄しておく
$ docker rm 07588ca07dd0
07588ca07dd0

osm2pgsqlを利用してOSMデータをPostGISへインポート

Docker起動

先にダウンロードしたOSMデータを作業ディレクトリに保存

$ ls -F
kansai-latest.osm.pbf   postgis_data/

新しく作ったイメージで起動する。(その際にOSMデータ用のマウントポイントを追加する)

# マウントオプション追加で起動
$ docker run -v `pwd`/postgis_data:/var/lib/postgresql/data -v `pwd`:/work -p 5432:5432 -d postgis_with_osm2pgsql

# 起動確認
$ docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS          PORTS                    NAMES
55f0d5aec9a0   postgis_with_osm2pgsql   "docker-entrypoint.s…"   36 seconds ago   Up 35 seconds   0.0.0.0:5432->5432/tcp   objective_swartz

# Docker侵入
$ docker exec -it 55f0d5aec9a0 /bin/bash
root@55f0d5aec9a0:/#

# 正しくマウントされているか確認
root@55f0d5aec9a0:/# ls -l /work
total 196612
-rw-r--r--  1 root     root 198153708 Dec 17 06:35 kansai-latest.osm.pbf
drwx------ 26 postgres root       832 Dec 20 04:55 postgis_data
PostGISのデータベース作成

osm2pgsqlでOSMデータをインポートする際にインポート先のPostgresqlデータベースの定義が事前に必要なので作成しておく(今回osmという名称で作成する)。その際に、そのデータベースに対してPostGISを扱うEXTENSIONの有効化が必要なので忘れずに実施すること

# Postgresql接続
root@55f0d5aec9a0:/# psql -U postgres
psql (12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.

# Database作成 (osmという名称とした)
postgres=# create database osm;
CREATE DATABASE

# Database一覧表示
postgres=# \l
                                    List of databases
       Name       |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges
------------------+----------+----------+------------+------------+-----------------------
 osm              | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
...

# osm Databaseへ接続
postgres=# \c osm
You are now connected to database "osm" as user "postgres".

# PostGISエクステンションをosmデータベースに適応
osm=# create extension postgis;
CREATE EXTENSION

# 以上で準備完了
osm=# \q
root@55f0d5aec9a0:/#
osm2pgsqlコマンドの実行

ようやくインポート作業となるがもう一つだけ準備が必要。OSMの各データには点や線、ポリコンといったデータが存在するが、そのそれぞれに対してさらに様々な属性値が存在する。その属性値をDBのTable列とどのような関係をもたせるかの定義ファイル(osm2pgsqlではstyleファイルと呼ぶ)を準備する必要がある。
今回は先に紹介したサイト LearnOSM - はじめてからのOpenStreetMapガイド 内から提供されているスタイルを利用する

上記をダウンロードして作業ディレクトリに保存する

# default.style を上記よりダウンロードして配置した
root@55f0d5aec9a0:/# ls -l /work
total 196620
-rw-r--r--  1 root     root      6025 Dec 20 05:16 default.style
-rw-r--r--  1 root     root 198153708 Dec 17 06:35 kansai-latest.osm.pbf
drwx------ 26 postgres root       832 Dec 20 04:55 postgis_data

ようやくインポート作業になる。下記コマンドを実行する

root@55f0d5aec9a0:/# cd /work
root@55f0d5aec9a0:/work# osm2pgsql --create --database=osm --slim --style=./default.style -U postgres -H localhost kansai-latest.osm.pbf
...
(かなりしばらく待つ)
...
Osm2pgsql took 24146s overall

あとは気長に待つ。私のような非力なPCの場合は一晩かかって翌朝完了するぐらいの所要時間になる。(24,146s = 6.7hour)

仕上がりを確認

planet_osm_* というTableが複数定義されており、それぞれにデータがInsertされていることが確認できる。

root@55f0d5aec9a0:/work# psql -U postgres osm;
psql (12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.

osm=# \dt
               List of relations
 Schema |        Name        | Type  |  Owner
--------+--------------------+-------+----------
 public | planet_osm_line    | table | postgres
 public | planet_osm_nodes   | table | postgres
 public | planet_osm_point   | table | postgres
 public | planet_osm_polygon | table | postgres
 public | planet_osm_rels    | table | postgres
 public | planet_osm_roads   | table | postgres
 public | planet_osm_ways    | table | postgres
 public | spatial_ref_sys    | table | postgres
(8 rows)

osm=# \d planet_osm_roads;
                         Table "public.planet_osm_roads"
       Column       |           Type            | Collation | Nullable | Default
--------------------+---------------------------+-----------+----------+---------
 osm_id             | bigint                    |           |          |
 access             | text                      |           |          |
 addr:housename     | text                      |           |          |
 addr:housenumber   | text                      |           |          |
 addr:interpolation | text                      |           |          |
 admin_level        | text                      |           |          |
 aerialway          | text                      |           |          |
...

osm=# select count(*) from planet_osm_point;
 count
--------
 367808
(1 row)

osm=# select count(*) from planet_osm_line;
  count
---------
 1263136
(1 row)

OSMのデータの基本構造

※本セクションは私の理解が甘いので間違っているかもしれません。
しっかりと理解する場合はこの辺を参照

nodes (planet_osm_nodes)

OSMを構成する点や線、ポリコンもすべて頂点の1個以上の集合。その頂点を定義するのが nodes である。Table構造もIDと緯度経度の3列なシンプルなもの。

osm=# select * from planet_osm_nodes limit 5;
    id    |    lat    |    lon
----------+-----------+------------
 56577716 | 353219106 | 1396343130
 56577734 | 353219087 | 1396342033
 56577739 | 353223037 | 1396319209
 56577850 | 353221939 | 1396321446
 56577855 | 353229890 | 1396317181

point (planet_osm_point)

いわゆる点を表す。例えばosmデータで以下のような点が定義されている場合には、id = osm_id として planet_osm_point に登録され、同IDにて座標値が planet_osm_nodes に定義されている。

 <node id="274017002" visible="true" version="4" changeset="14708059" timestamp="2013-01-19T14:04:25Z" user="xxxx" uid="xxxx" lat="34.9867552" lon="135.7624890">
  <tag k="amenity" v="restaurant"/>
  <tag k="name" v="新福菜館本店"/>
 </node>
osm=# select * From planet_osm_nodes where id = '274017002';
-[ RECORD 1 ]---
id  | 274017002
lat | 349867552
lon | 1357624890

osm=# select * From planet_osm_point where osm_id = '274017002';
-[ RECORD 1 ]------+---------------------------------------------------
osm_id             | 274017002
...
amenity            | restaurant
...
name               | 新福菜館本店
...
way                | 0101000020110F0000902A9E6466D36C41AB1EE7AF10C14F41

way (planet_osm_way) と polygon (planet_osm_polygon), line (planet_osm_line)

頂点の順序付き集合。閉じているものはpolygonであり、閉じてないものはlineとなる。
下記はployconの例で元のOSMでは以下で定義されているものの場合

<way id="160731923" visible="true" version="11" changeset="63886009" timestamp="2018-10-26T02:30:00Z" user="xxxx" uid="xxxx">
  <nd ref="1727782796"/>
  <nd ref="1727782822"/>
  <nd ref="1727782826"/>
  <nd ref="1727782831"/>
  <nd ref="1727782834"/>
  <nd ref="1727782833"/>
  <nd ref="1727782788"/>
  <nd ref="1727782787"/>
  <nd ref="1727782794"/>
  <nd ref="1727782792"/>
  <nd ref="1727782789"/>
  <nd ref="1727782796"/>
  <tag k="addr:block_number" v="590"/>
  <tag k="addr:city" v="京都市"/>
  <tag k="addr:housenumber" v="2"/>
  <tag k="addr:postcode" v="600-8216"/>
  <tag k="addr:province" v="京都府"/>
  <tag k="addr:quarter" v="東塩小路町"/>
  <tag k="addr:street" v="烏丸通七条下る"/>
  <tag k="addr:suburb" v="下京区"/>
  <tag k="building" v="retail"/>
  <tag k="building:levels" v="7"/>
  <tag k="height" v="30"/>
  <tag k="name" v="Yodobashi"/>
  <tag k="name:en" v="Yodobashi Camera"/>
  <tag k="name:fr" v="achat électronique apple"/>
  <tag k="name:ja" v="ヨドバシカメラ"/>
  <tag k="name:ja_rm" v="Yodobashikamera"/>
  <tag k="name:ko" v="요도야바시"/>
  <tag k="opening_hours" v="Mo-Su 09:30-20:00"/>
  <tag k="shop" v="electronics"/>
  <tag k="source" v="Bing"/>
  <tag k="website" v="http://www.yodobashi-kyoto.com/"/>
  <tag k="wheelchair" v="yes"/>
 </way>

planet_osm_way と planet_osm_polygon の2つのテーブルで以下で表現されている


osm=# select * from planet_osm_ways where id = 160731923;
-[ RECORD 1 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
id    | 160731923
nodes | {1727782796,1727782822,1727782826,1727782831,1727782834,1727782833,1727782788,1727782787,1727782794,1727782792,1727782789,1727782796}
tags  | {name,Yodobashi,shop,electronics,height,30,source,Bing,name:en,"Yodobashi Camera",name:fr,"achat électronique apple",name:ja,ヨドバシカメラ,name:ko,요도야바시,website,http://www.yodobashi-kyoto.com/,building,retail,addr:city,京都市,name:ja_rm,Yodobashikamera,wheelchair,yes,addr:street,烏丸通七条下る,addr:suburb,下京区,addr:quarter,東塩小路町,addr:postcode,600-8216,addr:province,京都府,opening_hours,"Mo-Su 09:30-20:00",building:levels,7,addr:housenumber,2,addr:block_number,590}

osm=# select * from planet_osm_polygon where osm_id = 160731923;
-[ RECORD 1 ]------+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------
osm_id             | 160731923
...
addr:housenumber   | 2
...
building           | retail
...
name               | Yodobashi
...
shop               | electronics
...
z_order            | 0
way_area           | 12775
way                | 0103000020110F0000010000000C000000F895DC782CD36C41CFB8D3368AC14F410A0A518A2CD36C41C2414A5E7CC14F41DE753F892
CD36C418CB722035FC14F41D10F7B1F32D36C41FAA90BC15EC14F41A67C765633D36C41566CC89A5EC14F412BD4BC963AD36C41A1CF38F55EC14F4165EA6F9C3
AD36C414D3854005DC14F41CE975B4B3ED36C4103E3FAE75CC14F4128B2515A3ED36C411735F2808CC14F415880BD0C36D36C41CA4633078CC14F41E5CCC0073
6D36C41DBEA5AAB8AC14F41F895DC782CD36C41CFB8D3368AC14F41

lineの例も同様に載せておく (planet_osm_waysとplanet_osm_line)

 <way id="174762075" visible="true" version="10" changeset="79181018" timestamp="2020-01-04T05:34:57Z" user="hayashi" uid="621319">
  <nd ref="2783315750"/>
  <nd ref="3554802235"/>
  <nd ref="410083087"/>
  <nd ref="5196506199"/>
  <nd ref="5196506198"/>
  <nd ref="2783315752"/>
  <nd ref="3743790129"/>
  <nd ref="3743790130"/>
  <tag k="highway" v="secondary"/>
  <tag k="lanes" v="2"/>
  <tag k="maxspeed" v="40"/>
  <tag k="name" v="塩小路通"/>
  <tag k="name:en" v="Shio-koji dori"/>
  <tag k="name:ja" v="塩小路通"/>
  <tag k="name:ko" v="시오코지도리"/>
  <tag k="oneway" v="yes"/>
  <tag k="oneway:bicycle" v="yes"/>
  <tag k="ref" v="115"/>
  <tag k="source" v="Bing"/>
 </way>

osm=# select * from planet_osm_ways where id = 174762075;;
-[ RECORD 1 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------
id    | 174762075
nodes | {2783315750,3554802235,410083087,5196506199,5196506198,2783315752,3743790129,3743790130}
tags  | {ref,115,name,塩小路通,lanes,2,oneway,yes,source,Bing,highway,secondary,name:en,"Shio-koji dori",name:ja,塩小路通,name:ko,시오코지도리,maxspeed,40,oneway:bicycle,yes}

osm=# select * from planet_osm_line where osm_id = 174762075;;
-[ RECORD 1 ]------+------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------
osm_id             | 174762075
...
highway            | secondary
...
name               | 塩小路通
...
oneway             | yes
...
ref                | 115
...
z_order            | 36
way_area           |
way                | 0102000020110F0000080000007397286741D36C41A7993C8930C14F41C41EAC8A43D36C41B2B274B630C14F412B5EB73249D36C417
A3ADA3F31C14F410EA803644DD36C41B59C6B8531C14F4173DF29004FD36C41258413E531C14F4151B3B01E51D36C41F6344DB232C14F41B567F1EC51D36C41A
FB7F4E932C14F418AD4EC2353D36C41366D9D9933C14F41

おそらく、planet_osm_line/polygon にある way のカラムは planet_osm_way への逆引きのようなものと思うが詳細は理解しいない。

planet_osm_rels

理解していないので保留

planet_osm_road

おそらく planet_osm_line のサブセット

QGIS で OSMデータを可視化

QGISは下記ダウンロードサイトよりダウンロード&インストールが可能。今回は安定版とされるVersion 3.10を利用する (2020.12現在)

PostGISとの接続

上記の手順通りにDockerを起動したままであれば、ローカル側の5432ポートがDocker内のPostGISの5432ポートにバインディングされている状態となる。QGISの左側ブラウザパネルよりPostGISを選択(右クリック)後、「接続の新規作成...」を選択する。

  • 名前: OSM_KANSAI
  • ホスト: localhost
  • ポート: 5432
  • データベース: osm

として接続テストを押すとユーザ名/パスワードを入力画面がでる。ここでユーザ名「postgres」としてパスワードを空にするとエラーになる.
image.png
よって、Postgres側で postgresユーザにパスワードをセットする。
その後、再びテストすると接続成功するはず。

# PostGISが動作しているDocker内にて
root@55f0d5aec9a0:/work# psql -U postgres
psql (12.5 (Debian 12.5-1.pgdg100+1))
Type "help" for help.

# パスワードをセットする
postgres=# alter role postgres with password 'postgres';
ALTER ROLE

接続完了後、ブラウザパネルPostGISから追加した接続先を展開すると以下のように line, point, polygon, roads が項目として現れる。
image.png

QGIS にて表示

試しに planet_osm_line を選択(ダブルクリック). レイアに追加してみる。
lineにはフェリー航路が含まれているため近畿地方が小さくlineが密集して表示される。
image.png
QGISのズームボタンを調整すると近畿地方が見える
image.png
更に拡大して京都駅周辺を見るとこんな感じになる
image.png
この状態で planet_osm_pointも選択するとこんな感じになる。
image.png

QGIS の地物情報表示

QGISの地物情報の表示機能を選択後、地図上のpointやlineを選択すると各種属性値が確認できる。
image.png

ブラウザパネルの「XYZ Tiles」からOpenStreetMapを選択後、レイヤーにて最下層に配置すると以下のようにOpenStreetMapの地図上にOSMデータを重ねて確認することができる。
image.png

QGISで簡単な地物絞り込み

例えば、上記京都駅周辺で郵便ポストのみを表示してみる。レイヤからplanet_osm_lineのチェックを外し point のみにしたあとに、レイヤの planet_osm_pointを右クリックから「フィルター」を選択。
フィールドの「amenity」を選択後、値の「全て」を押すと、値パネルに planet_osn_pointのamenityカラムに対して SQL でいうところの distinct をした要素のの一覧が表示される。
image.png
フィールドの「amenity」、演算子の「=」、値の「post_box」を順にダブルクリックすると白いフィルタ式の部分が "amenity" = 'post_box'となるはずで、これでOKを押すと地図上のPointが郵便ポストのみになる。(わかりやすくpointのスタイルを大きな赤丸に変更した)
image.png

興味がありましたらこのOSMデータを利用した経路検索エンジンValhallaを利用してみた記事も続きなので参照ください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
7
Help us understand the problem. What are the problem?