プログラマーのメモ書き

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

trim_osc.py の更新失敗

OpenStreetmap のタイルサーバーの運用ですが、またエラーで更新が止まってしまいました。トホホ。

しかし、今回は、こちらの記事で設定したメール通知のおかげですぐに気づくことができたのが不幸中の幸いでしょうかね? ですが、実際に対応が完了するまでにおおよそ一か月以上かかってしまいました。せっかく通知が来たのに時間がかかるとは難しいもんですね。

一応、原因調査や対策時のメモを残しておきます。

症状

出力されていたエラーは次のようなものです。

[2018-10-29 22:06:01] 4620 start import from seq-nr 53056, replag is 27 day(s) and 14 hour(s)
[2018-10-29 22:06:01] 4620 downloading diff
[2018-10-29 22:17:43] 4620 filtering diff
Traceback (most recent call last):
  File "/home/osm/src/regional/trim_osc.py", line 131, in <module>
    for nd in root.xpath('//way[@id={}]/nd'.format(row[0])):
  File "src/lxml/lxml.etree.pyx", line 1587, in lxml.etree._Element.xpath (src/lxml/lxml.etree.c:61854)
  File "src/lxml/xpath.pxi", line 307, in lxml.etree.XPathElementEvaluator.__call__ (src/lxml/lxml.etree.c:178516)
  File "src/lxml/xpath.pxi", line 227, in lxml.etree._XPathEvaluatorBase._handle_result (src/lxml/lxml.etree.c:177421)
lxml.etree.XPathEvalError: Invalid expression
[2018-10-29 22:20:04] 4620 [error] Trim_osc error
[2018-10-29 22:20:04] 4620 resetting state
osm@map:/var/log/tiles$

詳細は置いといて、 trim_osc.py 内部の処理でこけているようです。

オープンソースなんでこちらを調べるのも一つの手なのですが、Githubのリポジトリを見ると、既にいくつかコミットがあります。なので、とりあえず最新版を pull して動作するか試してみます。

更新

ということで、更新します。

osm@map:~/src/regional$ git fetch
osm@map:~/src/regional$ git merge origin/master

試します。

[2018-10-09 09:06:02] 653 start import from seq-nr 53056, replag is 7 day(s) and 1 hour(s)
[2018-10-09 09:06:02] 653 downloading diff
[2018-10-09 09:07:13] 653 filtering diff
Traceback (most recent call last):
  File "/home/osm/src/regional/trim_osc.py", line 9, in <module>
    import psycopg2
ImportError: No module named 'psycopg2'
[2018-10-09 09:07:14] 653 [error] Trim_osc error
[2018-10-09 09:07:14] 653 resetting state

あれ?今度は別のエラーで落ちてます。

リポジトリを見ると

Force python3 · Zverik/regional@2271549 · GitHub

このコミットで python3 を使うようになったみたいです。 なるほど、多分、 psycopg2 の python3 用モジュールが無いんでしょうね。

ということで、python3 用モジュールをインストールしてやって再度試します。 ただし、 python3 のほうは pip 経由でモジュールをインストールしてみます(pipのほうがaptよりも新しいバージョンがインストールされるっぽいので)

mor@map:~$ sudo apt-get update
mor@map:~$ sudo apt-get install pip3
mor@map:~$ su - osm
osm@map:~$ pip3 install shapely
osm@map:~$ pip3 install psycopg2
osm@map:~$ pip3 install lxml

pip でのインストールの場合、ユーザーごとにインストールされるようなので、実際にデータの更新をしているユーザー(osm)に切り替えてインストールします。 なお、インストールしたモジュールは、最初に設定した時のものと同じです。

で、再度試してみると。。。あれ?同じくエラーがでますね。

trim_osc.py の調査と修正

さて、最新版にしただけだと、エラーが解決できなかったので、頑張って処理を追いかけてみました。

上述したように、最新のリポジトリ(この時点では、)にアップして、動かしたころ、

osm@map:~/tmp/osmosis_test3$ ~/src/regional/trim_osc.py -d gis -p region.poly -z -v changes.osc.gz trimed.osc.gz
/home/osm/.local/lib/python3.5/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
  """)
Traceback (most recent call last):
  File "shapely/speedups/_speedups.pyx", line 234, in shapely.speedups._speedups.geos_linearring_from_py
AttributeError: 'list' object has no attribute '__array_interface__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/osm/src/regional/trim_osc.py", line 82, in <module>
    tpoly = poly_parse(options.poly)
  File "/home/osm/src/regional/trim_osc.py", line 42, in poly_parse
    result = Polygon(poly)
  File "/home/osm/.local/lib/python3.5/site-packages/shapely/geometry/polygon.py", line 240, in __init__
    ret = geos_polygon_from_py(shell, holes)
  File "/home/osm/.local/lib/python3.5/site-packages/shapely/geometry/polygon.py", line 494, in geos_polygon_from_py
    ret = geos_linearring_from_py(shell)
  File "shapely/speedups/_speedups.pyx", line 319, in shapely.speedups._speedups.geos_linearring_from_py
TypeError: object of type 'map' has no len()
osm@map:~/tmp/osmosis_test3$ 

のようなエラーになりました。

エラーが shapely の部分で起きていたので、ライブラリを apt でインストールしたものに切り替えても試したのですが、結論としては、微妙に呼んでるライブラリなどが違うけど、大筋同じエラーになっていました。

元に戻って、スタックトレースをよく見ていくと、trim_osc.py の引数で与えている領域指定のファイル(polyのファイル)の処理で落ちているようです。

とりあえずbounding box を使い、poly を迂回して実行できるか試します。

osm@map:~/tmp/osmosis_test3$ ~/src/regional/trim_osc.py -d gis -b 136.202763 34.482312 136.6553727 34.4756217  -z -v changes.osc.gz trimed.osc.gz
/home/osm/.local/lib/python3.5/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary ple
ase use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
  """)
Traceback (most recent call last):
  File "/home/osm/src/regional/trim_osc.py", line 161, in <module>
    for nd in root.xpath('//way[@id={}]/nd'.format(row[0])):
  File "src/lxml/etree.pyx", line 1577, in lxml.etree._Element.xpath
  File "src/lxml/xpath.pxi", line 307, in lxml.etree.XPathElementEvaluator.__call__
  File "src/lxml/xpath.pxi", line 227, in lxml.etree._XPathEvaluatorBase._handle_result
lxml.etree.XPathEvalError: Error in xpath expression

やはり落ちます。しかも、今回の作業のきっかけとなった最初のエラーと似たような箇所(xpath の処理)で落ちています。

ここまでの経緯を整理すると、最新版の trim_osc.py は2つの問題がありそうに思えます。

  • poly でポリゴンがつくれない
  • xpath でエラーになる

後者の xpath の問題は、今回のきっかけでも出てきましたね(まったく同じではないかもしれませんが)。

いろいろと調べると、どうも、昔、MLで指摘されていたようで、大きなファイルの場合 xpath に述語(predicate、四角いかっこでの指定)を与えて実行してもエラーとなる場合があったようです。

[lxml] lxml.etree.XPathEvalError: Invalid expression for correct XPath expression on large XML file

残念ながら、上記の ML では、問題の指摘以後、特に動きはなかったようです。

xpath 現象の確認

後者の問題を確かめるため、今回問題となった xml ファイルをダウンロードして、python3 のコンソールで同様にxpathを呼び出してエラーになるのを確認します。

その後、テキストエディタを使い、先頭の60行程度で別ファイルとして保存し、同じくコンソールで動かしてみると、同じようにxpathを指定しても正しく動きます。

ということで、ここはファイルサイズが大きい場合の xpath の述語の処理が怪しいと検討をつけ、 xpath の処理を述語を使わない形に修正しました(修正内容を Pull Request しておいたので、詳しくはそちらを参照)。 これで、動かすと、なんと、最後まで処理が進みました!

これで一個解決です。

ポリゴンファイルの確認

こちらの処理は

Polygon(poly)

という部分で落ちています。よくよく見てみると、python3 では mapの戻り値は map オブジェクトであり、listではないとのことです。 でも、Polygonのコンストラクタはtupleのリストが与えられることを想定しています。

きっとこれが原因ですね。

というわけで、poly を作るところで、

51c51
<             poly.append(map(lambda x: float(x.strip()), l.split()[:2]))
---
>             poly.append(list(map(lambda x: float(x.strip()), l.split()[:2])))

のように、map を list に変換して情報を格納するようにしたら、問題なく動作するようになりました。

めでたしめでたし。

余談

一応、上記の修正内容は Pull Request で送ってるのですが、今日時点(2018/12/4)でまだ取り込んでもらえていません。 まあ、進捗は GitHub上のリポジトリ を見ればわかるので、そちらを追いかけてください。