go-staticmapsの地図にアイコンとして任意の画像を表示したい

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

[10日目の記事](https://qiita.com/tsukumaru/items/62b54a45ece1d47c9d1a)で、`go-staticmmaps`というライブラリの紹介をさせていただきました。
https://github.com/flopp/go-staticmaps

実は最近このライブラリに出したp-r(小さな変更)がマージされて浮かれています。
ということで今回はせっかくなので、前回の記事の続編として、ライブラリをよくするために、さらにこんなの考えてみたよ！という内容で書こうと思います。

# 地図にアイコンとして任意の画像を表示したい
今現在、go-staticmapsはある一点を示すものとしてマーカーを用意しています。
しかし、このマーカーはサイズと色は変えられますが、形は変えられません。
せっかくなのですから、例えば会社の場所を示す地図には会社のロゴとか、なにかのキャラクターに関連する場所にそのキャラクターの顔を表示したり、そういう使い方をしたいものです。

そこで、アイコンとして任意の画像を表示できるようにライブラリを拡張してみました。

## いまのマーカーの実装
いまのマーカーがどのように実装されているかを見てみます。
https://github.com/flopp/go-staticmaps/blob/master/marker.go#L125-L149

```go
gc.DrawArc(x, y-m.Size, radius, (90.0+60.0)*math.Pi/180.0, (360.0+90.0-60.0)*math.Pi/180.0)
gc.LineTo(x, y)
gc.ClosePath()
gc.SetColor(m.Color)
gc.FillPreserve()
gc.SetRGB(0, 0, 0)	
gc.Stroke()
```
すごいですね、2D図形を描画できるライブラリでマーカーを描いてます。
ということで、画像を表示させる仕組みはなさそうなので、新しく作りましょう。

## `icon.go`の実装
https://github.com/tsukumaru/go-staticmaps/commit/4dd12b217c6b09855a8664fbcdf00e54e4020954

`Icon`という新しい機能を実装してみました。
これを使うと、こんな感じで指定した場所に画像を表示することができます。
もちろん、既存のものと組み合わせて、マーカーやパスを一緒に表示することもできます。
![my-map.png](https://qiita-image-store.s3.amazonaws.com/0/52234/7da05ff3-0f42-b5b8-1858-f4a28e8ebfde.png)

### 使い方
使い方を説明します。

```go
package main

import (
  "path/to/tsukumaru/go-staticmaps" //cloneしてきたパス
  "github.com/fogleman/gg"
  "github.com/golang/geo/s2"
)

func main() {
  ctx := sm.NewContext()

  spot := s2.LatLngFromDegrees(35.6259434, 139.7251859)
  icon := sm.NewIcon(spot, "tsukumaru.jpeg", 30 ) //座標、画像のパス、サイズ
  icon.SetOffset(0, 0) //オフセット
  ctx.AddIcon(icon)

  // 画像として出力
  img, _ := ctx.Render()
  gg.SavePNG("my-map.png", img)
}
```

いままでのMarkerやPathと同じような雰囲気で使うことができます。
> "path/to/tsukumaru/go-staticmaps"

いま現在は `add_icon`ブランチに変更が置いてあるので、使ってみたい場合は[`tsukumaru/go-staticmaps`](https://github.com/tsukumaru/go-staticmaps)をcloneしてブランチを変更してください。

#### **`NewIcon`関数**
この関数には画像を表示したい座標、画像のパス、サイズを指定します。
サイズは幅も高さも指定した数値で初期化されますが、それぞれ変更したい場合は`SetWidth`、`SetHeight`関数を使います。



#### **`SetOffset`関数**
この関数ではOffsetを指定します。
デフォルトでは指定した点を中心として画像が表示されるようになっているので、少し上にあげたいなどの場合にお使いください。

### 実装
実装の中身としては、そこまで難しいことはしておらず、以下がほぼすべてです。
https://github.com/tsukumaru/go-staticmaps/blob/add_icon/icon.go#L75-L81

```go
img = resize.Resize(i.Width, i.Height, img, resize.Lanczos3) //画像のリサイズ

//指定された場所を中心として画像が表示されるように
x, y := trans.ll2p(i.Position)
ix := int(x) - int(i.Width/2) 
iy := int(y) - int(i.Height/2)

//オフセットつきで画像を描画
gc.DrawImageAnchored(img, ix, iy, i.Offset[0], i.Offset[1])
```

画像のリサイズには以下のライブラリを使い、指定したサイズにリサイズしています。補完関数の指定はなんとなくで指定してます...
https://github.com/nfnt/resize

`trans.ll2p`という関数は、内部で定義されている関数で、`LatLng`型から内部の座標に変換してくれます。
https://github.com/flopp/go-staticmaps/blob/master/context.go#L292-L308

画像の描画は、`fogleman/gg`の中に`DrawImageAnchored`というオフセットを考慮して描画してくれるちょうどいい関数があったので、それを使っています。
https://github.com/fogleman/gg/blob/master/context.go#L575-L591

画像の合成とか必要なのかなーとか思ってましたが、単に画像を描画するだけで済んだのでよかったです。

# まとめ
go-staticmapsで地図画像に任意の画像を表示させるために、`Icon`という新しい機能を実装してみました。既存の機能と同様に使うことができます。
しかしまだコマンドラインに対応してなかったり、まだまだ改善できるところはあるので、改善していってまたp-r出せればと思っています！

明日は @tenmihi さんです。楽しみですね！