SwiftとHexavilleで駅探索APIを作ってみた

こんにちは。
ニジボックスでネィティブアプリを開発していることが多い@ochimです。

## モチベーション
- 緯度経度から近くの駅を取得するAPIを探してみたが、手軽に使えるのがない。
- [SimpleAPI](http://map.simpleapi.net/)とかあるけどhttpsじゃない
- サーバーサイドのSwiftを試してみたい

## 準備
- [Hexaville](https://github.com/noppoMan/Hexaville)のインストール。

```
curl -L https://rawgit.com/noppoMan/Hexaville/master/install.sh | bash
source ~/.bashrc
```

- [駅データ.jp](http://www.ekidata.jp/)から駅データをダウンロードする（会員登録が必要）

Hexaville は簡単に言えば、Swiftの為のモダンなWebアプリケーションフレームワーク及びエンジンです。Swiftで作られたwebアプリをAWS Lambda と ApiGatewayで実行する事をコンセプトにしているそうです。詳しくはリンク先を

## 環境
- Mac OS 10.12.6
- Swift 4.0
- Xcode 9.2

## プロジェクトを作る
```
hexaville generate NearStations
cd NearStations
swift package generate-xcodeproj
```
これでxcodeprojファイルが作成されるので、あとはXcodeで開発できます

## 今回のソース構成

![スクリーンショット 2017-12-11 20.20.25.png](https://qiita-image-store.s3.amazonaws.com/0/103548/9bd23779-90e1-beab-2c8f-52d36b25c776.png)

## 実装
ロジック部分を晒します。

```Convert.swift
import Foundation
import HexavilleFramework

struct Convert {

    static func convertCSV() -> [[String]] {
        do {
            let asset = AssetLoader.shared.availableAbsolutePathForAssets()
            let csvPath = "\(asset!)/station20171109free.csv"
            let csvStr = try String(contentsOfFile:csvPath, encoding:String.Encoding.utf8)
            
            var result: [[String]] = []
            let rows = csvStr.components(separatedBy: .newlines)
            for row in rows {
                let columns = row.components(separatedBy: ",")
                result.append(columns)
            }
            result.remove(at: result.count-1)

            return result
            
        } catch {
            return []
        }
    }
}
```
駅データのcsvを取り込んで配列化しています。

```Station.swift
import Foundation

struct Station {

    func radians (deg: Double) -> Double {
        return deg * Double.pi / 180
    }
    
    func rectangularCoordinateSystem (lat: Double, lng: Double) -> (Double,Double,Double) {
        let h = radians(deg:lat)
        let t = radians(deg:lng)
        let x = cos(h) * cos(t)
        let y = cos(h) * sin(t)
        let z = sin(h)
        return (x, y, z)
    }

    func calcDistance(lat1: Double, lng1: Double, lat2: Double, lng2: Double) -> Double{
        let Re = 6378.137 // 地球の半径
        let (x1, y1, z1) = rectangularCoordinateSystem(lat:lat1, lng:lng1)
        let (x2, y2, z2) = rectangularCoordinateSystem(lat:lat2, lng:lng2)
        return Re * acos(x1*x2 + y1*y2 + z1*z2)
    }

    /** 付近の駅を取得 **/
    func getNearStations (lat: Double, lng: Double) -> [[String:String]] {
        
        let stations = Convert.convertCSV()
        
        var array: [[String:String]] = []
        for station:[String] in stations {
            var dic :[String:String] = [:]
            let distance = calcDistance(lat1:lat, lng1:lng, lat2:atof(station[10]), lng2:atof(station[9]))
            dic["km"] = String(format:"%f", distance)
            dic["name"] = station[2]
            array.append(dic)
        }
        array = array.sorted(by: { $0["km"]! < $1["km"]! })
        return array[0...2].map{$0}
    }
    
}
```
getNearStations() で付近の駅を近い順に３件取得しています。
2地点の緯度経度から距離を計算するのに使った方法は[こちら](http://www.ic.daito.ac.jp/~mizutani/gps/measuring_earth.html)

## 動作確認
### ローカルで実行して確認
ニジボックスの入っているビルで試してみましょう。緯度：35.660944, 経度：139.775199

<img width="818" alt="スクリーンショット 2017-12-16 21.08.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/103548/6491131e-2a53-9f4d-4f06-1c1ee31d61eb.png">

よっしゃ！
付近の駅３件の名前と距離を表示しています。

このままデプロイするか、てところなんですが、
息切れしたので、デプロイは次回に取っておきます。

ソースコードをgithubに上げました。[こちら](https://github.com/ochim/StationsAPI/tree/master)

## 参考
- [Lambdaで最寄駅検索APIを作ってみた](https://qiita.com/dorarep/items/af8c168a24a3ba2c468a)
このページのJavascriptをSwiftに書き直した感じです。ありがとうございます！

- [サーバーレスサーバーサイドSwiftとHexaville](https://qiita.com/tk1024/items/ec1f9def64be905e8598)

## デプロイしました
[別記事](https://qiita.com/ochim/items/7ae896081a91ac19de5f.md)