Lambdaで最寄駅検索APIを作ってみた

公開日:2018-11-04
最終更新:2018-11-04
※この記事は外部サイト(https://qiita.com/dorarep/items/af8c168a24...)からのクロス投稿です

概要

最寄駅といっても要件によって定義が曖昧で、今後の振れ幅を考えると自前で持ちたいなぁということで実装しました。

駅データの獲得

駅データ.jpで駅データを獲得します。

駅データを加工

パフォーマンスも考え、awkを使ってrequireできるjavascriptの形に加工します。

$ tail -n +2 station20170403free.csv | awk  'BEGIN {FS=",";OFS=",";print "exports.default = ["} {print "    {id:"$2",name:\""$3"\",lng:"$10",lat:"$11"},"} END {print "]"}' > station_data.js
$ head -n 3 station_data.js
exports.default = [
    {id:1110101,name:"函館",lng:140.726413,lat:41.773709},
    {id:1110102,name:"五稜郭",lng:140.733539,lat:41.803557},

$1が同じ駅でも路線が異なると別で採番されるID。 $2が同じ駅だと同じ数値で採番されるIDになります。 今回は要件上、路線を意識する必要はないので$2だけを採用しました。

また要件次第では$2のidでuniqを取った方が使いやすいと思います。 例えば近い順で何件かとってきたい場合、そのままだと「新宿駅だらけ」のような問題も起きてしまいます。 その場合、同じ駅でも路線によって位置情報や名前が異なるケースもあるので、どちらにしろAPI側で戻り値がuniqueになるようにゴニョゴニョする必要があります。

緯度・軽度から距離を計算する関数の作成

駅データ、APIに渡す値ともに緯度・経度なので、緯度経度から距離を計算する関数を作成します。

下記URLを参考に実装してみました。 http://www.ic.daito.ac.jp/~mizutani/gps/measuring_earth.html

function calcDistance (lat1, lng1, lat2, lng2) {
  const Re = 6378.137 // 地球の半径

  function radians (deg) {
    return deg * Math.PI / 180
  }

  function rectangularCoordinateSystem (lat, lng) {
    const h = radians(lat)
    const t = radians(lng)
    const x = Math.cos(h) * Math.cos(t)
    const y = Math.cos(h) * Math.sin(t)
    const z = Math.sin(h)
    return [x, y, z]
  }

  function innerProduct(vector1, vector2) {
    return vector1.reduce((carry, value, index) => {
      return carry + value * vector2[index]
    }, 0)
  }

  const v1 = rectangularCoordinateSystem(lat1, lng1)
  const v2 = rectangularCoordinateSystem(lat2, lng2)
  return Re * Math.acos(innerProduct(v1, v2))
}

最寄りの駅を獲得

現在地からの直線距離を全ての駅に対して計算して、近い順にソート。そして先頭のデータが最寄駅です。

const stations = require('station_data.js').default

function getNearestStation (lat, lng) {
    return stations.map((station) => {
        station.distance = calcDistance(lat, lng, station.lat, station.lng)
        return station
    }).sort((station1, station2) => 
        station1.distance - station2.distance
    ).slice(0, 1)
}

これで直線距離が最短の駅を獲得!

Lambda関数として実装する

あとは、今回作った関数をLambdaで呼び出すだけです。

module.exports.getNearestStation = function(event, context, callback) {
    const nearestStation = getNearestStation(event.lat, event.lng)

    callback(null, {
      statusCode: 200,
      body: JSON.stringify(nearestStation)
    })
  })
}

完成!

これですごい簡単ですができました! 実際の現場では、GoogleMapAPIと連携して徒歩で何分かも取得しつつ、近い順に何件か取得するロジックで使っています。 オープンデータを提供くださっている方々のおかげで、自前でもなんとかなるものですね!

記事が少しでもいいなと思ったらクラップを送ってみよう!
72
+1
@zd2JVKCd8JyW6MUgの技術ブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする