トップOpenStreetMap > MapnikをC++で使う

MapnikをC++で使う

1.はじめに

OSMに触れて、最初の5年ほど(2015〜2019年頃)は、 Mapnikでレンダリングした。

当初はWindows(C#,python)で、やがて(2019年頃) Ubuntu(C++)にシフトした。

タイル画像の更新はかっての1週間〜10日間が10数時間へと、20〜30倍もの高速化が達成できた。 Mapnikで生成するタイルサイズを大きくしたことが一番大きいが、 ハードディスクパソコンをSSDパソコンに変えたこと、C#/pythonをC++に変えたことが 高速化の要因である。

pythonの役割が大きかった場合、C++への移行で苦労するかと思ったが、それは杞憂だった。 しかし、Ubuntu で Mapnik を動かすことについて、苦労があった。 当初は、Mapnikのコンパイルに時間がかかった。

2.Mapnik 3.0 を ubuntu 18.04 にインストール

あれこれ失敗したが、最終的には ubuntu 18.04 を新規にインストールして、 文献[1]に従ってMapnikをインストールした。

sudo apt-get update
sudo apt-get upgrade

# install mapnik
git clone https://github.com/mapnik/mapnik mapnik --depth 10
cd mapnik
git submodule update --init
sudo apt-get install python zlib1g-dev clang make pkg-config curl
source bootstrap.sh
./configure CUSTOM_CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" CXX=${CXX} CC=${CC}
make
make test
sudo make install
下記のように環境変数を設定する(.profile の末尾に追加)。
export PROJ_LIB="/home/hatada/mapnik/mason_packages/linux-x86_64/proj/4.9.3/share/proj"
export ICU_DATA="/home/hatada/mapnik/mason_packages/linux-x86_64/icu/57.1/share/icu/57.1"
export GDAL_DATA="/home/hatada/mapnik/mason_packages/linux-x86_64/libgdal/2.1.3/share/gdal"

Windows単独ではメインメモリの余剰分が自動的にディスクキャッシュに回されるが、 ubuntuでは、configファイルにディスクキャッシュ(shared_buffers)の設定がいる。 また、ホストOS(Windows)とゲストOS(Ubuntu)でメインメモリを分割するため、 ubuntuでは十分なメインメモリが使えないため、パフォーマンスが損なわれるかも知れない。

/etc/postgresql/10/main/postgresql.confを見ると shared_buffers = 128 MB となっている。 これを 512MB に設定した。

ubuntuに 5GBのメモリを与えたが、4GB に減らしてもいいようだ。 余ったメモリは Windows がディスクキャッシュに使う。

ただし osm2pgsqlを実行するときは、4GB では不足、やはり 5GB とする。

記事[11]によれば、Windows(ホストOS)のディスクキャッシュによりPostgreSQL(ubuntuゲストOS上)の リード性能が上がるようだから、Windowsで何も走らせなくても無駄にはならない。 エディタやブラウザなどは使う。

3.OSMタイル地図更新プログラム

現在使用中のpythonプログラムをほぼそのままC++に移し動作確認した。

引き続き、現在、C#プログラムで行っているタイル更新処理を全て、抜本的に見直し、C++ に移す。

タイル画像作成では、日本地図領域が必要となる。 その都度、多角形から算出するのは時間がかかるので、 テーブル xmin[], ymin[], ymin[][], ymax[][] をプログラムの初期化で求めておく。

日本地図領域は geofabric は極座標の多角形で定義しているが、 まず、頂点座標を zoom 20 でのタイル座標(xy座標)に変換する。

ymin[20][x], ymax[20][x] を求めたら、 zoom 19 は zoom 20 の2ラインになるので、これをもとに ymin[19][], ymax[19][] を求める。

これを繰り返す。実際に使うのは、zoom = 3 までである。

因みに、xmin[3] = 6, xmax[3] = 7, ymin[3][] = { 3, 2 }, ymin[3][] = { 3, 3 } である。

4.Mapnikスタイルファイル

connect_timeout

ときどき、実行時に次のようなタイムアウトエラーが起きるようになった。

terminate called after throwing an instance of 'mapnik::config_error' what(): Postgis Plugin: timeout expired Connection string: 'host=192.168.11.8 port=5432 dbname=osm user=postgres connect_timeout=4 fallback_application_name=mapnik' encountered during parsing of layer 'roads-ref-lowzoom' in Layer of '/media/sf_osm/my_osm.xml'

これまでは Mapnik は Windows でクローズしていたが、現在は、Mapnikはゲスト(ubuntu)で、PostgreSQL+PostGIS はホスト(Windows) にある。 ubuntuの負荷が重いとWindows上のPostgreSQLの応答性が悪くなるのが原因であろう。ubuntuのCPU使用を制限すれば解決するかも知れないが、 まずは、タイムアウト値をデフォルトの4秒からもっと大きな値に変えることによって対応する。

他にも方法があるかも知れないが、とりあえず、Mapnikスタイルファイルの Datasouce に connect_timeout を追加した。

    <Layer name="power_line" srs="省略">
        <StyleName>power</StyleName>
        <Datasource>
            <Parameter name="dbname">osm</Parameter>
            [中略]
            <Parameter name="type">postgis</Parameter>
            <Parameter name="connect_timeout">100</Parameter>
        </Datasource>
    </Layer>

結局、データベースは ubuntuに移したので、タイムアウト値はデフォルトの4秒でいいと思うので、 この修正は不要になった。

5.データベースをubuntuに置く

データベースを Windowsに置いた状態では、 十分な性能が得られない可能性があるので、ubuntu上の postgresql 10 にコピーする。

ubuntuに postgresql 10.0をインストールした。

まず、次のようにして、Windows上のデータベースをカスタム形式で共有フォルダ(usbメモリ)にダンプした。

C:\Program Files\PostgreSQL\9.5\bin>pg_dump -Fc -Upostgres osm  1>c:\sf_osm\osm_c.dump

現在は、osm2pgsqlが生成したテーブルと容量的には同程度の独自テーブルを描画に使用しているため、 osm_c.dumpのサイズは 5GB近くなった。ダンプ時間は 30分強であった。 restore時間は測定しなかったが、dumpより時間がかかった。チューニングをしていない影響が大きいかも知れない。

問題がなければ、いずれ、Windowsには osmデータベースを置かないので、dump/restoreは要らなくなる。

ubuntuでは ユーザ postgres が共有フォルダにアクセスできるように vboxsf グループに追加しておく。

$ sudo adduser postgres vboxsf
$ su - postgres
$ pg_restore -Fc -d osm /media/sf_osm/osm_c.dump

その前に 「FATAL: could not access private key file "/etc/ssl/private/ssl-cert-snakeoil.key": 許可がありません」 というエラーがでたので、ssl-cert グループにも追加した。adduser と gpasswd は同じことができるのだろう。

$ sudo gpasswd -a postgres ssl-cert

データベース osm を作成し、postgis を使えるようにする。

postgres@hatada-VirtualBox:~$ sudo -u postgres createdb --encoding=UTF8 osm;
postgres@hatada-VirtualBox:~$ psql --username=postgres --dbname=osm -c "CREATE EXTENSION postgis;"
CREATE EXTENSION

6.実行時間測定結果

実行時間測定結果を下に示す。C++に不慣れなことと Mapnikの詳細に通じているわけではないので、色々手間取ったが、 結果的には、十分な性能が得られた。

VirtalBoxは遅いという記事[11]もあり、高速化は無理かという思いもあったが、 C#/pythonよりも更新時間が短縮した。これには、pngファイル出力が2回から1回に減った効果も含む。

自分にとっては C++の方が pythonよりはプログラムの改善がやりやすいので、 今後、さらに、性能向上が図れる可能性が大きい。

Timeが全体の更新時間、Mapnikがレンダリング時間を表す(単位:時間)。 C#/pythonの場合、Mapnikにはpngファイル書き込み時間を含むが、 C++ の場合、この時間はない。Imageデータ作成までのレンダリング時間のみを表す。 すなわち、Time - Mapnik が更新対象抽出およびpng化・アーカイブ時間である。

zoom  Time(Mapnik)       タイルサイズ       tiles(arcs)    size   threads
  8    0.11(0.11)     1x1 ( 256x256 画素)      356(3)        4MB      2
  9    0.10(0.10)     2x2 ( 512x512 画素)    0.1万(5)       10MB      2
 10    0.14(0.14)     4x4 (1024x1024画素)    0.5万(11)      26MB      2
 11    0.25(0.25)     4x4 (1024x1024画素)     2万(34)       80MB      4
 12    0.40(0.38)     8x8 (2048x2048画素)     8万(102)     230MB      4
 13    0.54(0.50)     8x8 (2048x2048画素)     5万(123)     740MB      4 
 14    0.77(0.65)    16x16(4096x4096画素)    17万(394)     2.4GB      4
 15    1.49(1.20)    16x16(4096x4096画素)    55万(2,151)   6.7GB      4
 16    4.38(3.42)    16x16(4096x4096画素)   195万(3,981)  16.5GB      4
 17    2.45(2.19)     8x8 (2048x2048画素)    40万(2,766)   6.5GB      4
 18    1.23(0.97)    16x16(4096x4096画素)    50万(1,539)   5.9GB      4
 19    0.42(0.31)    16x16(4096x4096画素)    22万(493)     2.3GB      4
 20    0.04(0.03)    16x16(4096x4096画素)     3万(46)      141MB      4
-------------------------------------------------------------------------
 計    12.3(10.3)                          386万(1.2万)     41GB   

A.リファレンス

[1] Installing Mapnik on Ubuntu
[2] Reverse-engineering the Mapnik rendering library
[3] C++11で始めるマルチスレッドプログラミング その2 std::mutex 編
[4] C++11: lockせずにデータ競合を防ぐ
[5] libarchive Examples
[6] Zipping/Unzipping files in C++
[7] C++ wrapper for libarchive library
[8] A C++ wrapper for LibArchive
[9] libarchive: アーカイブを作成する
[10] PostgreSQL のパフォーマンスチューニング
[11] VirtualBox のストレージは本当に遅いのか? vs VMware Fusion
[12] c++ 違い stringstream、string、およびchar*変換の混乱