OpenStreetMapというプロジェクトがあることを知り、綺麗な地図だなと思っていたらMapnikを使ってレンダリングしているよということが書かれており地図画像が出力できたら面白いなということで試してみました。
今回は、Ubuntu (WSL)で試してみました。全体の流れですが、まずは元となる地図のデータを用意する必要があります。そのあとにその地図をレンダリングするMapnikの準備をしていきます。また、Mapnikで地図をレンダリングするときには、地図のスタイルを指定するXMLファイルを用意するのですが、OpenStreetMapで使われているものを今回は利用していく形にします(ものすごく細かい定義が多くされているので最初からつくると本当に大変なことになると思います)。
地図データを用意する
地図データですが、OpenStreetMapによるとミラーサイトからダウンロードして下さいねと書かれているのでミラーサイトからOSM形式のファイルをダウンロードします。
全体: http://download.geofabrik.de/index.html
日本: http://download.geofabrik.de/asia/japan.html
今回は、.osm.bz2
の形式のものをダウンロードしてきました。容量も大きいのでまず試すという目的では一部だけでも大丈夫だと思います。ここでダウンロードしたファイルは解凍する必要はないのでそのままにしておきます(この後利用するツールで自動的に解凍してくれます)。
Mapnikでは .shp
形式のファイルを直接レンダリングすることができるのですが、OpenStreetMapで使用している地図のスタイルの方でPostgreSQLからデータを呼び出すような記述がされています。そのようなことからここでダウンロードしたファイルを直接利用するのではなく、一度PostgreSQLにデータをインポートしていきます。
PostgreSQLのインストールとセットアップ
aptを使ってインストールしていきます。PostGISも使用しているのでこちらもインストールしていきます。
sudo apt install postgresql sudo apt install postgresql-contrib postgis
完了したら起動(起動していなければ再起動)します。
sudo /etc/init.d/postgresql restart
postgresユーザーとして新しくユーザーとgisという名前のDBを作成します。
createuser -d -U postgres -P {ユーザー名(WSLでのログイン名など)} createdb --encoding=UTF8 --owner={ユーザー名} gis
次にDBにプラグイン(PostGISとhstore)を導入します。これで、gisのDBでPostGISなどが利用できるようになります。
psql --dbname=gis CREATE EXTENSION postgis; CREATE EXTENSION hstore;
これでPostgreSQL関連の作業はおしまいです。
地図データの取り込み
地図を取り込む際にスタイルを定義しているプロジェクト(openstreetmap-carto)のスクリプトを利用しているようですので、まずスタイルのプロジェクトファイルを取得します。
git clone https://github.com/gravitystorm/openstreetmap-carto.git
次に実際にOSM形式のファイルを取り込むためのプログラムであるosm2pgsqlを導入します。aptでインストールができるので、こちらを利用します。
sudo apt install osm2pgsql
インストールができたら次のコマンドでPostgreSQLにデータを取り込みします。時間がかかりますので待ちます。
osm2pgsql --hstore -d gis kansai-latest.osm.bz2 -S openstreetmap-carto/openstreetmap-carto.style --tag-transform-script openstreetmap-carto/openstreetmap-carto.lua -U {作成したPostgreSQLのユーザー名} --cache 1000
メモリが少ない場合は、--slim
オプションもつけると良いそうです。
これで地図データの用意はおしまいです。
Mapnikのスタイルファイルを用意する
スタイルファイルの変換
OpenStreetMapで使用されているスタイルファイルを作成しているプロジェクトが、実は先ほどのopenstreetmap-cartoになります。ここで作成されているものをMapnikで読み込めるように変換を行う必要があります。このプログラムがcartoになりますが、node.jsのnpm経由でインストールできます。
aptでnode.jsをインストールします。
sudo apt install nodejs sudo apt install npm
(WSLの場合はhomebrew経由でインストールした方がパスの問題などが起こらないので楽かもしれません)
npmでcartoをインストールします。
npm install -g carto (もしWSLのエラーで実行できないときは次を試してみる) npm install carto
インストールが終わったらopenstreetmap-cartoのパスに移動して変換を実行します。
cd openstreetmap-carto carto project.mml > mapnik.xml (もしWSLのエラーで実行できないときは次を試してみる) ../../node_modules/carto/bin/carto project.mml > mapnik.xml (nodemodulesの場所は環境に合わせて変える)
フォントのインストール
aptでインストールします。
sudo apt install fonts-noto-cjk fonts-noto-hinted fonts-noto-unhinted fonts-hanazono ttf-unifont
スタイルから参照されているファイルの準備
実は、変換されたmapnik.xmlの中で.shpファイルの参照が行われています。(他にもopenstreetmap-carto
にあるsymbols
のパスの中のファイルも参照しているのでmapnik.xmlを別のパスで利用するときには注意が必要です。)
このため、mapnik.xmlが置いてあるパスにdataディレクトリを作成してその中にファイルをダウンロードして解凍していきます。ダウンロードするファイルはこちらopensteetmap-cartoのインストールのドキュメントに書かれています。
openstreetmap-carto/INSTALL.md at master · gravitystorm/openstreetmap-carto · GitHub
(今回は直接ダウンロードしてunzipしたのですが、openstreetmap-cartoの scripts/get-shapefiles.py
を使うの楽なようです。)
Mapnikの導入からレンダリング
いよいよMapnikを導入します。少し試したところnode.js版が一番楽なのではないかと思いますので、node.js版の説明になります。
node.jsのインストール(インストールを行っていない場合のみ)
aptでインストールします。
sudo apt install nodejs sudo apt install npm
(WSLの場合はhomebrew経由でインストールした方がパスの問題などが起こらないので楽かもしれません)
mapnikのインストール
npm経由でインストールします。
npm install mapnik
Mapnikを使用してレンダリング(全体)
mapnikを使用して地図全体をレンダリングするコードは次のようになります。
var mapnik = require("mapnik"); var fs = require("fs"); mapnik.register_default_fonts(); mapnik.register_fonts("/usr/share/fonts", {recurse: true}); mapnik.register_default_input_plugins(); var map = new mapnik.Map(1024, 1024); map.loadSync("mapnik.xml"); map.zoomAll(); map.renderFileSync('map_all.png');
ex1.js
というファイル名で保存します。そして次のコマンドで実行します。
node ex1.js
同じパスに map_all.png
が出力されていれば動作しています。
(samplesも参考にしました)
Mapnikを使用してレンダリング(拡大)
ex2.js
というファイルで次のようなコードを書きます。
var mapnik = require("mapnik"); var fs = require("fs"); mapnik.register_default_fonts(); mapnik.register_fonts("/usr/share/fonts", {recurse: true}); mapnik.register_default_input_plugins(); var map = new mapnik.Map(1024, 1024); function d2mLat(lat) { var y = Math.log(Math.tan((90.0 + lat) * Math.PI / 360)) / (Math.PI / 180.0); return y * 20037508.34 / 180.0; } function d2m(lon1, lat1, lon2, lat2) { return [ lon1 * (20037508.34 / 180.0), d2mLat(lat1), lon2 * (20037508.34 / 180.0), d2mLat(lat2) ]; } map.loadSync("mapnik.xml"); map.zoomToBox( d2m( 135.738163, 35.016356, 135.783160, 34.981814 ) ); map.renderFileSync('map_zoom.png');
同じように実行します。
node ex2.js
map_zoom.png
が出力されていれば完了です。
参考にさせていただいたページ
- 地図タイルの計算まとめ - Qiita
- GitHub - mapnik/node-mapnik-sample-code: Sample code demonstrating usage of node-mapnik
参考にさせていただいたページ(全体)
wiki.openstreetmap.org qiita.com www.develop-memo.com qiita.com qiita.com qiita.com