Turf.jsを使用して地球上の2点間の距離を計測する

## はじめに
仕事である特定の2点間の距離を計測する必要が出てきたので
以前他のPJで使用されていた、Turf.jsというライブラリがあったので試してみました。

## 作業環境
* node 12.0.0
* npm 6.9.0
* Turf.js 6.9.0

## 前提条件
* 複数の箇所の緯度・経度をデータとして持っている
    * データはjson形式のファイル(lists.json)
* できれば外部ファイルに書き出して確認したい
* 上記と、ある特定の一点との距離を計測する必要がある
* 地球上(球面状)の2点間の計算

```json:lists.json
[
    {
        "NAME": "名古屋駅",
        "LATITUDE": 35.170406,
        "LONGITUDE": 136.881695,
        "Distance": 267.50358367757696
    },
    {
        "NAME": "大阪駅",
        "LATITUDE": 34.702485,
        "LONGITUDE": 135.495951,
        "Distance": 403.0278954652313
    }
]
```


## 事前準備

まずは今回の作業を行うのにディレクトリを作っておきます。

```
mkdir turf_test
cd turf_test
```

## Turf.jsについて

参考にさせていただいた[スライド](https://www.slideshare.net/takahi/javascript-gis-turfjs)からそのまま引用すると

> 軽量・高速・オープンなWeb地図用のGISライブラリ

とあります。
公式サイトを見る限りかなり色々できそうです。

[Turf.js公式サイト](https://turfjs.org)

ただ色々できる分、全て入れるとかなりの容量になります。

```
npm install @turf/turf
```

上記コマンドでインストールをすると本体や関連しているライブラリ含めて、17.3MBあります。
全て入れた状態でも使用はできるんですが、公式サイトを見る限りかなりモジュール化が徹底されているようなので、用途に沿ったものだけを入れていきます。

ここで前提条件を思い出します。
基本的には2点間の計測ができれば良いので、それを実現できそうなものを公式サイトから探しました。

distanceがそれに該当しそうです。
[distanceの説明](https://turfjs.org/docs/#distance)

記載されている説明を直訳します。(ありがとうgoogle翻訳)

> Calculates the distance between two points in degrees, radians, miles, or kilometers. This uses the Haversine formula to account for global curvature.

> 2つのポイント間の距離を度、ラジアン、マイル、またはキロメートルで計算します。これは、Haversine公式を使用して、全体的な曲率を考慮します。

説明を見る限りでは今回の要件を満たしていそうです。
というわけでこれをインストールしていきます。

## distanceの実装

該当の機能の説明を見る限り`@turf/distance`だけあれば十分なようです。
インストールします。

```
npm init
npm install @turf/distance
```
ちなみにこのモジュールだけだと、131KBだけです。

まずは公式に載っている通りの内容を実行してみます。
今回はターミナルから直接叩くことを想定しています。

ファイルの作成。

```
touch index.js
```

今回は東京駅から名古屋駅までの距離を計測します。
まずは公式サイトに載っているテストコードを掲載

```js:テストコード
var from = turf.point([-75.343, 39.984]);
var to = turf.point([-75.534, 39.123]);
var options = {units: 'miles'};

var distance = turf.distance(from, to, options);
```
ちなみに公式はライブラリが全部入っている前提のコードのようなので、以下のように変更しました。

```js:index.js
// turf.js呼び出し
const { point } = require('@turf/helpers')
const distance = require('@turf/distance').default

const from = point([139.766765, 35.681283]); //[経度, 緯度] 東京駅
const to = point([136.881695, 35.170406]); //[経度, 緯度] 名古屋駅
const options = { units: 'kilometers' }; // degrees, radians, miles, kilometersが指定可能

const result = distance(from, to, options);
console.log(result) // 267.50358367757696
```

```
node index.js
// consoleに右記が出力 → 267.50358367757696
```

だいたい267Kmほどの距離があるようです。
試しにgooglemapでも距離計測をしておきます。

![スクリーンショット 2019-09-14 21.57.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191638/973d5600-aecd-5f5e-70bb-964fb056798a.png)

おおよそあっています。

## ファイルの外部読み込みから、吐き出しまで

* 複数の箇所の緯度・経度をデータとして持っている
    * データはjson形式のファイル(lists.json)
* できれば外部ファイルに書き出して確認したい

上記も含めてコードに落とし込みました。
これを実行すると該当のディレクトリに`results.json`が吐き出されます。

Node.jsにおける外部ファイルの読み込み、書き出しの説明に関しては割愛します。

```js:index.js
// turf.js
const { point } = require('@turf/helpers')
const distance = require('@turf/distance').default

// 設定ファイルの読み込み
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
let results = []

const now = new Date()

const isNumber = (value => {
  return ((typeof value === 'number') && (isFinite(value)));
})

let errorTarget = ''
readFile('lists.json', 'utf-8')
  .then((data) => {
    lists = JSON.parse(data);

    lists.forEach((d, index) => {
      if ((d.LONGITUDE && d.LATITUDE) && (isNumber(d.LONGITUDE) && isNumber(d.LATITUDE))) {
        let pt1 = point([139.766765, 35.681283], { 'name': '東京駅' })
        let pt2 = point([Number(d.LONGITUDE), Number(d.LATITUDE)], { 'name': d.name })

        const result = distance(pt1, pt2, { units: 'kilometers' })
        d.DISTANCE = result
        results.push(d)
      }
    })
  })
  .catch((err) => {
    console.log(err)
    throw err;
  }).finally(() => {
    fs.writeFileSync(`results.json`, JSON.stringify(results, null, '    '));
  })


```
### 生成物

最終的に吐き出された生成物は以下です。

```results.json
[
    {
        "NAME": "名古屋駅",
        "LATITUDE": 35.170406,
        "LONGITUDE": 136.881695,
        "DISTANCE": 267.50358367757696
    },
    {
        "NAME": "大阪駅",
        "LATITUDE": 34.702485,
        "LONGITUDE": 135.495951,
        "DISTANCE": 403.0278954652313
    }
]
```

## まとめ
初めはgoogleのAPIを有料になってでも叩かないとあかんかなーと思っていたんですが、
意外と簡単にいけてびっくりしました。
公式サイト見る限りかなり多機能なので、他にも活用してみたいものです。

最後まで読んでくださってありがとうございました。

## 参考
* https://day-journal.com/memo/turf-js-005/
* https://www.slideshare.net/takahi/javascript-gis-turfjs