プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

避難所検索@伊勢 オフライン用 map, ghz ファイルの自動生成

避難所検索@伊勢で使用する、オフライン用 map, ghz ファイルを自動生成できるようにしたいと思います。

当初はこちらの記事に書いたように、自前のタイルサーバーに最新のデータがあるので、これの上で pbf ファイルを生成しようとしたのですが、調べてみたらちょっと厳しいことがわかりました。

ということで方針を変更して、 Geofabrik 社さんの地域別の pbf データをダウンロードして、mapsforgemap-creatorgraphhopper を動かして、必要なデータを生成する、というやり方とします。

たいそうなことを書いているようですが、やってることは、過去記事の

と基本的には同じです。 とはいえ、バージョンが違うなどで、結構戸惑った部分もあるので、そのあたりを中心に一連の手順をメモっときます。

準備

必要なソフトウェアをインストールしておきます。

osmosis 最新版のインストール

まず、最初に osmosis の最新版をインストールします(理由は後述)。

タイルサーバー立ち上げ作業時に(こちらの記事参照)、apt経由でインストールしていたので、これをいったんアンインストールします。

mor@map:~$ sudo apt purge osmosis

現時点の最新版 0.47 を改めてインストールします。

osm@map:~$ 
osm@map:~$ cd bin/
osm@map:~/bin$ mkdir osmosis-0.47
osm@map:~/bin$ cd osmosis-0.47/
osm@map:~/bin/osmosis-0.47$ wget https://bretth.dev.openstreetmap.org/osmosis-build/osmosis-0.47.tgz
osm@map:~/bin/osmosis-0.47$ 
osm@map:~/bin/osmosis-0.47$ tar zxvf osmosis-0.47.tgz 
osm@map:~/bin/osmosis-0.47$ 

シンボリックリンクを張っておきます。

osm@map:~/bin/osmosis-0.47$ cd ..
osm@map:~/bin$ ln -s osmosis-0.47/bin/osmosis .
osm@map:~/bin$ ls
osm-tile-update-expire-w-mail  osmosis  osmosis-0.47
osm@map:~/bin$ 
osmosis を最新版にした理由

前述したように、このサーバーにはタイルサーバーの更新設定時に apt 経由でインストールしたもの ( 0.44.1) が入っていました。 なので、最初はこれをそのまま利用して、mapsforge の最新版(0.11.0 など)を使おうとすると、

java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom

のような Exception が発生していました。osmosis を最新にしたら解決したのでこのように入れ替えています。

なお、タイルサーバーの更新処理のほうは 0.47 でも問題なく動作しています。

mapsforge の Osmosisプラグインのインストール

mapsforge-creator を使うには、 mapsforge の osmosis プラグインをインストールしておく必要があります。

osmosis のプラグインディレクトリは ~/.openstreetmap/osmosis/plugins/ になります。なければ、作っておきます。 map-writer と poi-writer をwgetでとってくればOKです(jar-with-dependenciesを指定すること)。

osm@map:~$ 
osm@map:~$ cd .openstreetmap/osmosis/plugins/
osm@map:~/.openstreetmap/osmosis/plugins$ 
osm@map:~/.openstreetmap/osmosis/plugins$ wget https://search.maven.org/remotecontent?filepath=org/mapsforge/mapsforge-map-writer/0.11.0/mapsforge-map-writer-0.11.0-jar-with-dependencies.jar -O mapsforge-map-writer-0.11.0-jar-with-dependencies.jar
osm@map:~/.openstreetmap/osmosis/plugins$ 
osm@map:~/.openstreetmap/osmosis/plugins$ wget https://search.maven.org/remotecontent?filepath=org/mapsforge/mapsforge-poi-writer/0.11.0/mapsforge-poi-writer-0.11.0-jar-with-dependencies.jar -O mapsforge-poi-writer-0.11.0-jar-with-dependencies.jar
osm@map:~/.openstreetmap/osmosis/plugins$ ls
mapsforge-map-writer-0.11.0-jar-with-dependencies.jar  mapsforge-poi-writer-0.11.0-jar-with-dependencies.jar

mapsforge-creator のインストール

続いて、mapsforge-creator 自身をインストールします。

osm@map:~/bin$ 
osm@map:~/bin$ git clone https://github.com/mapsforge/mapsforge-creator.git
Cloning into 'mapsforge-creator'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 216 (delta 4), reused 10 (delta 2), pack-reused 201
Receiving objects: 100% (216/216), 61.62 KiB | 0 bytes/s, done.
Resolving deltas: 100% (137/137), done.
Checking connectivity... done.
osm@map:~/bin$ 

master ブランチの最新版を使います。

graphhopper のインストール

graphhopper もインストールしておきます。

osm@map:~/bin$ 
osm@map:~/bin$ git clone https://github.com/graphhopper/graphhopper.git
Cloning into 'graphhopper'...
remote: Enumerating objects: 88369, done.
remote: Total 88369 (delta 0), reused 0 (delta 0), pack-reused 88369
Receiving objects: 100% (88369/88369), 72.96 MiB | 6.62 MiB/s, done.
Resolving deltas: 100% (46622/46622), done.
Checking connectivity... done.
osm@map:~/bin$ 

リリースがタグで指定できるので、最新のリリース (0.12.0) に切り替えます。

osm@map:~/bin$ 
osm@map:~/bin$ cd graphhopper/
osm@map:~/bin/graphhopper$ 
osm@map:~/bin/graphhopper$ git tag
0.10.0
・・・
0.9.0
osm@map:~/bin/graphhopper$ 
osm@map:~/bin/graphhopper$ git checkout 0.12.0
Note: checking out '0.12.0'.
・・・
HEAD is now at 3f1482b... use 0.12-SNAPSHOT
osm@map:~/bin/graphhopper$ 
JDK のインストール

Graphhopper には JDK が必要っぽいので、インストールしておきます。

mor@map:~$ sudo apt install default-jdk

ちなみに、 default-jre / default-jdk ともに openjdk-8 に依存しています。

一度 graphhopper を起動しておきます。graphhopper 以下に maven がインストールされます。

osm@map:~/bin/graphhopper$ 
osm@map:~/bin/graphhopper$ ./graphhopper.sh 
## using java 1.8.0_191 (64bit) from 
## action  not found!
Usage:
-a | --action <action>    must be one the following actions:
     --action import      creates the graph cache only, used for later faster starts
     --action web         starts a local server for user access at localhost:8989 and API access at localhost:8989/route
     --action build       creates the graphhopper web JAR
     --action clean       removes all JARs, necessary if you need to use the latest source (e.g. after switching the branch etc)
     --action measurement does performance analysis of the current source version via random routes (Measurement class)
     --action torture     can be used to test real world routes via feeding graphhopper logs into a GraphHopper system (Torture class)
-c | --config <config>    specify the application configuration
-d | --run-background     run the application in background (detach)
-fd| --force-download     force the download of the OSM data file if needed
-h | --help               display this message
--host <host>             specify to which host the service should be bound
-i | --input <file>       path to the input map file or name of the file to download
--jar <file>              specify the jar file (useful if you want to reuse this script for custom builds)
-o | --graph-cache <dir>  directory for graph cache output
-p | --profiles <string>  comma separated list of vehicle profiles
--port <port>             start web server at specific port
-v | --version            print version
./graphhopper.sh: 行 138: mvn: コマンドが見つかりません
No Maven found in the PATH. Now downloading+installing it to ./maven
--2019-05-01 12:44:33--  http://archive.apache.org/dist/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bin.zip
・・・
(中略)
・・・
osm@map:~/bin/graphhopper$ 

実際に呼び出すときは、 MAVEN_HOME を指定してから呼び出します(スクリプト内で mvn を使っています)。

その他

あと、zipも使うのでインストールしておきます。

mor@map:~$ sudo apt install zip

GDAL はどこかでインストールしたっぽいので、省略します。こちらにもインストール方法がメモしてあるので、ご参考に。

その他、もし不足しているものがあれば、適宜インストールしておきます。

スクリプトの調整

さて、動かす前にスクリプトを調整します。今回は次のような構成を想定しています。

  • ~/bin/osmosis-0.47 : osmosis(環境によってはシステムに入っています)
  • ~/bin/mapsforge-creator : mapsforge-creator
  • ~/bin/graphhopper : Graphhopper
  • ~/bin/offlinemap : オフラインマップファイル作成用スクリプト類
  • ~/work/offlinemap 作業ディレクトリ
  • ~/work/offlinemap/data 作業用データ保存場所
  • ~/work/offlinemap/maps map データ出力場所
  • ~/work/offlinemap/result 最終的なオフライン用データ保存場所

スクリプトを修正する前に作業ディレクトリを作っておきます。

osm@map:~/work$ 
osm@map:~/work$ mkdir offlinemap
osm@map:~/work$ cd offlinemap/
osm@map:~/work/offlinemap$ mkdir data maps result
osm@map:~/work/offlinemap$ 

map-creator スクリプト

最初に map-creator スクリプトを手直しします。

パス設定まわり

mapsforge-creator にある map-creator スクリプトを編集して、パスなどを設定します。

# Configuration

# http://wiki.openstreetmap.org/wiki/Osmosis
#[ $OSMOSIS_HOME ] || OSMOSIS_HOME="$HOME/programs/osmosis"
[ $OSMOSIS_HOME ] || OSMOSIS_HOME="$HOME/bin/osmosis-0.47"

# add by Junichi MORI, 2019/5/1
[ $WORK_HOME ] || WORK_HOME="$HOME/work/offlinemap"

#[ $DATA_PATH ] || DATA_PATH="$HOME/mapsforge/data"
[ $DATA_PATH ] || DATA_PATH="$WORK_HOME/data"

#[ $MAPS_PATH ] || MAPS_PATH="$HOME/mapsforge/maps"
[ $MAPS_PATH ] || MAPS_PATH="$WORK_HOME/maps"

#[ $POIS_PATH ] || POIS_PATH="$HOME/mapsforge/pois"
[ $POIS_PATH ] || POIS_PATH="$WORK_HOME/pois"

[ $PROGRESS_LOGS ] || PROGRESS_LOGS="true"

[ $TAG_VALUES ] || TAG_VALUES="false"

[ $THREADS ] || THREADS="1"

#[ $DAYS ] || DAYS="30"
[ $DAYS ] || DAYS="27"

# added by Junichi MORI, 2019/5/1
SKIP_POI_CREATION="true"
対象領域を限定

map-creator は Geofabrik のダウンロードサイトから pbf データを自動的にダウンロードし、mapファイルを生成してくれます。 Geofabrik が提供している pbf ファイルは、大陸/国/地域 といった分類になっています。例えば、日本だと、三重県を含むデータは、

asia/japan/kansai

という指定になります。kansai は関西地方なので、伊勢近辺のデータのみが欲しい場合は範囲が大きすぎます。

なので、map-creator を一部修正してあらかじめ指定した領域を対象に動作するように変更します。

起動引数を下記のように変更します。

if [ $# -lt 2 ]; then
  #echo "Usage: $0 continent/country[/region] ram|hd [lang,...]"
  #echo "Example: $0 europe/germany/berlin ram en,de,fr,es"
  echo "Usage: $0 continent/country[/region] ram|hd [lang,...] [poly_file]"
  echo "Example: $0 europe/germany/berlin ram en,de,fr,es ise.poly"
  exit
fi

4番目の引数として、領域を指定するファイルを指定可能とします。ただし、この場合、必ず3番目の引数を指定していることを前提としています(面倒だったのでスクリプトは修正していません)。

省略可能引数が多くなるので、2番目の引数(ram|hd)のチェックを追加しておきます。

POIS_PATH="$(dirname "$POIS_PATH/$1")"

# check 2nd argument
if [ $2 != "ram" -a $2 != "hd" ]; then
  echo "2nd argument is invalid. must be ram or hd. : " $2
  exit
fi

# Check map date

要は2番目の引数の指定を忘れたときへの対応です。まあ、なくてもどこかで落ちるんですけどね。

4番目の引数がある場合は指定ファイルを用い、ない場合は pbfファイルと一緒にダウンロードされる poly ファイル(kansai を指定していれば、 kansai.poly)をもとに処理対象領域を決定します。

# modified by Junichi MORI, 2019/5/1
# Bounds

[ $4 ] && POLY_FILE="$WORK_HOME/$4" || POLY_FILE="$WORK_PATH/$NAME.poly"
echo "Poly file is " $POLY_FILE

#BBOX=$(perl poly2bb.pl "$WORK_PATH/$NAME.poly")
BBOX=$(perl poly2bb.pl "$POLY_FILE")
BBOX=(${BBOX//,/ })
BOTTOM=${BBOX[0]}
LEFT=${BBOX[1]}
TOP=${BBOX[2]}
RIGHT=${BBOX[3]}

# Start position

# modified by Junichi MORI, 2019/5/1
#CENTER=$(perl poly2center.pl "$WORK_PATH/$NAME.poly")
CENTER=$(perl poly2center.pl "$POLY_FILE")

pbf ファイルから対象領域を取り出す処理を追加します。

# added by Junichi MORI, 2019/5/1
# extract only bounding box
BOUNDED_PBF="$NAME.pbf"
echo $BOUNDED_PBF

CMD="$OSMOSIS_HOME/bin/osmosis --rb file=$WORK_PATH/$NAME-latest.osm.pbf \
                 --bb left=$LEFT right=$RIGHT top=$TOP bottom=$BOTTOM completeWays=yes \
                 --wb file=$WORK_PATH/$BOUNDED_PBF"
echo $CMD
$CMD

# Land

osmosis の引数として completeWays=yes を指定しているのは、後続の graphhopper にて領域の外側にwayが広がっている場合に エラーとならないようにするためです。

あとは、後続する処理において、領域を限定したファイルを使うように変更します。

# Merge

# modified by Junichi MORI, 2019/5/1
#CMD="$OSMOSIS_HOME/bin/osmosis --rb file=$WORK_PATH/$NAME-latest.osm.pbf \
#                               --rx file=$WORK_PATH/sea.osm --s --m"
CMD="$OSMOSIS_HOME/bin/osmosis --rb file=$WORK_PATH/$BOUNDED_PBF \
                               --rx file=$WORK_PATH/sea.osm --s --m"
if [ "$SKIP_POI_CREATION" != "true" ]; then
  # modified by Junichi MORI, 2019/5/1
  #CMD="$OSMOSIS_HOME/bin/osmosis --rb file=$WORK_PATH/$NAME-latest.osm.pbf \
  #                               --pw file=$WORK_PATH/$NAME.poi \
  #                                    progress-logs=$PROGRESS_LOGS"
  CMD="$OSMOSIS_HOME/bin/osmosis --rb file=$WORK_PATH/$BOUNDED_PBF \
                                 --pw file=$WORK_PATH/$NAME.poi \
                                      progress-logs=$PROGRESS_LOGS"

元々のスクリプトは、処理が終了するときに、pbfファイルなどの中間ファイルを削除していましたが、後続のgraphhopperでpbfファイルを利用するため、削除を外しておきます。

# Post-process

#rm -rf "$WORK_PATH"

こちらに、ファイル全体を載せときますので、気になる方はご参考にしてください。

起動スクリプト

次に、 map-creator と graphhopper を起動するスクリプトなど、および全体を制御するスクリプトを作成します。 bin/offlinemap に以下のファイルを作成しました。

  • bin/offlinemap/doMapCreator : map-creator 実行
  • bin/offlinemap/doGh : graphhopper 実行(およびghzファイルに圧縮)
  • bin/offlinemap/uploadS3 : S3 へのアップロードを実行
  • bin/offlinemap/createOfflineMap : 全体を実行

個別のスクリプトは特別なことは特にしていませんないので、気になる方は、gistを見てください。 これで、一連のファイルを自動で作ることができます。

動作テスト

まず、動作テストの前に設定ファイルを準備します。

次いで、map-creator と graphhopper.sh をそれぞれ個別に動かしてから、全体を通して動かしてみます。

設定ファイルの準備

作業ディレクトリ直下に、領域を限定するためのファイル(以前、避難所検索@伊勢のオフラインファイル作成時に作ったやつを流用 ise.poly)とgraphhopperの設定ファイル(gh_config.yml)を作成します。

graphhopperの設定ファイルは、graphhopper を clone したディレクトリ(bin/graphhopper)に config-example.yml があるので、これをコピーします。

今回の場合は、災害時の避難所マップに使うのが目的なので、先頭の

  # Possible options: car,foot,bike,bike2,mtb,racingbike,motorcycle (comma separated)
  # bike2 takes elevation data into account (like up-hill is slower than down-hill) and requires enabling graph.elevation.provider below
  graph.flag_encoders: car

car とある部分を、 foot に変更します。

  # Possible options: car,foot,bike,bike2,mtb,racingbike,motorcycle (comma separated)
  # bike2 takes elevation data into account (like up-hill is slower than down-hill) and requires enabling graph.elevation.provider below
  graph.flag_encoders: foot

あと、map-creator を動かした場合に、JVMのメモリが足りなくて落ちることがあります。これへの対策として、 ~/.osmosis にJVMのオプションを書いておきます。

osm@map:~$ cat .osmosis 
export JAVACMD_OPTIONS="-Xmx6G"
osm@map:~$ 

これで、準備OKです。

map-creator の動作

map-creator の動作について述べておきます。

前述したように map-creator のスクリプトは、引数で指定された国・地域について、Geofabrik が提供する国別・地域別の pbf ファイルを自動でダウンロードして、mapファイルを作成する、という動作をしています(なので、引数の値は Geofabrikのダウンロードサイトで用意されている地域にしてください)。

日本の場合は、こちらのページをみるどこ地方があるのかわかります。

なので、直接呼び出し、対象領域を制限しない場合は

osm@map:~/bin/mapsforge-creator$ ./map-creator asia/japan/kansai ram

のようにします(kansaiを選んでいるのは伊勢近辺はここのデータに含まれているためです)。

処理対象領域を限定する場合は、スクリプトで指定した作業ディレクトリ(今回の場合は、 ~/work/offlinemap)に領域を指定する ise.poly ファイルを置き、引数でこれを指定します。

osm@map:~/bin/mapsforge-creator$ ./map-creator asia/japan/kansai ram ja ise.poly

どちらの場合も、例外が表示されず、処理が終了し、 map データ出力場所(~/work/maps/v4/asia/japan)に kansai.mapファイルが作成されていれば、OKです。 なお、初回起動時はファイルのダウンロードにかなりの時間がかかりますので、ご注意ください。

また、領域を指定した場合は、

2019-05-01 15:29:50 URL:https://download.geofabrik.de/asia/japan/kansai.poly [15329/15329] -> "/home/osm/work/offlinemap/data/asia/japan/kansai/kansai.poly" [1]
Poly file is  /home/osm/work/offlinemap/ise.poly
kansai.pbf
/home/osm/bin/osmosis-0.47/bin/osmosis --rb file=/home/osm/work/offlinemap/data/asia/japan/kansai/kansai-latest.osm.pbf --bb left=136.417000 right=137.028000 top=34.685000 bottom=34.277000 completeWays=yes --wb file=/home/osm/work/offlinemap/data/asia/japan/kansai/kansai.pbf

のようにpbfファイルの領域を区切っているコマンドも表示されます。

なお、ise.poly ファイルに記載された緯度経度(のそれぞれ東西南北の端の値)を見ると、上記コマンドで表示される値が微妙にずれていることがわかります。 これは、領域を指定したファイルを囲む矩形を求める際に、poly2bb.pl スクリプトで 0.1 deg 分マージンがとられるためです。

結果のファイルは、領域を限定しない場合、

osm@map:~/work$ 
osm@map:~/work$ ls -l offlinemap/maps/v4/asia/japan/kansai.map 
-rw-rw-r-- 1 osm osm 128542439  51 15:17 offlinemap/maps/v4/asia/japan/kansai.map
osm@map:~/work$ 

おおよそ、130 MB ぐらいですね。

伊勢市近辺に限定した場合は

osm@map:~/work$ 
osm@map:~/work$ ls -l offlinemap/maps/v4/asia/japan/kansai.map
-rw-rw-r-- 1 osm osm 4728762  51 15:33 offlinemap/maps/v4/asia/japan/kansai.map
osm@map:~/work$ 

約5MB弱ぐらいですね。ずいぶんと小さくなっており、いい感じです。 これなら、スマホに載せることも十分できそうです。

graphhopper の動作

初回 graphhopper.sh を起動すると、graphhopper.jar のビルドも同時に行われます。

osm@map:~/bin/offlinemap$ ./doGh 
target output dir : /home/osm/work/offlinemap/data/asia/japan/kansai/kansai-gh
target output file: kansai.ghz
/home/osm/bin/graphhopper/graphhopper.sh --action import --input /home/osm/work/offlinemap/data/asia/japan/kansai/kansai.pbf --config /home/osm/work/offlinemap/gh_config.yml
## using java 1.8.0_191 (64bit) from 
## using existing osm file /home/osm/work/offlinemap/data/asia/japan/kansai/kansai.pbf
## building graphhopper jar: web/target/graphhopper-web-0.12-SNAPSHOT.jar
## using maven at /home/osm/bin/graphhopper/maven
## now import. JAVA_OPTS=-Xmx1000m -Xms1000m

エラーもなく処理が終了すると

osm@map:~/work$ 
osm@map:~/work$ ls -l offlinemap/data/asia/japan/kansai/
合計 187504
drwxrwxr-x 2 osm osm      4096  51 15:56 kansai-gh
-rw-rw-r-- 1 osm osm 169917967  51 08:45 kansai-latest.osm.pbf
-rw-rw-r-- 1 osm osm        56  51 09:23 kansai-latest.osm.pbf.md5
-rw-rw-r-- 1 osm osm   7395204  51 15:32 kansai.pbf
-rw-rw-r-- 1 osm osm     15329  51 08:43 kansai.poly
-rw-rw-r-- 1 osm osm     19985  51 15:33 land.dbf
-rw-rw-r-- 1 osm osm       143  51 15:32 land.prj
-rw-rw-r-- 1 osm osm    889172  51 15:33 land.shp
-rw-rw-r-- 1 osm osm     13380  51 15:33 land.shx
-rw-rw-r-- 1 osm osm   7118778  51 15:33 land1.osm
-rw-rw-r-- 1 osm osm   6606690  51 15:33 merge.pbf
-rw-rw-r-- 1 osm osm       857  51 15:33 sea.osm
osm@map:~/work$ 
osm@map:~/work$ ls -l offlinemap/data/asia/japan/kansai/kansai-gh/
合計 17036
-rw-rw-r-- 1 osm osm 3145828  51 15:56 edges
-rw-rw-r-- 1 osm osm 2097252  51 15:56 geometry
-rw-rw-r-- 1 osm osm 4795480  51 15:56 kansai.ghz
-rw-rw-r-- 1 osm osm 1048676  51 15:55 location_index
-rw-rw-r-- 1 osm osm 1048676  51 15:56 names
-rw-rw-r-- 1 osm osm 1048676  51 15:56 nodes
-rw-rw-r-- 1 osm osm 1048676  51 15:56 nodes_ch_fastest_foot_node
-rw-rw-r-- 1 osm osm   32868  51 15:56 properties
-rw-rw-r-- 1 osm osm 3145828  51 15:56 shortcuts_fastest_foot_node
osm@map:~/work$ 

のように、kansai.ghz というファイルが作成されます。

全体の動作

それぞれのスクリプトが問題なく動作していそうなので、全体処理を動かしてみます。

osm@map:~$ 
osm@map:~$ bin/offlinemap/createOfflineMap 

ダウンロード等で時間がかかりますが、何もなければ処理が終了します。

~/work/offlinemap/result に ise.map, ise.ghz ファイルが作成されていればOKです。

Android アプリに組み込んで動作確認

避難所検索@伊勢 に組み込んで表示させてみます。

インストール時に使用するオフライン用データは asset に入れてあるので、これを上記で作成したファイルと入れ替えて起動してみます。

でも、残念ながら、落ちます。LogCat で見ると

2019-05-02 10:32:24.280 22133-22133/? E/GraphHopperWrapper: Graphhopper file load failed
    java.lang.IllegalArgumentException: Encoder foot was used in version 3, but current version is 5
        at com.graphhopper.routing.util.EncodingManager.parseEncoderString(EncodingManager.java:271)
        at com.graphhopper.routing.util.EncodingManager.createBuilder(EncodingManager.java:80)
        at com.graphhopper.routing.util.EncodingManager.create(EncodingManager.java:137)
        at com.graphhopper.GraphHopper.load(GraphHopper.java:708)
        at com.mori_soft.escape.model.GraphHopperWrapper.prepareGraphHopper(GraphHopperWrapper.java:55)
        at com.mori_soft.escape.model.GraphHopperWrapper.getInstance(GraphHopperWrapper.java:46)
        at com.mori_soft.escape.model.NearestShelterAsynkTaskLoader.<init>(NearestShelterAsynkTaskLoader.java:50)
        at com.mori_soft.escape.MapFragment$NearestShelterLoaderCallbacks.onCreateLoader(MapFragment.java:367)
        at android.app.LoaderManagerImpl.createLoader(LoaderManager.java:556)
        at android.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:565)
        at android.app.LoaderManagerImpl.restartLoader(LoaderManager.java:715)
        at com.mori_soft.escape.MapFragment.searchNearShelter(MapFragment.java:266)
        at com.mori_soft.escape.MapFragment.updateCurrentLocation(MapFragment.java:257)
        at com.mori_soft.escape.MainActivity$6.onLocationResult(MainActivity.java:272)
        at com.google.android.gms.internal.location.zzau.notifyListener(Unknown Source:4)
        at com.google.android.gms.common.api.internal.ListenerHolder.notifyListenerInternal(Unknown Source:17)
        at com.google.android.gms.common.api.internal.ListenerHolder$zaa.handleMessage(Unknown Source:5)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

というログが出力されていました。 どうも graphhopper のバージョン違いのため(encoder foot のバージョン違い)、経路情報ファイル(ghz)が読み込めなくて、それで落ちているようです。

app/build.gralde の graphhopper ライブラリを 0.12.0 に上げて試してみます。すると今度はビルド自体が落ちます。

ここで、いろいろと試行錯誤したのですが、結論からいうと、 gradle /gradle plugin を 4.10.1 / 3.3.2 に上げて再度コンパイルしたら問題なくビルドできました。

この状態にして、上記で作ったファイルを Android アプリに組み込んで表示させると、問題なく動作しました。 なので、オフライン用ファイルを生成は問題なくできていそうです。

オフラインファイルを S3 にアップロード

あと一息です。 作成したオフラインファイルを S3 にアップロードするようにします。

AWS CLI をインストール

osm@map:~$ pip3 install awscli --upgrade --user

つぎに、AWSのマネージメントコンソールで、オフライン用ファイルを公開するバケットを作成します。また、この公開用バケットに書き込み許可があるIAMユーザーを作成します。

サーバーに戻り、IAM ユーザーのアクセスキー等を設定します。

osm@map:~$ aws configure --profile=upload-user-name
AWS Access Key ID [None]: xxxxxxxxxxxxxxxxxxxxxx
AWS Secret Access Key [None]:  xxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: ap-xxxxxxx-x
Default output format [None]:  json

アップロード用のスクリプトは前述のgistにありますので、参考にしてください。

動作させてみて、問題なくアップロードできることを確認しておきます。

cron による定期実行

最後に、ここまでの一連の処理を cron で定期実行できるようにしておきます。

# The first element of the path is a directory where the debian-sa1
# script is located
PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/home/osm/bin:/home/osm/.local/bin

# run createOfflineMap at am 6:01 1st day of every month
1 6 1 * * osm /home/osm/bin/offlinemap/createOfflineMap >> /home/osm/work/offlinemap/log/run.log 2>&1

これだと、毎月1日にオフラインマップの作成とアップロードが行われることになります。

オフライン用マップファイルもこれで定期的に更新されるようになりました。めでたしめでたし。

あれ?なんか忘れてない?

さいごに

そうです。まだアプリ側が対応できていません。 ということで、アプリ側で最新のオフラインファイルをチェックして、ダウンロードする機能を追加しないといけません。

ま、これはまたおいおい対応したいと思います。

あとですね。

当初の流れで、タイルサーバー上に動作環境を作りましたが、(タイルサーバーの持つ最新のデータを使うという当初の目論見が外れたので)別段ここで動かす必然性はありません。

ということで、アプリ側の対応が終わったら、ここまでの一連の処理をコンテナ化してクラウド上にでも持ってこうと思います。

今年のGW終わるまでに全部終わるかな?

NVR510 の不正アクセス検知のパケット破棄とタイルサーバー公開

こちらの記事で書いたように OpenStreetMap のタイルサーバーを運用しています。

先日、なんの気なしに、このタイルサーバーを使っているWebサイトを見に行くと、地図が表示されていません。 タイルサーバー側で特に変更もしていないので、何か不具合でも起こったかな?と思って調べてみたら、いろいろとはまったので、解決するまでの顛末をメモっときます。

問題の切り分け

まずはタイルサーバー自体に問題ないか調べました。sshで接続して、プロセスやサーバー上のログ等を見ても特に問題点は見当たりません。

でも、さきほどのWebサイトを見るとやっぱり地図は表示されません。念のため、同じLAN内のPCからローカルIPアドレスで接続すると、問題なく表示されます。

あれ?どういうことだ?

こうなるということは、サーバーの問題ではなく、外部からのアクセス(DNSかルータ)が怪しい。 ということで、もうちょっと調べると、ルータが原因でした。

ルータの設定内容の確認

最初に結論を書いておくと、

  • LAN内のWebサーバーを公開(今回は80以外のポートでした)
  • 不正アクセス検知のパケット破棄を有効

とした時に問題が起きました。

どうも、この設定だと、外部からのアクセスができなくなるようです。 実際、不正アクセス検知のパケット破棄を無効にすると、外部からもアクセス可能になりました。

そういえば、少し前にルータから不正アクセス検知があったと通知が来ていて、その際になにも考えずに、『パケット破棄』を有効にしたことを思い出しました。 これが原因だったんですね。

なお、設定していた内容は次の通りです。

Web サーバーの公開方法

Webサーバーの公開設定はこちらの記事にあるようにしています。具体的には次の通りです。

IPマスカレード設定 f:id:junichim:20190320141415p:plain

NATディスクプリタのインターフェースへの適用 f:id:junichim:20190320141526p:plain

受信方向のIPフィルタ設定 f:id:junichim:20190320141702p:plain

NATディスクプリタでIPマスカレードを設定し、LAN内のWebサーバーに転送しています。同時に、受信方向のIPフィルタを定義して、ルータを通過させています。 IPフィルタは、NATディスクプリタを設定すると、自動で定義してくれるやつをそのまま使っています。

不正アクセス検知とパケットの破棄

パケット破棄は、ルータの管理画面の『詳細設定』より、『セキュリティ』->『不正アクセス検知』と進み、『設定』ボタンを押して表示される画面で指定することができます。

f:id:junichim:20190320142243p:plain

画面上部の『検知したパケットの破棄』を選択すると、すべての場合でパケット破棄を指定することができます。

設定後はこんな感じになります(すべての場合でパケットを破棄にしました)。

f:id:junichim:20190320141929p:plain

詳細な原因

さて、画面上の機能の説明からすると、直感的にはこれは不思議な挙動です。 ということで、よくわからなかったこともあり、ヤマハのサポートさんに聞いてみたところ、いろいろと懇切丁寧に教えていただけました。

それは、上記の不正アクセス検知とパケット破棄の設定の一つとして、WinnyとShareのパケットを破棄する設定があるのですが、それを設定したことにより、タイルサーバーの通信も破棄していた、というものでした。

どういうことか理解するために、Winnyのパケット破棄がどのように行われているか調べてみます。

ちなみに、あとからルータのヘルプページを見てみると、不正アクセス検知のパケット破棄を有効にするとIPフィルタが変更されるとちゃんと書いてました。最初からこれに気づいていれば、もうちょっと解決が早かったかもしれません。

f:id:junichim:20190320153609p:plain

Winnyのパケット破棄について

Winnyのパケット破棄を設定した際には、静的フィルタで外部への通信をすべて遮断します。 そのうえで、動的フィルタにて内側->外側の通信を許可するようにしているようです。

静的フィルタの設定はこんな感じです(ルータの管理画面から『IPフィルタ』の『送信方向のフィルタ』を選択すると見ることができます)。

f:id:junichim:20190320145610p:plain

通信開始で使われる ACK を持った外部へのパケットを破棄していることがわかります。一方、通信開始時一番最初の SYN は通しています。

動的フィルタは、tcpを対象に外向き方向に設定しています。最初に内側から外側に通信が開始されて、それが静的フィルタで通過されると、次からは動的フィルタが先に適用されて、通信が許可される、という流れだそうです。Winnyの検知はこの動的フィルタの動作のタイミングで行われるようです。

(参考:IPフィルタについて)

動作確認

この動作を確認するためにログを見てみました。

まず、送信時のIPフィルタ設定(上記の 200098)を変更して、 reject-nolog から reject に変更しログに記録させます。

次に、syslog がデフォルト設定のままだと、破棄されたパケットを見ることができないので、ルータの管理画面より『管理』->『保守』->『コマンドの実行』を開いて、

syslog notice on

と実行し、 notice レベルのログを吐くようにします(コンソールでもできます)。

f:id:junichim:20190320144941p:plain

これで、タイルサーバーにアクセスすると

2019/03/20 11:24:17: Configuration saved in "CONFIG0" by HTTPD
2019/03/20 11:24:24: [INSPECT] PP[01][out][動的フィルタID] UDP 192.168.xxx.xxx:aaaaa > 送信先アドレス・ポート (2019/03/20 11:23:54)
2019/03/20 11:24:26: PP[01] Rejected at OUT(200098) filter: TCP 192.168.yyy.yyy:bbbbb > 送信先アドレス・ポート
2019/03/20 11:24:28: PP[01] Rejected at OUT(200098) filter: TCP 192.168.zzz.zzz:cccccc > 送信先アドレス・ポート
2019/03/20 11:24:29: PP[01] Rejected at OUT(200098) filter: TCP 192.168.zzz.zzz:cccccc > 送信先アドレス・ポート

のように、静的フィルタ(ID番号 200098)により、パケットが破棄されていることがわかります。

なお、この際、動的フィルタがあるんだから、通信できないのだろうか?と考えたのですが、よく考えると、この設定の場合、

  • 内から外へ通信を開始る際に、パケットを通過させる

ということなので、最初に外から来たタイルサーバーへのアクセス(に対応する応答のパケット)はこのIPフィルタで落とされてしまうんじゃないかと思ってます(合ってんのかな?)。

なお、ログの内容については、下記などを参考にしてください。

(参考:ログ設定など)

(参考:ログの見方)

回避策

ということで、一応原因がわかりましたので、回避策を取ります。とえらそうに書いてますが、これも、ヤマハのサポートさんに教えていただきました。重ね重ねありがとうございます。

やることは、送信時のフィルタ設定で、当該タイルサーバーから外に戻るパケットを明示的に許可してあげればよい、というものです。

実際の設定方法は、

f:id:junichim:20190320150437p:plain

のように、パケットを破棄するフィルタ設定の手前に、タイルサーバーからの通信(IPアドレス、ポート番号を指定)を通すためのフィルタを追加してやります。

テスト

ということで、この状態でテストすると、無事表示されました。 一方、ルータの設定で不正アクセス検知のパケット破棄も生きたままになっています。

いや、なかなか難しいですね。

OpenStreetMap タイルサーバーで pbf ファイル生成に挑戦 (失敗)

最初に結論を書いておきます。

こちらの記事に書いた osm2pgsql を利用して立ち上げたタイルサーバーでは、pbfファイルを作るのは非常に難しいです。

以下は、この結論になるに至るまでに調べたこととかを備忘録代わりに書いておきます。

動機

さて、まず、タイルサーバーでpbfファイルを作りたいなと思った動機です。

避難所検索@伊勢というオフラインマップを利用したAndroidアプリをリリースしているのですが、このアプリで使うオフライン用の地図データが、 pbf ファイルをもとにして、map ファイルという形で作成されます( なお、作り方については、こちらの記事などを参照してください)。

この、pbfファイルは、通常のOpenStreetMap のデータファイル(.osmファイル、xml形式)をバイナリでまとめたものになります。 タイルサーバーは常に最新の地図データになるように更新しているので、これをもとにpbfファイルを作れれば、簡単に最新のオフライン用地図データを作れるんじゃないかな?と思ったのが動機です。

最初のトライ

OpenStrretMap の osmosis のドキュメントに --read-apidb ってオプションを使って xml ファイルに書き出す例が載ってるので、それに従って試してみます。 この時、pbf で出力したいなら、 --write-pbf オプションを指定すればいいみたい。

Creating a Custom PBF File

試してみると

osm@map:~$ osmosis --read-apidb database="gis" user="osm"  --write-pbf file="jm.osm.pbf"
1 25, 2019 11:22:54 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Osmosis Version 0.44.1
1 25, 2019 11:22:55 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Preparing pipeline.
1 25, 2019 11:22:55 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Launching pipeline execution.
1 25, 2019 11:22:55 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Pipeline executing, waiting for completion.
1 25, 2019 11:22:55 午前 org.openstreetmap.osmosis.core.pipeline.common.ActiveTaskManager waitForCompletion
重大: Thread for task 1-read-apidb failed
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (サーバーは、パスワード・ベースの認証を要求しました、しかし、いかなるパスワードも提供されませんでした。)
        at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:243)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:128)
        at org.openstreetmap.osmosis.apidb.common.DatabaseContext2.executeWithinTransaction(DatabaseContext2.java:89)
        at org.openstreetmap.osmosis.apidb.v0_6.ApidbReader.run(ApidbReader.java:105)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (サーバーは、パスワード・ベースの認証を要求しました、しかし、いかなるパスワードも提供されませんでした。)
        at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1551)
        at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1390)
        at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1046)
        at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:202)
        ... 5 more
Caused by: org.postgresql.util.PSQLException: サーバーは、パスワード・ベースの認証を要求しました、しかし、いかなるパスワードも提供されませんでした。
        at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:444)
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:173)
        at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:64)
        at org.postgresql.jdbc2.AbstractJdbc2Connection.<init>(AbstractJdbc2Connection.java:136)
        at org.postgresql.jdbc3.AbstractJdbc3Connection.<init>(AbstractJdbc3Connection.java:29)
        at org.postgresql.jdbc3.Jdbc3Connection.<init>(Jdbc3Connection.java:24)
        at org.postgresql.Driver.makeConnection(Driver.java:393)
        at org.postgresql.Driver.connect(Driver.java:267)
        at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
        at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
        at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1558)
        at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1547)
        ... 8 more

1 25, 2019 11:22:55 午前 org.openstreetmap.osmosis.core.Osmosis main
重大: Execution aborted.
org.openstreetmap.osmosis.core.OsmosisRuntimeException: One or more tasks failed.
        at org.openstreetmap.osmosis.core.pipeline.common.Pipeline.waitForCompletion(Pipeline.java:146)
        at org.openstreetmap.osmosis.core.Osmosis.run(Osmosis.java:92)
        at org.openstreetmap.osmosis.core.Osmosis.main(Osmosis.java:37)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchStandard(Launcher.java:328)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:238)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:408)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:351)
        at org.codehaus.classworlds.Launcher.main(Launcher.java:31)

osm@map:~$ 

Exceptionの山。

何が悪いんだろうか?と思って、ネットであれこれ調べると read-pgsql ってオプションもあることがわかりました。

xml - Using osmosis to convert POSTGIS Table to .OSM - Stack Overflow

なので、これに切り替えて試すも、結果は同じ、Exceptionの山。 その後、host, user, password とかを変えていろいろと試してみたけど、一向にできません。

原因を調べる

Exception をよく見ると、認証で引っかかっている模様です。 そういえば、こちらの記事でセットアップしたのだけど、Postgres のユーザーにはパスワードを付けた覚えがありません。

でも、下記のリファレンスを見ても、--read-apidb で特段のオプション指定がないと、パスワードなしで接続する模様です。

https://wiki.openstreetmap.org/wiki/Osmosis/Detailed_Usage_0.47#--read-apidb_.28--rd.29

さて、何が起こっているんだろうか?

考えてみる

ちょっと立ち止まって、少し考えてみました。

psql でデータベースに接続するときは、osmユーザー(OSのユーザー)でログインしていれば、

psql -d gis

のようにデータベース名だけ指定すれば問題なく接続できます。これって、なんででしょうか?

これは、PostgreSQL の認証周りを調べないとよくわからないぞ。 ということで、PostgreSQL の認証周りを調べてみることにしました。

PostgreSQL の認証

どこから見つけた情報かはもう忘れてしまいましたが、 pg_hba.conf というファイルでユーザー認証を制御しているらしいということがわかりました。

19.1. pg_hba.confファイル

今のタイルサーバーだと、 /etc/postgresql/9.5/main に pg_hba.conf というファイルがありました。

で、デフォルトの設定を確認すると

# Database administrative login by Unix domain socket
local   all             postgres                                peer

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

となっていました。

この設定内容がどういうことなんだろうかと、わからないながらマニュアルと見比べていくと、 ネットワーク経由の場合(hostの行が該当)は、md5認証(パスワード認証の一種)になっており、 パスワードが未設定の場合は、常にPostgreSQLへの接続に失敗する、と書いてありました。

19.3.2. パスワード認証

これが原因でしょう、きっと。

一方、psqlでの接続は、localが使われてるっぽい。なので、Unixユーザーがosmに切り替わっていれば、peerで認証されるので、そのままPostgresqlのユーザーとしてosmが使われ、 パスワードなしだから、接続できる、ってことのようです。

19.3.6. Peer認証

認証方法の変更(pg_hba.conf の編集)

さて、osmosis での接続時は -h でホストを指定するので常にネットワーク経由での接続じゃないかと推測されます。なので、この設定ファイルを変更してやればよさげです。 ちなみに、dbユーザーにパスワードを付けるという選択肢もあるけど、タイルサーバーの更新スクリプト周りも変えることになるとはまりそうなので、こっちは採用しませんでした。

ということで、pg_hba.conf を編集します。

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    gis             osm             127.0.0.1/32            trust
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

host 接続の対象データベースが gis でユーザーが osm の場合のみ信頼するように trust を付けました。

変更内容を有効にするため、postgreSQL を再起動します。

mor@map:/etc/postgresql/9.5/main$ sudo /etc/init.d/postgresql restart
[ ok ] Restarting postgresql (via systemctl): postgresql.service.
mor@map:/etc/postgresql/9.5/main$ 

これで再度試してみます。

二回目のトライ

さて、再度試してみましたが、結果は変わらず Exception の山。

osm@map:~$ osmosis --read-apidb database="gis" user="osm"  --write-pbf file="jm.osm.pbf"
1 25, 2019 11:48:21 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Osmosis Version 0.44.1
1 25, 2019 11:48:22 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Preparing pipeline.
1 25, 2019 11:48:22 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Launching pipeline execution.
1 25, 2019 11:48:22 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Pipeline executing, waiting for completion.
1 25, 2019 11:48:22 午前 org.openstreetmap.osmosis.core.pipeline.common.ActiveTaskManager waitForCompletion
重大: Thread for task 1-read-apidb failed
org.openstreetmap.osmosis.core.OsmosisRuntimeException: Unable to create resultset.
        at org.openstreetmap.osmosis.apidb.common.DatabaseContext.executeQuery(DatabaseContext.java:429)
        at org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator.validateDBVersion(SchemaVersionValidator.java:82)
        at org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator.validateVersion(SchemaVersionValidator.java:55)
        at org.openstreetmap.osmosis.apidb.v0_6.ApidbReader.runImpl(ApidbReader.java:74)
        at org.openstreetmap.osmosis.apidb.v0_6.ApidbReader$1.doInTransactionWithoutResult(ApidbReader.java:110)
        at org.springframework.transaction.support.TransactionCallbackWithoutResult.doInTransaction(TransactionCallbackWithoutResult.java:33)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:131)
        at org.openstreetmap.osmosis.apidb.common.DatabaseContext2.executeWithinTransaction(DatabaseContext2.java:89)
        at org.openstreetmap.osmosis.apidb.v0_6.ApidbReader.run(ApidbReader.java:105)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.postgresql.util.PSQLException: ERROR: relation "schema_migrations" does not exist
  ポジション: 21
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1886)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:555)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:403)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:283)
        at org.openstreetmap.osmosis.apidb.common.DatabaseContext.executeQuery(DatabaseContext.java:424)
        ... 9 more

1 25, 2019 11:48:22 午前 org.openstreetmap.osmosis.core.Osmosis main
重大: Execution aborted.
org.openstreetmap.osmosis.core.OsmosisRuntimeException: One or more tasks failed.
        at org.openstreetmap.osmosis.core.pipeline.common.Pipeline.waitForCompletion(Pipeline.java:146)
        at org.openstreetmap.osmosis.core.Osmosis.run(Osmosis.java:92)
        at org.openstreetmap.osmosis.core.Osmosis.main(Osmosis.java:37)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchStandard(Launcher.java:328)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:238)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:408)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:351)
        at org.codehaus.classworlds.Launcher.main(Launcher.java:31)

osm@map:~$ 

ただ、接続はできたっぽくて、Exception の中身が違ってきてます。

一応、--read-pgsqlも試してみると、

osm@map:~$ osmosis --read-pgsql database="gis" user="osm" --dataset-dump  --write-pbf file="jm.osm.pbf"
1 25, 2019 11:49:28 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Osmosis Version 0.44.1
1 25, 2019 11:49:28 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Preparing pipeline.
1 25, 2019 11:49:28 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Launching pipeline execution.
1 25, 2019 11:49:28 午前 org.openstreetmap.osmosis.core.Osmosis run
情報: Pipeline executing, waiting for completion.
1 25, 2019 11:49:28 午前 org.openstreetmap.osmosis.core.pipeline.common.ActiveTaskManager waitForCompletion
重大: Thread for task 1-read-pgsql failed
org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [SELECT version FROM schema_info]; nested exception is org.postgresql.util.PSQLException: ERROR: relation "schema_info" does not exist
  ポジション: 21
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:237)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:407)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:456)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:464)
        at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:472)
        at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:477)
        at org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator.validateDBVersion(SchemaVersionValidator.java:64)
        at org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator.validateVersion(SchemaVersionValidator.java:47)
        at org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.PostgreSqlDatasetContext.initialize(PostgreSqlDatasetContext.java:99)
        at org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.PostgreSqlDatasetContext.iterate(PostgreSqlDatasetContext.java:197)
        at org.openstreetmap.osmosis.dataset.v0_6.DumpDataset.process(DumpDataset.java:48)
        at org.openstreetmap.osmosis.pgsnapshot.v0_6.PostgreSqlDatasetReader.run(PostgreSqlDatasetReader.java:53)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.postgresql.util.PSQLException: ERROR: relation "schema_info" does not exist
  ポジション: 21
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1886)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:555)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:403)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:283)
        at org.apache.commons.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:209)
        at org.apache.commons.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:209)
        at org.springframework.jdbc.core.JdbcTemplate$1QueryStatementCallback.doInStatement(JdbcTemplate.java:441)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:396)
        ... 11 more

1 25, 2019 11:49:29 午前 org.openstreetmap.osmosis.core.Osmosis main
重大: Execution aborted.
org.openstreetmap.osmosis.core.OsmosisRuntimeException: One or more tasks failed.
        at org.openstreetmap.osmosis.core.pipeline.common.Pipeline.waitForCompletion(Pipeline.java:146)
        at org.openstreetmap.osmosis.core.Osmosis.run(Osmosis.java:92)
        at org.openstreetmap.osmosis.core.Osmosis.main(Osmosis.java:37)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchStandard(Launcher.java:328)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:238)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:408)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:351)
        at org.codehaus.classworlds.Launcher.main(Launcher.java:31)

osm@map:~$ 

微妙にExceptionの中身が違ってきてますね。

いずれにせよ、schemaがどうこうってメッセージがでてます。なんだこれ?

調査

調べてみると、そのももずばりで困ってる人がいました。

Postgis DB to osm pbf file - OSM Help

でも、回答なしです。

もうちょっと調べると、インポート時の話ですが、read-pgsql の時の Exception と似たことで困ってるのがありました。

Osmosis: import .osm to postgresql fails - OSM Help

この回答を見ると、schemaを作れ、とあります。

ん? なんだその話。

最初にタイルサーバーを立ち上げたときの手順にはそんなの載ってなかったはずです。

ということで、上記コメント内のリンクをたどると、ありました。

Osmosis/PostGIS Setup - OpenStreetMap Wiki

でも、この手順って、まんま OpenStreetMap のデータベースを作成する手順なんで、今のものと互換性あるんでしょうか?

で、気になったので、上記のドキュメントに載っている osmosis_dir/script/pgsnapshot_schema_0.6.sql なんてのの中身を見てみようと思い、 実際に今のタイルサーバーにインストール済みの osmosis について調べると

mor@map:~$ dpkg -L osmosis
/.
/etc
/etc/osmosis
/etc/osmosis/log4j.properties
/etc/osmosis/plexus.conf
/usr
/usr/bin
/usr/bin/osmosis
/usr/share
/usr/share/doc
/usr/share/doc/osmosis
/usr/share/doc/osmosis/examples
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6.sql
/usr/share/doc/osmosis/examples/contrib
/usr/share/doc/osmosis/examples/contrib/apidb_0.6_osmosis_xid_indexing.sql
/usr/share/doc/osmosis/examples/contrib/apidb_0.6.sql
/usr/share/doc/osmosis/examples/contrib/dump_apidb.sh
/usr/share/doc/osmosis/examples/contrib/replicate_osm_file.sh
/usr/share/doc/osmosis/examples/contrib/CreateGeometryForWays.sql
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6_upgrade_4-5.sql
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6_bbox.sql
/usr/share/doc/osmosis/examples/pgsimple_schema_0.6_bbox.sql
/usr/share/doc/osmosis/examples/pgsnapshot_and_pgsimple.txt
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6_upgrade_5-6.sql
/usr/share/doc/osmosis/examples/pgsimple_schema_0.6_linestring.sql
/usr/share/doc/osmosis/examples/pgsimple_load_0.6.sql
/usr/share/doc/osmosis/examples/munin
/usr/share/doc/osmosis/examples/munin/osm_replication_lag
/usr/share/doc/osmosis/examples/munin/README
/usr/share/doc/osmosis/examples/munin/osm_replication.conf
/usr/share/doc/osmosis/examples/pgsnapshot_load_0.6.sql
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6_action.sql
/usr/share/doc/osmosis/examples/pgsimple_schema_0.6.sql
/usr/share/doc/osmosis/examples/pgsimple_schema_0.6_action.sql
/usr/share/doc/osmosis/examples/pgsnapshot_schema_0.6_linestring.sql
/usr/share/doc/osmosis/examples/fix_line_endings.sh
/usr/share/doc/osmosis/copyright
/usr/share/doc/osmosis/changelog.Debian.gz
/usr/share/doc/osmosis/README
/usr/share/man
/usr/share/man/man1
/usr/share/man/man1/osmosis.1.gz
/usr/share/osmosis
/usr/share/osmosis/osmosis-apidb-0.44.1.jar
/usr/share/osmosis/osmosis-hstore-jdbc-0.44.1.jar
/usr/share/osmosis/osmosis-core-0.44.1.jar
/usr/share/osmosis/osmosis-dataset-0.44.1.jar
/usr/share/osmosis/osmosis-xml-0.44.1.jar
/usr/share/osmosis/osmosis-pbf-0.44.1.jar
/usr/share/osmosis/osmosis-replication-http-0.44.1.jar
/usr/share/osmosis/osmosis-areafilter-0.44.1.jar
/usr/share/osmosis/osmosis-extract-0.44.1.jar
/usr/share/osmosis/osmosis-set-0.44.1.jar
/usr/share/osmosis/osmosis-pgsnapshot-0.44.1.jar
/usr/share/osmosis/osmosis-pgsimple-0.44.1.jar
/usr/share/osmosis/osmosis-pbf2-0.44.1.jar
/usr/share/osmosis/osmosis-tagfilter-0.44.1.jar
/usr/share/osmosis/osmosis-replication-0.44.1.jar
/usr/share/osmosis/osmosis-osm-binary-0.44.1.jar
/usr/share/osmosis/osmosis-tagtransform-0.44.1.jar
mor@map:~$ 

/usr/share/doc/osmosis/examples にインストールされていますね。 で、実際に中を見てみると、下記のような今のテーブル構成と全然異なっています。

osm@map:~$ psql -d gis
psql (9.5.14)
Type "help" for help.

gis=> \d
               List of relations
 Schema |        Name        | Type  |  Owner   
--------+--------------------+-------+----------
 public | geography_columns  | view  | postgres
 public | geometry_columns   | view  | osm
 public | planet_osm_line    | table | osm
 public | planet_osm_nodes   | table | osm
 public | planet_osm_point   | table | osm
 public | planet_osm_polygon | table | osm
 public | planet_osm_rels    | table | osm
 public | planet_osm_roads   | table | osm
 public | planet_osm_ways    | table | osm
 public | raster_columns     | view  | postgres
 public | raster_overviews   | view  | postgres
 public | spatial_ref_sys    | table | osm
(12 rows)

gis=> 

ということで、単純に examples 以下の sql を実行してもいろいろと問題になりそうな予感ばかりが立ちます。 さて、どうしたものかな。

スキーマ

ということで、もうちょっと調べてみると、OpenStretMap のデータベースって1種類じゃなくて、用途に応じて何種類もスキーマがあるということがわかりました。

Databases and data access APIs - OpenStreetMap Wiki

なるほど、そういうことか。

でもって、osm2pgsql で使ってるスキーマは、レンダリングに特化したもので、osm データ(xmlやpbf)の取得には適さないそうです。

ということで、今使っているタイルサーバーのスキーマと osmosis でpbf出力を行う際に想定しているスキーマが異なるので、簡単には pbf ファイルが作れないということがわかりました。 ついでに、 --read-apidb と --read-pgsql オプションでは想定しているスキーマも異なっていたようです。

まとめ

たぶん OpenStreetMap 界隈でサーバー扱ってる人には、このようなことは当たり前なんでしょうけど、初心者にはハードルが高いです。どこかに情報がまとまって落ちてるとありがたいです(が、そこを見つけるのもまだ一苦労なんだろうな、きっと)。 さすがに、調べに調べた挙句、できなかったのでちょっと疲れました。

とはいえ、オフラインマップ用のデータを更新していかないといけないので、めげずに次の方法を探ることにします。