smellman's Broken Diary

クソみたいなもんです

東京の鉄道路線図SVGをさっそくタイル化してみた


東京の鉄道路線図SVGを作りました&パブリックドメインで配布します - Liner Note

このような面白い記事がありましたので、さっそく鉄道路線SVGを利用してタイル化してみました。

やっている事自体は僕が前回書いたゲームタイルの話の延長だったりします。

ソースなどは以下の所にあります。

smellman/railstilemap · GitHub

簡単に言うと、Phantomjs版はzoomFactorを変えつつ256x256のスクリーンショットをずらして取っていて、Ruby+rsvg2+ImageMagick版はrsvg2で拡大縮小した画像を生成して、256x256のサイズに切り取れるようなサイズの空っぽの画像を作ってそこにぺたって貼り付けてから256x256で分割して並べ直してる。

Ruby+rsvg2+Imagemagickの方は前回のプログラムをちょっとrsvg2が使えるように加工したっていう感じです。

ゲームタイル的な何かについて

FOSS4G Advent Calendar 2014 の5日目です。

今回は、ゲームタイルと呼ばれる何かについて話そうと思います。

ソーシャルストーキングが得意なみなさんなら僕が先月イベントで話したネタだとわかるはずです。
はい。
そうです。
使い回しです。

さて、ゲームタイルという何かですが、別に名前があるわけではないです。

平たく言うと L.CRS.Simple の話です。

Leafletの CRS.Simple.js の先頭にこんなのがあります。

/*
* A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
*/

ここにゲームマップって書いてあるよね。以上。

というわけで早速ゲームマップを動かしてみましょう。

とりあえず画像を拾ってきます。「ドラクエ 地図 画像」とかで検索するとそれっぽいのが拾えます。ただし、それを出してしまうとスクウェア・エニックスなだけに問題がありそうなので、今回は高野さんで代用します。

次に画像処理ですが、ぶっちゃけ Using Leaflet.js with non-geographic imagery - Oliver Marriott にある処理を行います。
ただし、total_tilesとか入力するのは面倒なので全て自動化してしまいます。

自動化したスクリプトこちら からゲットして下さい。

あとはflickrから落としてきた高野さんを食わせます。

高野さんタイル

おっと、この高野さんは長方形なので余計な横幅がひどく目立ちます!

というわけで正方形の高野さんをフォトショップなどで作成してからタイルを作成します。

正方形の高野さんタイル

ここから技術的な解説をします。

まずL.CSR.Simpleでは 2 の zoom レベル乗が縦横それぞれの長さとなります。
zoom レベルが0であれば1x1, zoomレベルが8であれば256x256, zoomレベルが12であれば4096x4096という範囲になります。

そしてタイルが256x256というサイズなので、zoomレベル12の時は縦横16x16の画像を作成するということになります。

ただし、長方形の画像が入力された場合は端っこが切れてしまいます。
単にzoomレベル12で15x14みたいな数しか出力されないというだけならいいのですが、
端っこの方の画像が256x256よりも小さい画像になってしまうと無理やり256x256のサイズで表示しようとしてしまい画像が無理やり引き伸ばされてしまいます。

今回はこれをキレイにするために縦横がそれぞれ256の倍数になっている画像をわざと生成して、それを元画像と合体させてからタイルの切り出しをしています。
なお、この処理をしても綺麗な高野さんは生成されません。

L.CSR.Simpleを使ってもlatlngの値は普通の地図と同じように90 -90,180 -180の範囲を採ります。
しかしながら単純に画像を処理しているのでこれらの値を使うのはとても大変です。

そこで利用するのがmap.unprojectです。
この関数はタイル上のx, y座標をlatlngに変換します。

例えば 2048x2048 (maxZoom 11) のタイルを表示するときに300, 200ぐらいの座標を中心にもっていく場合はこんな感じでやります。

var map = L.map('map', {
    maxZoom: 11,
    minZoom: 8,
    crs: L.CRS.Simple
}).setView([0, 0], 0);
map.setView(map.unproject([300, 200], 11));

GeoJSONもlatlngではなく、x, y座標で記述することもできます。
ただし、取り込みの際に coordsToLatLng で変換ルールを記述する必要があります。

具体的にはこんな感じで動かします。

var geojson = {
    "type":"Feature",
    "geometry": {
        "type":"LineString",
        "coordinates":[[200,300],[400,400]]
    },
    "properties":{}
}; 
var geojson_layer = L.geoJson(geojson, {
    coordsToLatLng: function (newcoords) {
        return (map.unproject([newcoords[0], newcoords[1]], 11));
}});

これを駆使すれば高野さんにいくらでもイタズラすることができます。

以上です。

明日はGUNMA GIS GEEKこと @_shimizu さんです。

僕とJavaとコンピュータとおばあちゃん

この話、どっかで文字にした気がするんだけど、探しても見当たらなかったので文字に起こしてみる。
ちょっと酒入ってるので雑だけどね。

この話は僕がコンピュータの仕事をちゃんと目指す間接的なきっかけとなった話です。

僕は高校入学当時は薬剤師を目指していた。元々医療系の家系というのもあったんだけど、血を見るのが嫌いというのがあって、まだちょっとできる科学の知識が使えるかもしれないというところから医療系なら薬剤師しかできないだろうと思ってた。
でも、高校時代に生物の先生が本当にやばくて、生物には近づきたくないというのもあって、完全にやりたいことが数学or物理という方向になってしまった。
ちなみに英語も国語も留年ギリギリみたいな状態で、数学系以外はまったくだめだった。
そんなこんながあって、高校二年の頃には得意な分野を活用するには物理演算などの活用できる3Dゲームの分野と思い、ゲームプログラマになりたいという気持ちがあった(ちなみにゲームプログラマは昔も目指そうと思ったけど小学校、中学校、高校全部でBASICで完全に挫折してた、行番号が本当にわからなかった)。

そんなときにおばあちゃんが亡くなった。

僕自身おばあちゃん子って感じであった。
中学時代もおばあちゃんと一緒にお風呂によく入っててよく背中の流し合いをしてたし、小学校の頃に戦争の話とかを聞くとよくおばあちゃんの部屋に言ってはいろいろ聞いていた。
関東大震災なんかの話もよく聞いてたけど、正直大変だったという印象以外思い出せなくて申し訳ないという気持ちがある。

おばあちゃんが体調が悪くなって入院したのは高校一年のころ。
それから学校から時間があればおばあちゃんが入院してる病院(僕が生まれた病院で、親父の元勤務先でもあってすごく大切な場所)に自転車で行っていた。

たぶん、家族の中で最後の一年を一番過ごしたのが僕だったと思う。
でも、亡くなる時だけは立ち会えなかった。
僕が部活を終えてから家に帰ったら誰も居なくて、みんなが立ち会いに行って最後を看取ったけど、僕だけは家に居たって感じだった気がする。

その後はお通夜があって、僕と親父の二人で一晩を過ごした。もうその時のことは覚えてない。

そんなことがあって、次の日はお葬式。
たくさんの人たちがやってくるんだけど、正直まだ二十歳未満の身というのもあり、周りの人の対応とかは両親や姉たちがやるというのが暗黙の了解だった。

そんな時に親父が僕と妹に「暇だろうから何か暇つぶしを買ってこい」と二人に500円玉を渡してくれた。
丁度斎場の近くに今は潰れたTSUTAYAがあって、そこに本屋も併設されていたのでそこに本を買いに行った。

その時に出会ったのが「Hello! PC」という雑誌でした。
いわゆるDOS/V系の雑誌ではあったんだけど、その時の特集がJavaだった。

プログラミングというのは今まで僕の中ではBASIC以外のものがあるというのは知らなくて、僕が本当にわからないと思っていたもの以外でもプログラミングができるんだというのを知ってすごく興味を持った。
たしか480円だったので、そのまま親父にもらったお金を使って購入した。

ちなみに、当時はWindows自体は高校の部室には導入した(なお、DigitalというかCompaqのマシンだった)んだけど、何をすればいいのかさっぱりなものだった。
あと、部活の親友はかなりのMac信者で、「この水晶板をこれに変えればこれぐらいクロックアップができる」みたいな謎の勧誘を高校の先生にするような人で、Windowsを使いこなすような状況じゃなかった。

で、僕は生まれて初めてコンピュータ雑誌を買うんだけど、そこにすごく興味深い連載があった。
それはPC-UNIXの連載です。
当時は丁度JEのレポジトリがぶっ飛んでしまってPJEが始まったとかそんな内容だったと思う。
1997年の時です。

まったく知らないUNIXという言葉、FreeBSDLinuxという僕の知らないOSの名前(知ってたのはWindowsMacOSだけだった)にものすごい興奮を覚えた。
その連載に書いてあったのは無骨なコマンドラインばかり。
この意味を理解したら何かが変わるのかなと思った。
でも、それ以上にその時はJavaという謎の存在を理解しようというのが強かった。

そのあと、小遣いを使ってHello! PCを買うんだけど、丁度そのタイミングで親父がパソコンを買ってくれると言ってくれた。
僕の家には書院とPC-9801(姉の所有物)のノートがあったんだけど、前者はブラインドタッチの練習用途もしくはカセットテープのタイトル打ちがメインで、後者は本当に使い道がわからなくてフロッピーを挿して起動したら動いたぷよぷよもしくはBio100%のゲームだけしか使ってなかった。
僕にとっては初めてのパソコンです。
Hello PC!で得た知識をフル動員した。
このタイミングならISAが一杯ついてるやつのほうがいい!!!!!!!

敗北です。

その当時MMX200MHzのマシンでありながらPCIが2つに対してISAが5つという変なの買ったんです。
言い訳させて下さい。
Hello PC!に乗ってるボードでPCIのってグラフィックぐらいだからISAの方がいろんなデバイスあっていいじゃんって思ったんですよ!!!!
まさか次の年にPCIのサウンドカードが出始めるとは!!!!

まぁこの話は老いておこう。
老いです。

とりあえず秋葉原のアウトレットのお店で30万円ほどかけてIBMのマシンを購入しました。
ついでに、VectorのCD-ROM8枚組も一緒に買いました。
あと、アルプスのプリンターも買いました。テリー伊藤が広告やってたやつです。
品質はよかったけどランニングコストが泣けました。
ちなみに小学校からの親友が最初に就職したのもアルプスなんだよね。びっくりしたわ。

で、一ヶ月ほどはとりあえずIBMのマシンが家にあるだけで、何をしていいのかわからないのでとりあえずレジストリエディタを弄って遊んでました。
あと、VectorのCD-ROMの中を調べてとりあえず高音質で音楽が聞けるというのでMOD関係のプレイヤーで楽しんでました。303tekの初期のプレイヤーです。これ覚えてるの本当にもういないと思う。

その後一ヶ月してからネットが開通しました。
最初のインターネットはJustNetでした。
JustSystemのアレです。
JustViewの話は辞めよう。

とりあえず、まずJDKをダウンロードしました。デカイ。
その後、MOD関係のアーカイブを辿っていき、おもむろにrarファイルをダウンロードしました。
あとで気づいたんだけど、英語がわからずこの時ダウンロードしたのはMODファイルじゃなくて音源ファイルでした。
僕が生まれて初めて展開したのがrarという状況になったのも先にMODという変な文化に触れたからです。
MOD万歳。

で、Javaに戻るんだけど、インターネットに繋がったことで、JDKを入手できてやっとHello! PCに書いてあるサンプルコードを打てるようになりました。今思えばどっかの書籍から入手できそうなものだけど、当時は全然わからなかったんですよ。
で、ひたすら打ちまくるんだけど、とりあえずサンプル動かしたあとに、何故こういう仕組みなのかわからないというのが強く残りました。
いきなりオブジェクト指向とかAppletとかは辛かったのかもしれません。
というわけで高校の友達(Blenderの日本語化をやってるやつ)から「この前こういうクラック事件があってTcl/Tkが使われたらしい」みたいな情報を聞いてTcl/Tkを使い出したりとか、8086とかのアセンブラチャンレンジしたりとかPerlの勉強始めて挫折したりとかいろいろありました。
一番高校の時に知識を得たのは前述の高校の友達からFreeBSD徹底入門を借りて2日で読破したことです。まさかその本の著者のあさたくさんがいるBBQに参加するとはその時はまったく想像付かなかったと思います(というか亡くなられたというのがもっとつらいけど)。

まぁ、そんなこんなでJavaがきっかけでプログラマ目指せるかも!って思ったのが高校時代でした。
少なくともそのきっかけになったのはおばあちゃんの死です。
それがきっかけでずいぶん早い時期にPC-UNIXLinuxにも出会えました。
MODミュージックもたぶんそのタイミングが無ければ出会えなかったと思います。
僕は今MODのトラッカーがベースになった音楽を良く聞いています。breakcoreとか!

僕にとってはコンピュータにある何もかもはおばあちゃんからの最後の贈り物だと思えるのです。
わりと辛いことがあっても、やはりこの思いが支えになってくれてるところはあります。
そんなわけで、おばあちゃんありがとう!

タイルの説明によく使うあの図を作成するスクリプト

タイルの説明でよくこんな画像ありますよね。

f:id:smellman:20141129040615p:plain

f:id:smellman:20141129040624p:plain

f:id:smellman:20141129040636p:plain

いちいち作るのが面倒だと思うので、一発で作成するスクリプトを書きました。


gist30fa094aa256e25489d9

zoomレベルの範囲を変更すればもっといろいろ作れますが、サーバの負荷があまりかからぬようお願いします。

OSMのデータをつっこんだPostGISとTileStacheを組み合わせてベクタタイルを試してみた

手元でベクタタイル動かせたら面白いなぁというよくわからない欲求を持っていたんですが、ついうっかりやってしまいました。

今回使うのは以下のプログラム。

TileStache
軽量なタイルサーバ兼キャッシュサーバ
OSM.us-vector-datasource
http://openstreetmap.us/%7Emigurski/vector-datasource/ でつかわれてるTileStacheの設定

なお、今回はOSM.us-vector-datasourceにmacports向けの設定を書きつつ、PostGIS2に対応したものを用意しました。

前提条件として、前回の記事で書いた内容にあるデータを突っ込んでいる状態からスタートです。

MacPorts環境にosm2pgsql入れてOpenStreetMap CartoをTilemillで動かしたって話 - smellman's Broken Diary

まずは作業場所を作成します。

mkdir TileStache
cd TileStache
virtualenv-2.7 env
source env/bin/activate

次に依存関係の解消します。MacなのでPILの代わりにPillowを入れます。また、Shapelyはpipで入れようとするとgeos_c.hが見つからないとエラーになるので手動で入れています。

pip install -U Pillow
pip install -U modestmaps simplejson werkzeug
pip install psycopg2
git@github.com:Toblerity/Shapely.git
cd Shapely
python setup.py install
cd ..

次にTileStacheのインストールです。

git@github.com:TileStache/TileStache.git
cd TileStache
python setup.py install
rehash
cd ..

そして、OSM.us-vector-datasourceを導入します。今回は僕のレポジトリから取得します。

git clone git@github.com:smellman/OSM.us-vector-datasource.git local_vector_datasource
cd local_vector_datasource
git checkout macport_postgis2

最後にサーバの起動なんですが、標準のtilestache-server.pyだとアクセスが多いとsocket.pyでError 32が出てしまうので、gunicornを使って起動をします。

pip install gunicorn
gunicorn -b 127.0.0.1:8080 "TileStache:WSGITileServer('/Users/btm/develop/osm/TileStache/local_vector_datasource/tilestache.cfg')"

あとは確認をします。

curl http://127.0.0.1:8080/highroad/15/29088/12902.json

無事にJSONが帰ってきたら成功です。

では、d3.jsのVector Tilesのサンプルを参考にURLを127.0.0.1に向けて、最初の表示位置を日本にしてみます。gistからダウンロードして下さい。

https://gist.github.com/smellman/400adff2dece5f6854d6

これをブラウザで開いて上手く行けばOKです。

f:id:smellman:20141018030023p:plain

明日のOSC2014 Tokyo/FallのOpenStreetMapのブースでこちらのデモを行うと思います。ぜひ足を運んでみてください。

追記

ベクタタイルに必要なデータが足りなかったので補足します。

まず、OSM.us-vector-datasource にdataというディレクトリがあるのですが、この中のファイルを取り込む必要があります。

cd OSM.us-vector-datasource
cd data
unzip land-usages-naturalearth.zip
unzip water-areas-naturalearth.zip
PATH=/opt/local/lib/postgresql93/bin:$PATH ./shp2pgsql.sh|psql -U postgres gis

次にwater-polygonsを取り込みます。これは openstreetmapdata.com が配布してるWater PolygonsのShapeファイルを取り込みます。

mkdir water-polygons
wget http://data.openstreetmapdata.com/water-polygons-split-4326.zip
unzip water-polygons-split-4326.zip
/opt/local/lib/postgresql93/bin/shp2pgsql -dID -s 900913 -W Windows-1252 -g the_geom water-polygons-split-4326/water_polygons.shp water_polygons|psql -U postgres gis

あと、topojsonでも試してみました。

f:id:smellman:20141018061515p:plain

デモは全てgithubレポジトリにまとめました。

smellman/osm_vector_tiles_local_demos · GitHub

MacPorts環境にosm2pgsql入れてOpenStreetMap CartoをTilemillで動かしたって話

とある事情によりOpenStreetMap Cartoを手元で動かす必要があり、また個人的にもOpenStreetMapのデータが入ったPostgreSQLが欲しかったのでセットアップをしてみた。

まず、osm2pgsqlのインストールで必要なライブラリをいれる。

sudo port install protobuf-c protobuf-cpp
sudo port install proj47

他にもgeosとかいろいろいれる必要がありますが、qgisとかMacPortsで入れていればだいたい入ってると思う(何

あと、PostgreSQL 9.3+PostGISも別途入れておきます。

次にosm2pgsqlのインストールです。

git clone git://github.com/openstreetmap/osm2pgsql.git
cd osm2pgsql
./autogen.sh
./configure --with-proj=/opt/local/lib/proj47 \
  --with-protobuf-c-inc=/opt/local/include/google/protobuf-c
  --with-protobuf-c-lib=/opt/local/lib
  --with-protobuf-c=/opt/local
make
sudo make install

ポイントとなるのはconfigureで必ずproj47とprotobuf-cの場所を指定することです。特にprotobuf-cはincだけ指定してもダメだったりしてはまったりした。

次にpostgresqlgisというデータベースを作成します。注意点としてはMacPortsのデフォルトなので権限がかなり緩いです。

sudo su postgres -c 'createdb gis'
psql -U postgres -d gis -c 'CREATE EXTENSION postgis; CREATE EXTENSION hstore;'

あとは日本のデータをosm2pgsqlを使って投入します。
なお、以下のものでだいたい10GBぐらいメモリ消費するっぽいのでそれなりに気をつけてください。

wget http://download.geofabrik.de/asia/japan-latest.osm.pbf
osm2pgsql --create -U postgres -H 127.0.0.1 --database gis --slim -C 8000 --flat-nodes fuga japan-latest.osm.pbf

では、次にopenstreetmap-cartoを導入します。

git clone git@github.com:gravitystorm/openstreetmap-carto.git
cd openstreetmap-carto
./get-shapefiles.sh

get-shapefilesはopenstreetmap-cartoのproject.mmlから参照されるファイルをセットアップするために必ず実行します。

あとはTilemillのprojectフォルダ(~/Documents/Mapbox/project)にopenstreetmap-cartoのディレクトリをまるごとコピーします。

次にTilemillを起動するとOpenStreetMap Cartoというプロジェクトが増えてるので開きます。

f:id:smellman:20141016004602p:plain

するとこけます。
これはPostgreSQLのユーザが同じではないからです。
手っ取り早く動かすにはPGUSER変数を使いましょう。

PGUSER=postgres open /Applications/TileMill.app

これでうまく...いきません。

f:id:smellman:20141016004623p:plain

上記のエラーではNAMEという属性が無いというのです。

実はさっきopenstreetmap-cartoのget-shapefiles.shを実行した時に次のようなエラーが起きてました。

processing ne_10m_populated_places...
ERROR 1: Failed to create field name 'SCALERANK' : cannot convert to UTF8
ERROR 1: Failed to create field name 'NATSCALE' : cannot convert to UTF8
ERROR 1: Failed to create field name 'LABELRANK' : cannot convert to UTF8
ERROR 1: Failed to create field name 'FEATURECLA' : cannot convert to UTF8
ERROR 1: Failed to create field name 'NAME' : cannot convert to UTF8
ERROR 1: Failed to create field name 'NAMEPAR' : cannot convert to UTF8
ERROR 1: Failed to create field name 'NAMEALT' : cannot convert to UTF8
ERROR 1: Failed to create field name 'DIFFASCII' : cannot convert to UTF8
...

これはogr2ogrで処理してる部分でどうも変換できないというものなんですが、なんとなくogr側の問題な気がします。

とりあえず動かすためにogr2ogrから--configの指定を削除します。

diff --git a/get-shapefiles.sh b/get-shapefiles.sh
index 65fc306..acc6087 100755
--- a/get-shapefiles.sh
+++ b/get-shapefiles.sh
@@ -69,7 +69,7 @@ unzip $UNZIP_OPTS data/land-polygons-split-3857.zip \
 #process populated places
 echo "processing ne_10m_populated_places..."
 rm -f data/ne_10m_populated_places/ne_10m_populated_places_fixed.*
-ogr2ogr --config SHAPE_ENCODING UTF8 data/ne_10m_populated_places/ne_10m_populated_places_fixed.shp data/ne_10m_populated_places/ne_10m_populated_places.shp
+ogr2ogr data/ne_10m_populated_places/ne_10m_populated_places_fixed.shp data/ne_10m_populated_places/ne_10m_populated_places.shp
 
 #index
 echo "indexing shapefiles"

これであとはget-shapefiles.shを実行します。

processing ne_10m_populated_places...
Warning 1: One or several characters couldn't be converted correctly from UTF-8 to ISO-8859-1.
This warning will not be emitted anymore.

上記のようにWarningが出ますが、とりあえずはOKとしておきます。

これであとは先ほどと同様にTilemillのprojectフォルダにopenstreetmap-cartoディレクトリをコピーしてから、PGUSERを指定した状態でTilemillを起動します。

f:id:smellman:20141016004656p:plain

無事起動しました。

f:id:smellman:20141016004708p:plain

時間はかかりますが日本のデータは入れているのでちゃんとスタイルが入った状態で表示されます。

さて、ここで編集...と思ったら速攻困ったことになります。

f:id:smellman:20141016004722j:plain

mmsファイルが4つしか表示されていません。

How to handle overflowing stylesheet tabs? · Issue #190 · mapbox/tilemill · GitHub

具体的にはこのバグなんですが、ここに手っ取り早い解決方法があるので手動で直してしまいます。

vim /Applications/TileMill.app/Contents/Resources/assets/css/style.css

ここの.tabsの定義を以下のようにします。

.tabs {
  float:left;
  position:relative;
  /* overflow:hidden; */ /* comment out */
  overflow-y: scroll; /* add */
  height:30px;
  padding-left:10px;
  }

やってることはoverflowの定義をコメントアウトしてoverflow-y: scroll;を追加するというものです。

これで起動します。

f:id:smellman:20141016004802j:plain

スクロールバーが出てきて無事に最初に見えていたファイル以外が編集が可能となりました。

とりあえず新しいUIのものをリリースして欲しいのだけど、どうもデスクトップ版Tilemillは今後出てくるのかどうかが微妙な気がします。Mapbox Studioも出てしまったし...