OpenMapTilesとgo-staticmaps

この記事は[モバイルファクトリー Advent Calendar 2017](https://qiita.com/advent-calendar/2017/mobilefactory) 10日目の記事です。

今回は、`go-staticmaps`というライブラリを紹介したいと思います。
https://github.com/flopp/go-staticmaps

# go-staticmaps
このライブラリは、地図のタイルデータ(地図のデータを四角く小さく切ったようなもの)に対してマーカーを置いたりパスを引くことができたり、最終的に画像として出力することができます。

手軽に自分のオリジナルの地図画像を作りたいときにもってこいのライブラリです。

# さっそく使ってみる
といきたいところですが、その前に、今回使うタイルデータを準備します。
今回は[OpenMapTiles](https://openmaptiles.org/)の地図タイルデータを使用します。

実は、自前でタイルデータを用意しなくても、go-staticmapsはデフォルトでOpenStreetMapのタイルデータを使用して地図を表示させることができます。
しかし、OpenMapTilesはデフォルトでいくつかスタイルが用意されていて、自分好みに変えることもできるため、今回はあえて自分でタイルサーバを立てて使ってみることにします。

## タイルデータの準備
準備は簡単です。公式でDockerを使ってタイルサーバを立てる手順が提供されているので、その通りに進めていきます。
https://openmaptiles.com/server/#install

少し時間はかかりますが、特につまるところはないと思われるので、詳細は割愛しますが、日本全体のタイルデータをダウンロードしてlocalhostでつなぐとこんな感じな地図が表示されると思います。
<img width="890" alt="スクリーンショット 2017-12-09 22.09.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/52234/452b7767-ac07-728d-5a49-3df931826967.png">

## go-staticmapsを使う
それではgo-taticmapsを使っていきましょう。
最初にソースコードを載せます。

```go
package main

import (
  "image/color"
  "github.com/flopp/go-staticmaps"
  "github.com/fogleman/gg"
  "github.com/golang/geo/s2"
)

func newTileProvider() *sm.TileProvider {
  t := new(sm.TileProvider)
  t.Name = "example-map"
  t.Attribution = "(c) OpenMapTiles"
  t.TileSize = 256
  t.URLPattern = "http://localhost:32769/styles/klokantech-basic/%[2]d/%[3]d/%[4]d.png"
  t.Shards = []string{"a", "b", "c"}
  return t
}

func main() {
  ctx := sm.NewContext()
  ctx.SetTileProvider(newTileProvider()) //タイルデータ指定
  ctx.SetSize(400, 300)
  ctx.SetZoom(17)

   //マーカーを追加
  spot := s2.LatLngFromDegrees(35.6259434, 139.7251859)
  ctx.AddMarker(sm.NewMarker(spot, color.RGBA{0xff, 0, 0, 0xff}, 16.0))

  // 画像として出力
  img, _ := ctx.Render()
  gg.SavePNG("my-map.png", img)
}
```
これをmain.goとして保存して実行すると、以下の画像が出力されます。
![my-map.png](https://qiita-image-store.s3.amazonaws.com/0/52234/3c02a039-2652-f945-15a1-feaccd81107e.png)

### 解説
ソースコードの解説をしていきます。

**`newTileProvider`関数**
地図の表示に使うタイルデータを指定しています。
(最初にも言ったようにデフォルトのタイルデータを使用するのであれば、この関数は必要ないです。)
ここでは先ほど準備したOpenMapTilesのタイルデータのURLの指定の他にもいくつかパラメータを設定していますが、`URLPattern`と`TileSize`以外は必須ではないので、なくてもかまいません。
`Attribution`を指定しなければ画像の下の黒い帯は表示されません。
`URLPattern`の`%[2]d`、`%[3]d`、`%[4]d`は、それぞれズーム、x座標、y座標に対応しています。

URLPatternのURLは以下の場所から取得しましょう。そして`{z}`となっているところを`%[2]d`という具合にxとyも変換すれば大丈夫です。
<img width="966" alt="スクリーンショット 2017-12-10 0.11.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/52234/d8072501-71eb-b545-d0e6-01f2187e06b0.png">


**`main`関数**
`ctx.SetSize`や`ctx.SetZoom`は任意で設定できるパラメータです。地図画像全体のサイズ、ズームを指定できます。
指定しなければよさげなサイズやズームに自動で調整されます。

マーカーを追加する部分では、座標を一度`s2.LatLngFromDegrees`に渡しています。
これにより数値の座標が`LatLng`型に変換され、`sm.NewMarker`に渡すことができるようになります。
`sm.NewMarker`にはマーカーを置きたい座標、色、サイズを指定して、マーカーのインスタンスを生成します。

`Render`関数では、タイルデータに対してマーカーやパスなどを描画して返却してくれています。
それを`gg.SavePNG`を使うことで拡張子、ファイル名を指定して保存しています。

## パスも追加したい
さきほどのコードに少し手を加えることで、マーカー同士にパスをひくことも可能です。

```go
  //マーカーを追加
  spot1 := s2.LatLngFromDegrees(35.6259434, 139.7251859)
  ctx.AddMarker(sm.NewMarker(spot1, color.RGBA{0xff, 0, 0, 0xff}, 16.0))

  spot2 := s2.LatLngFromDegrees(35.620023, 139.72818)
  ctx.AddMarker(sm.NewMarker(spot2, color.RGBA{0, 0xff, 0, 0xff}, 16.0))

  // パスを追加
  path := new(sm.Path)
  path.Positions = []s2.LatLng{spot1, spot2}
  path.Color = color.RGBA{0, 0, 0xff, 0xff} //パスの色
  path.Weight = 3.0 //パスの幅
  ctx.AddPath(path)
```
![my-map.png](https://qiita-image-store.s3.amazonaws.com/0/52234/ac11340a-de6d-a034-190a-1a9984271555.png)

## エリアを追加したい
マーカーやパスだけではなく、指定した箇所を塗りつぶすこともできます。

```go
  //マーカーを追加
  spot1 := s2.LatLngFromDegrees(35.6259434, 139.7251859)
  ctx.AddMarker(sm.NewMarker(spot1, color.RGBA{0xff, 0, 0, 0xff}, 16.0))

  spot2 := s2.LatLngFromDegrees(35.620023, 139.72818)

  spot3 := s2.LatLngFromDegrees(35.626159, 139.723602)

  // エリアを追加
  area := new(sm.Area)
  area.Positions = []s2.LatLng{spot1, spot2, spot3}
  area.Color = color.RGBA{0, 0, 0xff, 0xff} //パスの色
  area.Fill = color.RGBA{0, 0xff, 0xff, 0xff} //塗りつぶす色
  area.Weight = 3.0 //パスの幅
  ctx.AddArea(area)
```
![my-map.png](https://qiita-image-store.s3.amazonaws.com/0/52234/f63ca0b9-80f5-40a8-735a-27741280fc1a.png)


# まとめ
なんとも簡単に地図タイルサーバの準備から、地図タイルデータに対してマーカーを置いたりパスを引いたりすることができました。すごい！！
これでいつでも自分だけの地図画像を作ることができます。

ライブラリの中身もシンプルで追いやすいと思うので、興味ある人はのぞいてみると面白いと思います。

# おまけ
え？いまでも十分手軽だけどもっと手軽に地図画像を作りたい？
そんな方のために(?)、go-staticmapsはコマンドラインからも実行が可能です。

```
go get -u github.com/flopp/go-staticmaps/create-static-map
```
でインストールし、

```
$ create-static-map -m "35.6259434,139.7251859"
```
を実行すると、
![map.png](https://qiita-image-store.s3.amazonaws.com/0/52234/f28c7998-2e9a-36fd-0afb-d587210b6c3c.png)

こうなります。圧倒的な手軽さ。もちろん、パスを引いたり、エリアで囲んだり、ライブラリの時とほぼほぼ機能は変わりません。
ただ、OpenMapTilesのような自前で用意したタイルサーバからはタイルデータを取得できないので、そこだけご注意ください。

それでは快適な地図画像ライフを。