0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

緯度経度から2点間距離の計算方法

Posted at

Haversine式を使用して2点間の緯度経度から距離を計算する方法を紹介します。

Haversine式は、球面上の2点間の距離を計算するための公式です。この式は、地球のように球形の天体上での距離計算(いわゆる「大円距離」や「球面上の直線距離」)に使われます。特に、経度と緯度を使って地球上の2地点間の最短距離を求める場合に非常に便利です。

vueのleafletを使用してマップを作成しています。

1.距離の計算するファイルを作成

Haversine.js
const getRadian = (value) => {
    return value * Math.PI / 180;
};

const getDistance = (location1, location2) => {
    const latitude1 = location1.latitude;
    const longitude1 = location1.longitude;
    const latitude2 = location2.latitude;
    const longitude2 = location2.longitude;

    const R = 6371; // km
    const diffLatitudeRadian = getRadian(latitude2 - latitude1);
    const diffLongitudeRadian = getRadian(longitude2 - longitude1);
    const latitudeRadian = getRadian(latitude1);
    const longitudeRadian = getRadian(latitude2);

    const a = Math.sin(diffLatitudeRadian / 2) * Math.sin(diffLatitudeRadian / 2) +
        Math.sin(diffLongitudeRadian / 2) * Math.sin(diffLongitudeRadian / 2) *
        Math.cos(latitudeRadian) * Math.cos(longitudeRadian);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
};

export { getDistance };

2. 地図表示

map.vue
<l-map :zoom="zoom" :center="center" @update:zoom="updateZoom" @update:center="updateCenter" @ready="onMapReady" ref="mapContainer">
    <l-tile-layer url="https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}" layer-type="base" name="GoogleMaps"></l-tile-layer>
    <l-marker v-for="marker in markers" :key="marker.id" :lat-lng="[marker.lat, marker.lng]">
        <l-popup>{{ marker.name }}</l-popup>
    </l-marker>
    <l-marker :lat-lng="userLocation" v-if="userMarker"></l-marker>
</l-map>

まず、(Vue-Leafletのコンポーネント)で地図を表示し、ズームやセンターを管理するための状態(zoom、center)を定義しています。

  • ユーザーの位置をマーカーで表示するために下記が使われています
    <l-marker :lat-lng="userLocation" v-if="userMarker"></l-marker>

  • ここでは、markers配列を使って、マーカーをループ処理して表示しています

3.ユーザー位置の取得

:map.vue
const locateUser = () => {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
            (position) => {
                const { latitude, longitude } = position.coords;
                userLocation.value = [latitude, longitude]; // ユーザー位置を更新
                userMarker.value = true; // ユーザーマーカーを表示
                center.value = userLocation.value; // 地図の中心をユーザー位置に設定
                if (mapVisible.value) {
                    mapRef.value.setView(center.value, zoom.value); // 地図の中心を設定
                }
                getMyMaps(); // 自分のマップ情報を取得
            },
            (error) => {
                console.error('Could not get current position:', error);
            },
            {
                enableHighAccuracy: true,
                timeout: 10000,
                maximumAge: 0
            }
        );
    } else {
        console.error('Browser does not support geolocation API.');
        getMyMaps(); // ユーザー位置が取得できない場合でもマップを表示
    }
};

locateUserメソッドでは、ブラウザの位置情報を使用して、ユーザーの(緯度・経度)を取得します。

4.マップデータの取得

const getMyMaps = async () => {
    const url = 'DBからマップのデータを取得するためのエンドポイント';
    try {
        const response = await axios.get(url, { params: { lat: center.value[0], lng: center.value[1] } });
        if (response.data.result === true) {
            myMaps.value = response.data.myMap; // 取得したマップデータを格納
            setMarkers(); // マーカーをセット
            calculateDistances(); // ユーザーとマーカー間の距離を計算
        } else {
            console.error('Unexpected response structure:', response.data);
        }
    } catch (error) {
        console.error('Error fetching maps:', error);
    }
};

getMyMapsメソッドは、サーバーからマップ情報(myMap)を取得します。この際、地図の現在位置(緯度・経度)をサーバーに送信し、その位置にベースってマーカーを設定します。

4.マーカーの設定

const setMarkers = () => {
    markers.value = myMaps.value.map((myMap) => ({
        id: myMap.id,
        lat: myMap.latitude,
        lng: myMap.longitude,
        name: myMap.name,
    }));
};

setMarkersメソッドでは、取得したマップデータ(myMaps)からマーカー情報を取り出して、markers配列に格納しています。

controller

controller.php
    public function hoge(Request $request)
    {

        $latitude       = $request->latitude;
        $longitude      = $request->longitude;
        $loggedInUserId = Auth::id();
        $myMap          = [];


        try {
            $myMap = UserMap::selectDistance($longitude, $latitude, $loggedInUserId)
                ->orderBy('distance', 'asc')
                ->take(10)
                ->get();
        } catch (\Exception $e) {
        }
        return ([
            'result' => true,
            'myMap' => $myMap,
        ]);
    }
model.php
    public function scopeSelectDistance($query, $longitude, $latitude, $loggedInUserId) // 現在地との距離を取得できるようにしてます
    {
        $query->selectRaw(
            'id,user_id, name, address,content'.
            'ST_Y(location) AS longitude, ST_X(location) AS latitude, '.
            'st_Distance_Sphere(POINT(?, ?), POINT(ST_Y(location), ST_X(location)))  AS distance',
            [ $longitude, $latitude,]
        )
        ->where('user_id', $loggedInUserId);
    }

5. 2点間の距離を計算する

import { getDistance } from '@/Haversine.js';

const calculateDistances = () => {
    const userLoc = { latitude: userLocation.value[0], longitude: userLocation.value[1] };
    myMaps.value = myMaps.value.map((myMap) => {
        const mapLoc = { latitude: myMap.latitude, longitude: myMap.longitude };
        return {
            ...myMap,
            distance: getDistance(userLoc, mapLoc).toFixed(2) // ユーザーとマーカー間の距離を計算
        };
    });
    myMaps.value.sort((a, b) => parseFloat(a.distance) - parseFloat(b.distance)); // 距離が近い順にソート
};

calculateDistancesメソッドは、ユーザーの位置とマーカーの位置と距離を計算する処理です。計算には、getDistance関数(Haversine式)を使用します。

  • getDistance関数を使って、ユーザーとマーカーの距離(キロメートル)を計算しています
  • 計算した距離をmyMapsの各オブジェクトに追加し、その後、距離が近い順にソートします

6.距離をマーカーに表示

<l-marker v-for="marker in markers" :key="marker.id" :lat-lng="[marker.lat, marker.lng]">
    <l-popup>{{ marker.name }} - {{ marker.distance }} km</l-popup>
</l-marker>

最後に、markers配列に折りたたまれたマーカーを地図に表示します。 マーカーのポップアップには、marker.nameとともに距離を表示できます。
ここでは、各マーカーの距離をポップアップに表示しておりますので、ユーザー{{ marker.distance }} kmとそのマーカーの距離を表示します。

最後に

2点間距離を計算するために公式を使用しましたが、2点間の直線距離なのであくまで目安でしかありません。
有料でGoogleMapAPIを使用すればより正確な距離を取得することはできます。
参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?