Help us understand the problem. What is going on with this article?

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

はじめに

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

作業環境

  • node 12.0.0
  • npm 6.9.0
  • Turf.js 6.9.0

前提条件

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

参考にさせていただいたスライドからそのまま引用すると

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

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

Turf.js公式サイト

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

npm install @turf/turf

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

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

distanceがそれに該当しそうです。
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

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

テストコード
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);

ちなみに公式はライブラリが全部入っている前提のコードのようなので、以下のように変更しました。

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

おおよそあっています。

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

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

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

Node.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を有料になってでも叩かないとあかんかなーと思っていたんですが、
意外と簡単にいけてびっくりしました。
公式サイト見る限りかなり多機能なので、他にも活用してみたいものです。

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

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away