プログラマーのメモ書き

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

OpenStreetMap のタイルサーバーの更新設定:エラーへの対応

前の記事で、タイルサーバーの地図データの自動更新も設定したので、『調子はどうかな?』なんて軽い気持ちでログを見ると、あれ?途中から error の文字が・・・

よくよくみると、 osm2pgsql で、差分データを反映させるところでエラーになっています。 『年末の忙しい時に、困ったなー』、と思いつつ、エラーを調べて、対応したので、顛末をメモっときます。

状況

もう一度、エラーの出方をよく見ます。ログは、 /var/log/tiles 以下にあります。実行時に概要のログは、 run.log ファイルにかかれています。 run.log を見ると、更新設定後しばらくの間はエラーも出ず、快調に動いています。 でも、ある時(あるシーケンス番号の更新)から、エラーとなっています。

ログはこんな感じでした。

[2017-12-29 22:06:01] 6654 start import from seq-nr 46396, replag is 1 day(s) and 2 hour(s)
[2017-12-29 22:06:01] 6654 downloading diff
[2017-12-29 22:07:05] 6654 filtering diff
[2017-12-29 22:07:52] 6654 importing diff
[2017-12-29 22:08:10] 6654 [error] osm2pgsql error
[2017-12-29 22:08:10] 6654 resetting state

一度、エラーになると、そのシーケンス番号が反映されていない、と判断されるためか、その後ずっと同じシーケンス番号を更新しようとしてはエラーをはく、というのが続いていました。 これはこれでエラー通知を入れとかないとひどいことになりそうですね。

エラー原因の確認

更新に使っている openstreetmap-tiles-update-expire の処理の概要はこんな感じかな?と理解しています。

  • state.txt(シーケンス番号)に基づいて、差分データを取得する (osmosis)
  • 更新したい地域のデータのみにフィルタリング (trim_osc.py)
  • データベースに反映 (osm2pgsql)
  • 地図データの無効化 (render_expired)

まさにデータベースに反映する処理のところで、エラーになっているようです。

/var/log/tiles にはそれぞれの個別処理毎のログもあるので、 osm2pgsql.log を見ると詳細なエラーが載っています。

Using XML parser.
Processing: Node(7k 0.4k/s) Way(8k 0.12k/s) Relation(0 0.00/s)node cache: stored: 7640(100.00%), storage efficiency: 33.19% (dense blocks: 1, sparse nodes: 7412), hit rate: 3.27%
Osm2pgsql failed due to ERROR: result COPY_END for planet_osm_line failed: ERROR:  invalid input syntax for integer: "'"
CONTEXT:  COPY planet_osm_line, line 1, column layer: "'"

一番最後の行の planet_osm_line がDBのテーブル名で layer がタグ名 ということに気づくのに随分と時間がかかってしまいました。 それさえわかれば、 planet_osm_line というテーブルの layer というカラムが integer なのにデータが '(シングルクォート)になっている、ということが理解できます。

もし、類似のエラーでお困りの時は、

osm@map:~/src/mod_tile$ psql -d gis
psql (9.5.10)
Type "help" for help.

gis=> \dt
              List of relations
 Schema |        Name        | Type  | Owner 
--------+--------------------+-------+-------
 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 | spatial_ref_sys    | table | osm
(8 rows)

gis=> 

としてテーブル一覧を確認したり、

gis=> \d planet_osm_line

とかして、関連するテーブル(ここでは planet_osm_line)のカラム名と型などを確認するのも有効かもしれません。

エラー内容の確認

とはいえ、layer タグのどういうところで具体的にエラーになっているのか?というのが気になります。

そこで、上記の更新処理を手作業で行ってみて、データを直接確認してみます。 適当に作業ディレクトリを作成して、

osm@map:~$ mkdir tmp
osm@map:~$ cd tmp
osm@map:~/tmp$ cp -p -R /var/lib/mod_tile/.osmosis osmosis_test 
osm@map:~/tmp$ osmosis  --read-replication-interval workingDirectory=./osmosis_test --simplify-change --write-xml-change sample.osc.gz 
osm@map:~/tmp$ ../src/regional/trim_osc.py -d gis -p osmosis_test/region.poly -z sample.osc.gz filtered.osc.gz 

/var/lib/mod_tile/.osmosis の作業ディレクトリを手元にコピーして使います。state.txt はエラーが発生したタイミングなので、 49396 のシーケンス番号が書かれているはずです。 ここまでは問題なく動作します。では、実際に差分ファイルの中身を確認します。

osm@map:~/tmp$ gunzip filtered.osc.gz 
osm@map:~/tmp$ grep -n layer filtered.osc 

とすると、運よく最下部に

207860:      <tag k="layer" v="'"/>
207868:      <tag k="layer" v="'"/>

と出ていました。なるほど確かにシングルクォートですね。で、エディタで開いて 207860 行付近を確認します。

    <way id="549551378" version="1" timestamp="2017-12-28T12:51:49Z" uid="192905" user="ribbon" changeset="54982406">
      <nd ref="5308380768"/>
      <nd ref="5308380769"/>
      <tag k="bridge" v="yes"/>
      <tag k="highway" v="steps"/>
      <tag k="incline" v="up"/>
      <tag k="layer" v="'"/>
    </way>

となってます。編集時のチェンジセットの番号もわかりますね。地図上で確認したければ、

OSM Changeset Analyzer

などのサービスを使って、フィルタリングして表示してみるとよいと思います。

ちなみに、このOSM の layer タグってどんなものなのか?というのもよくわかっていないので、併せて確認すると、

JA:Key:layer - OpenStreetMap Wiki

にあるように、地物の上下関係を示すのに使うタグのようです。

これが、整数を期待しているのに、文字(シングルクォート)だったのが直接の原因でした。

対応策

エラーの原因ですが、osm2pgsql のバグとかではなく、大元のOSMのデータの不整合が原因でした。 この不整合箇所を修正すれば、インポート自身はきっと成功するでしょう。でも、毎回エラーをはくたびにいちいち調べてそんなことやってられません。

どうしたものかと思案していると、ちょっと待てよと。 冷静に考えると、一番最初にOSMのデータをインポートしていますが、それに一切不整合が含まれていない、ということはあり得るのでしょうか?たまたま自分がインポートしたタイミングだけ、不整合が無かったのでしょうか? いやいや、いま自分の住んでる田舎のようにほとんでデータの更新もない地域ならいざ知らず、日本全土を対象にデータをインポートしていれば、一つや二つ不整合はあるでしょう、きっと。自分が layer タグを知らないからと言って、layer タグがマイナーで滅多に出てこないタグと断定できるはずもありません。

であれば、初回のインポート時には、データの不整合があった場合に、何らかの処理を行っているはずだ、となります。 そこで、初回インポートの処理をこちらから再確認してみます。

osm2pgsql -d gis --create --slim  -G --hstore --tag-transform-script ~/src/openstreetmap-carto/openstreetmap-carto.lua -C 2500 --number-processes 1 -S ~/src/openstreetmap-carto/openstreetmap-carto.style ~/data/azerbaijan-latest.osm.pbf

--tag-transform-script ~/src/openstreetmap-carto/openstreetmap-carto.lua なんかそれっぽいのがありますねー。 ここで指定した lua スクリプトで tag の処理をしてくれるようです。

lua スクリプトはよくわかりませんが、中身を見てみますとlayer タグに対して何らかの処理を施してくれているようです。

ここまでわかれば、このオプションを差分の追加時に渡せば問題なく動きそうかな?と予想が立ちます。 改めて、openstreetmap-tiles-update-expire の中身を確認すると

#------------------------------------------------------------------------------
# The OSM2PGSQL_OPTIONS here only need setting if a tag transform script is
# in use.  See https://github.com/SomeoneElseOSM/SomeoneElse-style and
# http://wiki.openstreetmap.org/wiki/User:SomeoneElse/Ubuntu_1404_tileserver_load
#------------------------------------------------------------------------------
OSMOSIS_BIN=osmosis
OSM2PGSQL_BIN=osm2pgsql
OSM2PGSQL_OPTIONS="-d gis"
#OSM2PGSQL_OPTIONS="--flat-nodes /path/to/flatnodes --hstore"

と、そのものずばり、 tag transform script に関するコメントがあります。 ということで、この部分を

OSM2PGSQL_OPTIONS="-d gis -G --tag-transform-script /home/$RENDERACCOUNT/src/openstreetmap-carto/openstreetmap-carto.lua --hstore"

と書き換えてみます。ちなみに、 -G --hstore のオプションもインポート時のスクリプトにはあったので、つけるようにしました。

書き換え後、しばらく待ってみると今度は無事にエラーが出ずに成功していました! やった!

なかなか一筋縄ではいかないものですね。