smellman's Broken Diary

クソみたいなもんです

pandocでプログラムが記述された納品用PDFを作ってみる

仕事で一人プロジェクト(開発/コンサルティング)の時にはMarkdownとpandocでお客さん向けのPDFを作っていたんですが、デフォルトのテンプレートだとプログラムやシェルの動作などを記載したときにPDFではtext wrapが動かなくて特にシェルなんかは複数行に改行したり工夫していました。ただ、これをやっていくと精神がガリガリ削られてしまいます。

また、出力したPDFもちょっと格好悪いので使えそうなテーマを探してみました。

User contributed templates · jgm/pandoc Wiki · GitHub

今回は次のテンプレートが良さそうなので使ってみたというお話です。

GitHub - Wandmalfarbe/pandoc-latex-template: A pandoc LaTeX template to convert markdown files to PDF or LaTeX.

まず手元の環境ですが、 Homebrew 経由で入れた MacTex と pandoc となります。OSなどの違いは各自汲み取ってください。

とりあえず、導入をしてみます。

mkdir -p ~/.pandoc/templates/
cd ~/.pandoc/templates/
wget https://raw.githubusercontent.com/Wandmalfarbe/pandoc-latex-template/master/eisvogel.latex

あとは pandoc に --template eisvogel というオプションを追加すればよいのですが、単純にこれだけだとうまく動きません。

まず以下のような markdown があるとして、適当に処理してみます。

# openmaptilesでベクトルタイルの作成

本ドキュメントはopenmaptilesを利用してベクトルタイルのバイナリ(.mbtile形式)を作成するところまでを解説します。

## ターゲット

### ハードウェア

- Ubuntu 16.10 or Ubuntu 17.04
- 64bit CPU
- 4GBメモリ以上
- 30GB以上のディスクスペース
pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings

すると次のようなエラーになります。

! Package inputenc Error: Unicode char テ (U+30C6)
(inputenc)                not set up for use with LaTeX.

See the inputenc package documentation for explanation.
Type  H <return>  for immediate help.
 ...

l.308 \section{テストドキュメント}

Try running pandoc with --latex-engine=xelatex.
pandoc: Error producing PDF

メッセージに沿って xelatex を使うようにします。

pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings --latex-engine=xelatex

これでうまく行ったかと思ったら、日本語フォントが出てきません。

f:id:smellman:20170523041717p:plain

これを解決するには CJKmainfont のパラメータを与える必要があります。今回は完全な個人的な趣味で IPAexGothic を使います。

pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings --latex-engine=xelatex -V CJKmainfont=IPAexGothic

f:id:smellman:20170523041902p:plain

これで日本語もばっちり表示されるようになりました。

ただ、 usage を見るとYAMLでタイトルを拡張できるようです。なぜYAMLを閉じるのが ... なのかはわかりませんが、とりあえず markdown に組み込んでみます。

最終的なサンプルは以下のようにしました*1

---
title: "ベクトルタイルの作成方法"
author: [Taro Matsuzawa taro@georepublic.co.jp]
date: \today
...

# テストドキュメント

本ドキュメントはopenmaptilesを利用してベクトルタイルのバイナリ(.mbtile形式)を作成するところまでを解説します。

## ターゲット

### ハードウェア

- Ubuntu 16.10 or Ubuntu 17.04
- 64bit CPU
- 4GBメモリ以上
- 30GB以上のディスクスペース

### ソフトウェア

- Docker > 1.11.0
- Docker Compose > 1.7.1
- git
- make
- bc

## セットアップ

### utility

openmaptiles の ```quickstart.sh``` の実行に必要なコマンドをインストールします。

```bash
sudo apt-get install git make bc
```

### Docker

apt-getコマンドでdockerをインストールし、サービスを起動します。

```bash
sudo apt-get install docker.io
sudo service docker start
```

### Docker Compose

#### Ubuntu 16.10

Ubuntu 16.10ではパッケージで提供しているバージョンが古いため、手動でインストールをします。

```bash
sudo su -
curl -L https://github.com/docker/compose/releases/download/1.10.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
exit
```

#### Ubuntu 17.04

Ubuntu 17.04ではパッケージが提供してるものを利用します。

```bash
sudo apt-get install docker-compose
```

これで出力してみましょう。

pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings --latex-engine=xelatex -V CJKmainfont=IPAexGothic

f:id:smellman:20170523043120p:plain

これできれいになったと思ったのですが、よく見ると日付がドイツ語になっています。これはデフォルトがドイツ語になっているためなので、 README を参考にして英語にしてみましょう*2

pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings --latex-engine=xelatex -V CJKmainfont=IPAexGothic -V lang=en-US

f:id:smellman:20170523043611p:plain

これでうまくいきました。なお、日付のフォーマットを変更するのであれば --metadata date="`date +%Y-%m-%d`" などとしてdateの値を与えるとうまくいきます。

pandoc -f markdown_github test.md -o test.pdf --from markdown --template eisvogel --listings --latex-engine=xelatex -V CJKmainfont=IPAexGothic -V lang=en-US --metadata date="`date +%Y-%m-%d`"

ちなみに、僕の場合は最初にTable of Contentsをつけるので --toc オプションも追加します。この --toc オプションも言語設定によって影響があるので、このテンプレートを使う場合は -V lang=en-US をつけておくのがおすすめです。

なお、全て試していないのですが text wrap が有効なのは --listings オプションを追加したときで、他の Syntax Highlighting を設定するとうまく動きません。

というかそもそも --listings をつければ最初の text wrap の問題解決していたような気がするんだけど考えるのをやめました。

幸せになればいいんだよ!!!!なりてえよ!!!!

あ、ついでですが、これあくまでPDF向きなのでこのmarkdownをhtmlに出力すると変になるから気をつけてね!

*1:お客さんに怒られないようちょっと構成を変えてる

*2:http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf によると日本語はサポートされていないようです。

地図エンジニア死亡かるた

地図エンジニア歴がバイトを除いてそろそろ五年になるので、地図エンジニア死亡かるたを考えてみる。
なお、かるたってよくわからないので適当にあいうえお順で書いてみる。

あ: ArcGISは触ったことがないのに質問される
い: 伊能忠敬をそんなに知らない
う: 海の上に出て来るマーカー
え: 絵心がなくビジュアライゼーションを諦める
お: OpenLayers2
か: 漢字が読めない地名
き: 汚い図面
く: 苦しい修正仕様
け: 険しい3Dへの道
こ: 小受けが逃亡
さ: 殺意を覚える仕様書とスケジュール
し: 4月に案件がない
す: スケジュールがなぞの前倒し(お客さん都合)
せ: Sal◯sf◯rceに案件を持ってかれる
そ: SOAP
た: だいたいデータ量が多い
ち: 地図がそもそも読めない
つ: ツイッターで質問しても誰もわからない
て: 転送量の計算ミスで死ぬ
と: トイレのマッピングの重要性を痛感するまで五秒前
な: 夏休みはだいたい秋休み(もしくは冬休み)
に: 人月計算が適当
ぬ: Null Pointer Exception
ね: ネットでの地図界隈繋がり、だいたいリアルの繋がり、たまにお客さん
の: 納期がだいたい年度末
は: 破壊的仕様変更
ひ: 標準化
ふ: ブラウザーの知識が曖昧な人が多い
へ: 減らないというか増えていくExcelで管理されているタスクシート
ほ: 本気でその仕様でやるつもり?
ま: まじでその仕様でやるつもり?
み: 見積もりにG◯◯gle API for W◯rksの値段を入れ忘れる(てへぺろ)
む: 無理なスケジュール
め: メモリリークとの戦い
も: 文字コードで死ぬ(特にShapefile)
や: 休みも趣味で地図を描く
ゆ: 有給も趣味で地図を描く
よ: 呼ばれて見積もり作ってさんざんミーティングに時間使って案件にならない
ら: 来期もよろしくお願いいたします
り: リアルな3Dに見えるようにごまかす
る: ルーラを使って好きな街に飛んでいきたい
れ: 連休明けのメールチェック
ろ: 老朽化したGISとなんか違う座標のデータ
わ: 忘れた頃に復活する案件
京: 京都の住所

OpenStreetMapのMapbox Vector Tileを作ってみた その2 、あとスタイルの編集とかフォントとか

昨年12月にOpenStreetMapのMapbox Vector Tileを作ってみたというタイトルの記事を書きましたが、その時扱っていたosm2vectortilesが終了*1となり、現在はopenmaptilesという存在になりました。

openmaptiles自体はosm2vectortilesと同様にDockerを使ったタイルのビルド方法の提供などをしていますが、openmaptiles.comの方では非商用向けにフリーのタイル配信(githubなどからアクセストークンの取得が必要)や、月40ドルでのタイル配信サービスの提供していたり、iOS/Androidアプリのソースコードの販売などを行っています。

今回はopenmaptilesを使ってMapbox Vector Tileの作成と、Maputnikを使ったスタイルの編集や、既存のスタイルにM+とIPAの合成フォントを使ってみた例の紹介など、前回の記事よりもちょっと突っ込んだ内容となります。ただ、似たような説明の所はちょっと割愛してるので、そこだけ気をつけてください。

openmaptilesでMapbox Vector Tileの作成

さて、さっそくMapbox Vector Tileの作成をしていきます。

今回は前回同様Ubuntu 16.10をインストールしたThinkpad X220での解説に加え、OS X El Capitan上でのやり方を追加で解説します。ただ、OS X上でのやり方はあくまで追加となり、Dockerに慣れてない人は適当にがんばってください(何

まず、今回必要な環境は以下のものとなります。

  • Docker 1.10.0以上
  • Docker Compose 1.6.0以上*2

DockerをUbuntu 16.10で動かす方法については前回の記事を参照にしてください。

Docker ComposeについてはUbuntu 16.10ではパッケージのバージョンが古いため、別途インストールが必要となります。

sudo su -
curl -L https://github.com/docker/compose/releases/download/1.10.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
exit

このあと新しいターミナルウィンドウを開くなりすればdocker-composeの新しいバージョンが動きます。

あとは公式ドキュメントに沿って作業をします。

git clone https://github.com/openmaptiles/openmaptiles.git
cd ./openmaptiles
docker-compose pull
./quickstart.sh

quickstart.shはデフォルトではアルバニア*3のpbfファイルをgeofabrikからダウンロードしてMapbox Vector Tilesの作成とmbtiles形式(SQLite)への格納を行います。

この後、一旦tileserver-glを動かして検証をします。

make start-tileserver

あとは http://localhost:8080 へアクセスします。

f:id:smellman:20170204023959p:plain

f:id:smellman:20170204024014p:plain

さて、quickstart.shでは主に以下のことを行います。

  1. 生成する国を指定
  2. 以前のDocker imagesのクリーンアップ
  3. 必要なら新たにgeofabrikから対象となる国のpbfファイルのダウンロード
  4. 必要ならdocker-composeに渡す設定ファイルを作成
  5. Mapbox Vector Tileに必要なdocker-composeのタスクを順次実行

また、実際のタスク自体はMakefileの方に何をするかが書かれています。

では、次に日本のタイルを作成します。

これは非常に簡単です。

./quickstart.sh japan

以上です。

ですが、動かしてもズームレベル7までしかないので微妙なので、ズームレベルを0から14*4まで生成してみますが、一度quickstart.shを実行した後にやっかいなことがあります。

  1. docker-compose の一部タスクで利用される ./data/docker-compose-config.yml はgeofabrikからpbfをダウンロードするタイミングで作成される
  2. 1. のタスク自体の設定がレポジトリにある .env ファイルを参照している
  3. geofabrikからダウンロードしていたpbfがあると geofabrik からpbfをダウンロードするステップは省略されてしまう

公式ドキュメントなどにはズームレベルを変更する場合は .env ファイルを編集しろとあるのですが、実際には .env ファイルを変更したあとに再度 ./quickstart.sh を実行しても ./data/docker-compose-config.yml が変更されないため、結局のところズームレベル7までしか作成されないということになります。

なので、以下のいずれかの方法を取る必要があります。

  • .env ファイルを編集したあとに ./data 以下にある一度ダウンロードした pbf を削除してから再度実行
  • .env ファイルを編集したあとに ./data/docker-compose-config.yml も編集してから再度実行

ただし、 ./data/docker-compose-config.yml を生成する処理自体も docker によって実行されるので、 ./data/docker-compose-config.yml を編集する場合はroot権限が必要になります。

sudo vim ./data/docker-compose-config.yml

さて、この妙な所を説明したところで、 .env ファイルと ./data/docker-compose-config.yml の説明をします。

まずは .env ファイルです。

POSTGRES_DB=openmaptiles
POSTGRES_USER=openmaptiles
POSTGRES_PASSWORD=openmaptiles
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
QUICKSTART_MIN_ZOOM=0
QUICKSTART_MAX_ZOOM=7
DIFF_MODE=false

このような設定がありますが、基本的には QUICKSTART_MAX_ZOOM を作成したい MAX ZOOM に設定してあげればよいです。

個人的にはなぜこのファイルがレポジトリに突っ込んであるのか微妙ですが...

次に、 ./data/docker-compose-config.yml です。

version: "2"
services:
  generate-vectortiles:
    environment:
      BBOX: " 121.8236704, 23.7651400, 152.1180000, 46.9388859"
      OSM_MAX_TIMESTAMP : "2017-02-01T21:10:42Z"
      OSM_AREA_NAME: "japan"
      MIN_ZOOM: "0"
      MAX_ZOOM: "7"

ここにはズームレベルの他に ./quickstart.sh の引数として指定したエリアの名前とダウンロードしたPBFファイルのタイムスタンプ、そしてPBFファイルから算出したBounding Boxの値*5が格納されます。

ここもMAX_ZOOMを14にしてあげればよいということになります。

あとは、前回同様作成すればOKです。流れとしてはこんな感じです。

vim .env
sudo vim ./data/docker-compose-config.yml
./quickstart.sh japan

ちなみに./quickstart.shで国名を間違えるとPBFファイルが消されるので気をつけてください...

この後は make start-tileserver でタイルサーバを実行してもよいのですが、バックアップの意味も込めて別のところにコピーして動かしてみましょう。

mkdir ~/tiles
cp data/tiles.mbtiles ~/tiles/japan.mbtiles
cd ~/tiles
docker pull klokantech/tileserver-gl
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

これでlocalhost:8080にアクセスすればtileserver-glを通してMapbox Vector Tileへアクセスが可能となります。

さて、OSXの方ですが、quickstart.shでdateコマンドが GNU date でしか使われていないオプションがあり止まってしまいます。そのため、dateコマンドのオプションを変更する必要があります。Pull Requestは出して置いたのでそれを参考にしてください。

あとはgawkコマンドとmd5sumコマンドが無かったのでそれをHomebrewとかで入れます。

brew install gawk
brew install md5sha1sum

他にも足りないものがあるかもしれませんが、まぁそこは適当に調べてください。

スタイルの編集とフォントの追加

さて、今回は前回ノータッチだったスタイルエディタ周りについても紹介します。

Mapbox Vector Tileのスタイルの編集はいくつか方法がありますが、今回はlocal環境のみで完結できるMaputnikを紹介します。

Maputnikはオンライン、オフライン問わず利用できるMapbox Vector Tileのスタイル編集ソフトウェアです。ES6やReactを使って開発をされていて、また開発資金をkickstarterで募集したことも有名です。

Maputnikはオンラインで利用ができ、styleのjsonファイルをアップロードして編集することが可能なので非常に便利ですが、当然タイルのアクセスにはSSLが必要なのでlocal環境で構築したものの編集にはオンラインモードは使うことができません。

ですが、local環境で動かせばSSL無しで動作可能というわけでやってみます。

先に tileserver-gl を8080ポートで動いているということが前提とします。

次にmaputnikをダウンロードして起動します*6

git clone git@github.com:maputnik/editor.git
cd editor
npm install
npm start

あとは http://localhost:8888 にアクセスします*7

f:id:smellman:20170204024811p:plain

次にヘッダにあるOpenから元ネタとなるスタイルを選択します。今回はアップロードせずにKlokantech Basicを選びます。

f:id:smellman:20170204025033p:plain

次に、まず行方不明にならないよう日本付近を表示させておきます。

そして、Sourcesを選び、TileJSON URLを http://localhost:8080/data/v3.json に変更します。

f:id:smellman:20170204025525p:plain

そしてwaterのレイヤーのPaint Propertiesをいじっておもむろに海を血の海にしてやります*8

f:id:smellman:20170204030105p:plain

さて、血の海ごっこに飽きたところで次にフォントをMigMixに変えてみたいと思います。今回はroad_major_labelのみを対象としてみます。

まず最初にMapbox GLにフォントを対応させるためにMapbox GL用にglyphを作成します。

これには openmaptilesのfontsというレポジトリを利用します。

先に適当なディレクトリにmix-mplus-ipaのフォントを展開しておき、以下のようにします。

git clone git@github.com:openmaptiles/fonts.git
cd fonts
cp ../font/mix-mplus-ipa/*/*.ttf mix-mplus-ipa
npm install genfontgl
./generate.sh

これで _deploy というディレクトリにmix-mplus-ipa を含むフォントのディレクトリが作成されます。

次にこのディレクトリでhttpサーバを立ち上げます。今回は serve というnodeのパッケージを使います。

npm install -g serve
cd _deploy
serve -C -p 18888

今回は適当にCORS対応でかつ18888ポートで立ち上げておきます。

次に、MaputnikのStyle SettingsからGlyphs URLを http://localhost:18888/{fontstack}/{range}.pbf に変更します。

f:id:smellman:20170204030356p:plain

すると日本語どころか全ての文字が消えてしまいます。

URLを見ると、

http://localhost:18888/Klokantech%20Noto%20Sans%20Regular,Klokantech%20Noto%20Sans%20CJK%20Regular/36096-36351.pbf

となっていて、fontstackに対してKlokantech Noto Sans RegularとKlokantech Noto Sans CJK Regularの2つが与えられていることがわかります。

すくなくともMapbox Vector Tileでサーバ側のフォントの扱いがややこしいのがわかりましたが、とりあえずスタイルを編集してこれを回避します。

まず、 road_major_label のスタイルを開きます。

ここで、フォントを編集したいのですが、現行のMaputnikでは2つ以上のフォントがある場合GUIでの編集が上手くいかないという問題があります。

なので、下の方のJSON EditorでKlokantech Noto Sans Regularを削除しちゃいます。

f:id:smellman:20170204030656p:plain

これでちゃんと日本語の表示が出てくるようになりました。

ではこの表示のフォントをmigmix1p-boldに変更してみます。

f:id:smellman:20170204030836p:plain

これでフォントが変更されます。

f:id:smellman:20170204030946p:plain

他の所も同じように変更していけばよいという感じになります。

まとめ

Mapbox Vector Tileに関するものはスタイルの編集などを含めるとかなり敷居が高かったんですが、Mapuntikの出現で一気に敷居が下がりました。

また、openmaptilesでは以前よりもより簡単にタイルの構築が行えるようになっていてこちらも敷居が下がってよかったという感じです。

まだまだサーバ周りでは調査は必要ですが、だいぶこなれてきたと思います。

みなさんもぜひタイル作成にチャレンジしてみましょう。

あ、MacでビルドしてたプロセスどころかDockerの全プロセスが飛んだ...orz

*1:no longer maintainedなんで終了だと思ってる

*2:docker-compose.ymlにversion "2"と書いてある

*3:イタリアのかかとの裏とおぼえました

*4:なお、openmaptilesではズームレベル14までは取り込む内容がズームレベルごとに変わりますが、ズームレベル14以上は特にタイルの内容に変更がないのと十分な情報量があるのでズームレベル14までとしています。詳しいことはlayersフォルダの中にあるSQLを参考にしてください

*5:実際にはosmconvert --out-statisticsを使っていて、その出力ファイルが./data/osmstat.txtとして残る

*6:Maputnik CLIという便利そうなものがあるらしいがそれはインストール方法が良くわからなかったので割愛します

*7:この場合だと2つ以上のブラウザでアクセスすると死ぬっぽいので注意

*8:毎回なんで俺こんな遊びやるんだろう...

個人的な #2016ブレイクコアベスト 振り返り

今年も一年よいブレイクコアを聞きまくっていました。
というわけで #2016ブレイクコアベスト を振り返ってみようと思います。

まずは楽曲です。

アーティスト 楽曲名 収録元 レーベルと購入/ダウンロードリンク
Synthamesk Lamenski's Raw Deal(2007年リリース、2016年再販) self release/bandcamp
PZDC Team feat Fexomat Snejok RRR040 Ringe Raja Records/archive.org
虚空戦士 1 Dollar = 123.32 Yen (11/17/2015 2:18pm ET Mix) Internet Teknique Dochakuso Records
E-coli Mondo Kane The Star of Munster E.P. Otherman Records
ninzoc ASTEROID CANDY OVERDOSE SWEET DISEASE EP Otherman Records
CDR Amen Must Die ULTIMATE JUNGLE FREAKS VOL.0.95 Dochakuso Records
Flatland Sound Studio Sorry, Donny S02 - Sorry, Dunny Bottle Imp Productions/bandcamp
Laxenanchaos Not Perfect Atom Reaction Formula anybody universe/bandcamp
Marcanta The Collector MOZYK016 - The Marshalling Yard MOZYK/bandcamp
Datach'i Waveguiding System Planet Mu/bandcamp
Ca5 utopia DYSTOPIA ESC TRAX/bandcamp
DJIPE Turtle Devastation Wave People=Pigs The Third Movement/bandcamp

基準としては今年手に入れた物からチョイスなので、SynthameksのRaw Dealは2007年のリリースとやたらと古いものも入っています。

ちなみに上の順番は順位とかではなく印象に残ってるものを一覧に上げているという感じです。その中でもSynthameskのLameski'sは本当によくて、Twitterでは #ベストブレイクコア2016 として書いてみました。

いちおうジャンルとしてはDJIPEだけ違う気がしますが、まぁ別にいいだろうというチョイスです。というかこれほとんど今年聞いた曲のベストだし。

あと、ninzocさんのASTEROID CANDY OVERDOSEはひさびさに泣いたトラックですね。たまにブレイクコアで泣くことがあるんだけど、今年はこの曲が後半にぶっこまれて非常に満足しています。

そんなわけで、次はベスト・アルバムなんですが、これは1つにしぼりました。

MarcantaのMOZYK016 - The Marshalling Yardです。

楽曲の方にも入れた9曲目のThe Collectorは最初から珍妙な音が支配するまじでへんてこな曲で展開はもちろんめちゃくちゃ。
Marcantaは去年のMOZYKのスプリットで10分を超えるブレイクコアで衝撃が走りましたが、今回のこのアルバムは最初から最後まで無茶苦茶な展開にブレイクコアには珍しい6分を超える曲ばかりで、本当にやばかった。
SynthameskのRegressionもすごくよかったけど、今年は完全にMarcantaに持って行かれたという感じがします。

で、今年はブレイクコアというジャンルがまた面白い流れになってきたと思います。
shitmatのJapanツアーなんかはその最たる例で、干してある靴下を生で見てまじで吹いた。
天一アーメンからのKei Torikiさんのリリースや、umioさんのリミックス集、そして来年のBangfaceに日本勢の出演決定など日本においてもどんどん良い流れが出来ていると思います。

来年もブレイクコアを全力で楽しもうと思います。
みなさん、よいHawawaを〜
(ってかHawawaさん見にまじでイギリスいきてー)

wellknown.jsを使ってブラウザ上でWKTをさくっと使ってみる

GIS関連のWebサイトを作っている時にAPI側はGeoJSONを吐くようにしていても、管理画面は別にGeoJSONを取りに行くAPIを叩くのではなく直接オブジェクトをHTMLに書き出して取扱いしたいなーって時があったりします。

そんなわけで、WKTを直接ブラウザで扱ってみようっていう話です。
今回使うのはmapboxのwellknown.jsです。

github.com

ソースはこんな感じです。


wellknown.js demo

デモはこんな感じ。

https://cdn.rawgit.com/smellman/c140d72539cd4acb261d15a78bc2a024/raw/fff3e3423d441587a7f81ddbfc2e602e6b3b56e6/index.html

まぁ、単にwellknown自体がbrowserifyされた状態で配布されているので、それを読み込むと wellknown.parse とかでアクセスできるっていうだけです。
コレだけでもクソ便利すぎてやばい。

OpenStreetMapのMapbox Vector Tileを作ってみた

smellman.hatenablog.com

この記事にあるosm2vectortilesは古いため、Dockerのインストール方法以外は新しい記事を参考にしてください。

OpenStreetMap Advent Calendar 2016 3日目の記事です。

今回はOpenStreetMapのデータを使ってMapbox Vector Tileを自分で作ってみることに挑戦してみました。

対象となる環境はUbuntu 16.10をインストールしているThinkpad X220です。スペックは以下の通りです。

CPU
Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz (物理2コア/論理4コア)
Memory
16GB
Storage
SSD 240GB

なお、このマシンは本体と換装パーツ込で33000円ぐらいで揃えたものです*1

さて、余談はよいとして、Mapbox Vector Tileを作るに当たって、今回はosm2vectortilesGenerate your own vector tilesを参考に行いました。

まず先にosm2vectortilesについて軽く紹介したいと思います。

このプロジェクトはOpenStreetMapをベースとしたMapbox Vector Tileの地図タイルを自由にダウンロードできるようにするもので、世界全域の地図タイル以外にもいろんな地域の地図タイルがmbtiles形式でホスティングされています。また、地図タイルの作成にdockerを活用しているのが特長です。また、地図タイルを長くやってる人にはMapTilerというプロダクトで有名なKlokan Technologiesがスポンサーをしていることも話題になりました。俺も久々にこのカンガルーの会社ロゴ見てびっくりした(汗

さて、さっそく作業をしていきます。まずはDockerとDocker-composeをインストールします。

sudo apt-get install docker docker-compose
sudo service docker start
sudo docker run hello-world

次に一般ユーザで作業をするためにdockerグループにユーザを追加します。

sudo usermod -aG docker $USER

これで一旦リブートをして再度ログインして、一般ユーザで動作するかを確認します。

docker run hello-world

dockerが一般ユーザで動くようになったら次にosm2vectortilesをgithubから落としてきます。

mkdir -p ~/develop/osm
cd develop/osm
git clone https://github.com/osm2vectortiles/osm2vectortiles.git
cd osm2vectortiles

今回の作業パスは ~/develop/osm/osm2vectortiles とします。

まずは手順通りにdocker-composeでPostGISのコンテナを取得します。

docker-compose up -d postgis

ここで一旦別のディレクトリを作成して、そこに今回使うファイルをダウンロードします。

まずはテストということで、bbbikeから東京の範囲のpolyファイルとPBFファイルをダウンロードします。polyファイルは後でBBOXの範囲を計算するのに使います。

mkdir -p ~/tmp/osmdata
cd ~/tmp/osmdata
wget http://download.bbbike.org/osm/bbbike/Tokyo/Tokyo.poly
wget http://download.bbbike.org/osm/bbbike/Tokyo/Tokyo.osm.pbf

次にPBFファイルをosm2vectortiles/importに配置します。

cp ~/tmp/osmdata/Tokyo.osm.pbf ~/develop/osm/osm2vectortiles/import/

次にまた別の作業ディレクトリを作成して、OpenStreetMapDataからwater-polygonsとsimplified-water-polygonsを、Natural Earthからnatural_earth_vectorSQLiteのファイルをダウンロードします*2

mkdir -p ~/tmp/osmpoly
cd ~/tmp/osmpoly
wget http://data.openstreetmapdata.com/water-polygons-split-3857.zip
wget http://data.openstreetmapdata.com/simplified-water-polygons-complete-3857.zip
wget http://naciscdn.org/naturalearth/packages/natural_earth_vector.sqlite.zip

これらを全てunzipしてから必要なファイルをosm2vectortiles/importにコピーします。

unzip water-polygons-split-3857.zip
unzip simplified-water-polygons-complete-3857.zip
unzip natural_earth_vector.sqlite.zip
cp ~/tmp/osmpoly/water-polygons-split-3857/water_polygons.* ~/develop/osm/osm2vectortiles/import/
cp ~/tmp/osmpoly/simplified-water-polygons-complete-3857/simplified_water_polygons.* ~/develop/osm/osm2vectortiles/import/
cp ~/tmp/osmpoly/packages/natural_earth_vector.sqlite ~/develop/osm/osm2vectortiles/import/

最終的にosm2vectortiles/importディレクトリが以下のようになります。

% ls -l import
合計 1079640
-rw-r--r-- 1 btm btm  18088604 12月  3 15:11 Tokyo.osm.pbf
-rw-r--r-- 1 btm btm 414519296 12月  3 15:16 natural_earth_vector.sqlite
-rw-r--r-- 1 btm btm         6 12月  3 15:16 simplified_water_polygons.cpg
-rw-r--r-- 1 btm btm     74837 12月  3 15:16 simplified_water_polygons.dbf
-rw-r--r-- 1 btm btm       853 12月  3 15:16 simplified_water_polygons.prj
-rw-r--r-- 1 btm btm  29614272 12月  3 15:16 simplified_water_polygons.shp
-rw-r--r-- 1 btm btm     49948 12月  3 15:16 simplified_water_polygons.shx
-rw-r--r-- 1 btm btm         6 12月  3 15:15 water_polygons.cpg
-rw-r--r-- 1 btm btm    452789 12月  3 15:15 water_polygons.dbf
-rw-r--r-- 1 btm btm       847 12月  3 15:15 water_polygons.prj
-rw-r--r-- 1 btm btm 642308932 12月  3 15:15 water_polygons.shp
-rw-r--r-- 1 btm btm    301916 12月  3 15:15 water_polygons.shx

では、またosm2vectortilesの作業ディレクトリに戻って手順書通りに作業を進めていきます。

docker-compose up import-external
docker-compose up import-osm
docker-compose up import-sql

ここまできたらあとはexportをするのみとなりますが、BBOX(範囲)のパラメータを与えないと世界中が対象となってしまうので、BBOXの値を計算します。
BBOX自体はwest,south,east,northの順にパラメータを与えます。
なので、経度180度線を気にしなければ、min(longitudes),min(latitudes),max(longitudes),max(latitudes)となります。
今回の東京の範囲のPOLYファイルをみると今回は単純な四角形なので値は簡単にわかります。

Tokyo
1
   139.62  35.56
   139.95  35.56
   139.95  35.78
   139.62  35.78
END
END

これを読めば、BBOX=139.62,35.56,139.95,35.78という値がとれるので、それを使ってドキュメントに沿ってexportを実行します。今回はzoom 9からzoom 14までを作成してみます。

docker-compose run \
  -e BBOX="139.62,35.56,139.95,35.78" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="14" \
  export
docker-compose up export

これらを実行するとexport以下にtiles.mbtilesというファイルができあがります*3。ただし、dockerで生成したためか所有者がrootのファイルになっているので、所有者の変更とあとでファイルを変更しても良いように別名でコピーをとっておきます。

sudo chown $USER:$USER ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tiles.mbtiles ~/develop/osm/osm2vectortiles/export/tokyo.mbtiles

あとは実際に動作するかをチェックします。
これはGetting Startedではtileserver-gl-lightを使うようになっているのですが、tileserver-gl-light自体はすでに古く、tileserver-glが最新になっているのでそちらを使うようにします。
ただし、tileserver-glの方は現在npm installに失敗してしまいます*4
なので、今回はtileserver-glのdockerを使って検証してみます。

cp ~/develop/osm/osm2vectortiles/export/tokyo.mbtiles /tmp/
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

これでサーバが動いているIPアドレスの 8080 port へアクセスします。

f:id:smellman:20161203192934p:plain

ここでBasicスタイルのVectorタイルを選択します。

f:id:smellman:20161203193118p:plain

初期状態でzoom 2なのですが、何も出ててきません。しかし、ズームアウトすると...

f:id:smellman:20161203193151p:plain

全球が出てきます。そこで東京の方に見ていきます

f:id:smellman:20161203193249p:plain

途中まではすごく雑です。

f:id:smellman:20161203193440p:plain

作成したズーム9からはちゃんと出てきます。

f:id:smellman:20161203193527p:plain

f:id:smellman:20161203193547p:plain

とりあえずズーム14までちゃんと作られたのが確認できます。

では、せっかくなのでズーム0〜ズーム8をマージしてみましょう。

マージはドキュメントにあるようにMapboxのmbutilにあるpatchというシェルスクリプトを使います。なお、sqlite3が入っている必要があります。また、ドキュメントにあるmbtilesのダウンロードはズームレベル5までのもの*5なので、ダウンロードページからPlanet from zoom level 0 to 8を選んでダウンロードします。

cd ~/develop/osm/osm2vectortiles/export
wget https://osm2vectortiles-downloads.os.zhdk.cloud.switch.ch/v2.0/planet_2016-06-20_7088ce06a738dcb3104c769adc11ac2c_z0-z8.mbtiles
wget https://raw.githubusercontent.com/mapbox/mbutil/master/patch
chmod +x patch
cp tokyo.mbtiles tokyo-and-planet.mbtiles
./patch planet_2016-06-20_7088ce06a738dcb3104c769adc11ac2c_z0-z8.mbtiles tokyo-and-planet.mbtiles

これも同様に動かしてみましょう。

rm /tmp/tokyo.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tokyo-and-planet.mbtiles /tmp
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

f:id:smellman:20161203193638p:plain

最初から全球がレンダリングされていていい感じです。

f:id:smellman:20161203193710p:plain

f:id:smellman:20161203193730p:plain

日本の形もちゃんと出てきます。

f:id:smellman:20161203193800p:plain

ただし、zoom 8あたりで残念なマージになりました。
まぁ、ファイルを上書きしてしまってるのでしょうがないとはいえます。

とりあえずこれである程度の地図タイルが作れるようになりました。

では、せっかくなので日本全国の地図タイルを作ってみましょう。

まずはGeofebrikのOSMデータのミラーサイトから日本全体のデータをダウンロードします。

cd ~/tmp/osmdata
wget http://download.geofabrik.de/asia/japan.poly
wget http://download.geofabrik.de/asia/japan-161202.osm.pbf

次に、以前の作業に使ったpbfファイルの削除と、以前使ったdocker-composeを全て削除します。
docker-composeを削除するのはどうもpgdataを削除したぐらいでは何かが残ってしまっていてimposm3のmergeの処理が走ってしまうためです。
ただ、すでにイメージ自体はダウンロードしてるので再構築自体は高速です。

cd ~/develop/osm/osm2vectortiles
rm import/Tokyo.osm.pbf
docker-compose stop postgis
docker-compose rm -f export
docker-compose rm -f import-sql
docker-compose rm -f import-osm
docker-compose rm -f import-external
docker-compose rm -f postgis
docker-compose rm -f pgdata
docker-compose rm -f cache

あとは以前の手順と同様に再構築を進めます。

cp ~/tmp/osmdata/japan-161202.osm.pbf import
docker-compose up -d postgis
docker-compose up import-external
docker-compose up import-osm
docker-compose up import-sql

次にBBOXを取り出したいのですが、日本の範囲として定義されているPOLYファイルはPOLYGONになっていてちょっと計算が面倒なので、とりあえずスクリプトをでっち上げたのでこれを使ってBBOXの値をゲットします。

cd ~/tmp/osmdata
wget https://gist.githubusercontent.com/smellman/a2770f3a6c4717315cc0a4538b985a1c/raw/1008b6d5f061fb7c0eeaddef86d8faf68f1a6d57/poly2bbox.py
% python poly2bbox.py japan.poly
BBOX=122.5607,21.20992,153.8901,45.80245

こんな感じで取れるのでこれをBBOXのパラメータとして使います。
なお、今回はMAX_ZOOMを13としています。というのはどうもズーム9から14で試したら17時間ぐらい処理にかかるらしく、今日のAdvent Calendarに間に合わないので...*6

docker-compose run \
  -e BBOX="122.5607,21.20992,153.8901,45.80245" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="13" \
  export
docker-compose up export

できあがったのはだいたい550MBぐらいのmbtilesで、 exportの処理時間はおよそ3時間ぐらい、importを含めてだいたい4時間ぐらいでいけました*7

あとはバックアップを取ってからtileserver-glでチェックをしてみます。

rm /tmp/tokyo-and-planet.mbtiles
sudo chown $USER:$USER ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tiles.mbtiles ~/develop/osm/osm2vectortiles/export/japan.mbtiles
cp ~/develop/osm/osm2vectortiles/export/japan.mbtiles /tmp
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

f:id:smellman:20161203201425p:plain

東京周辺はこんな感じです。

f:id:smellman:20161203201443p:plain

千葉市周辺、ちょっとレンダリング対象が少ない感じです。

f:id:smellman:20161203201500p:plain

伊豆大島も建物がレンダリングされていないので寂しいです。

f:id:smellman:20161203201518p:plain

三宅島も同様です。

やっぱりzoom 14の壁が厚いなーという印象ですね。
とりあえずzoom 14だけ足せるかこんなコマンド打って放置しておきます。

sudo chown root:root ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
docker-compose run \
  -e BBOX="122.5607,21.20992,153.8901,45.80245" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="14" \
  export

あと13時間後らしいので、お楽しみですね!!!!!1

*1:今日の小江戸LUGはこのネタをやる予定だったんだけど、この記事がニャオスだったので不参加にしました...

*2:なおドキュメントにはどのファイルを落とすのかが具体的に書いてないので注意してください、僕はソースコードを読んでダウンロードするファイルを判断しました

*3:最後のdocker-compose up exportが必要かどうかっていうとちょっと謎なんだけど、とりあえずドキュメント通りにやってる

*4:たしかMapboxの方のパッケージングでNode 4.xでバグがあるのが引っかかっているような気がします。なお、tileserver-gl-lightの方はUbuntu 16.10でnpm installができ、動作しています

*5:ちなみにこれURLが古くリンク切れしてる

*6:そもそも日本全域自体結構範囲がありますが、それ以外にもBBOXが四角形なので必要のない範囲のレンダリングも必要となってしまいます

*7:ズームレベルを1つ上げると単純に処理が4倍になりますが、データがほとんど無い範囲が多いとそれだけPostgreSQLから帰ってくるものが少なくなるはずなので時間は減るはずです

iCloudカレンダーにspamの予定が突っ込まれたって話

さっきiCloudカレンダーにspamの予定が大量に突っ込まれた。
内容はこんな感じ。

f:id:smellman:20161101154022p:plain

たぶん怪しいサイトへのリンクと、参加通知をすることで相手のアドレスを調べるのを目的としてると思われる。

とりあえずそのまま削除をしようとするとこのようになって削除できない。

f:id:smellman:20161101154028p:plain

どうやら通知自体が必須となってしまっている模様。
なので、とりあえず通知無しで削除する方法を調べていたら以下のサイトに方法が書いてあった。

https://discussions.apple.com/thread/7637683?start=0&tstart=0

I've been getting spam invites as well. It's a workaround, but I got rid of it by creating a new iCloud calendar, moved the invite to that, and then deleted it.

というわけでiCloudのカレンダーに「名称未設定」という名前のカレンダーを作成し、予定をそこに移動してからカレンダーごと削除をしてみた。
すると通知せずに削除というものがでてきた。

f:id:smellman:20161101160057p:plain

まぁ、本当に通知してないかはわからないけど、たぶんこれでOKじゃないかなと思う。

あと関係ないけど、spamって打とうとすると間違ってsmapって打ってしまっていろいろ焦った。