読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

ストリートビューで最も近いマーカーへ向く方法

GoogleMapsAPI

近頃はお仕事でGoogleMapAPIを触ってます。

いやー、これだけ高機能な地図アプリケーションを、たったの数十行のコードで実現できるなんて素晴らしいですね。Google恐るべし。

ストリートビューを開いた時の向きを自動的に最適化する

さて、デフォルトではストリートビューを開いた時の方角は固定です。 デフォルト値は以下の通り変更できるのですが、だからどうしたという感じです。

const map = new google.maps.Map(document.getElementById("map"));

// デフォルトで右上(北東)を向くようにする
map.getStreetView().setPov({
    heading: 45, // 真北を0として時計回り180度まで。反時計回りはマイナスの値
    pitch: 0 // 仰角。真下は-90、真上は90
});


今回は地図上の最寄りのマーカへ自動的に向いて欲しいという要求があるので、以下の通りにしました。

import _ from 'lodash';
import google from 'google-maps-api';

// 地図上に配置したマーカー群の位置
/** @type {!Array<google.maps.LatLng>} */
const markerLocations = [
    new google.maps.LatLng(...),
    new google.maps.LatLng(...),
    ...
];

/**
 * 第二引数の要素のうち、第一引数に最も近いものを返す。
 *
 * @param {!google.maps.LatLng} currentPos
 * @param {!Array<google.maps.LatLng>} positions
 * @return {!google.maps.LatLng}
 */
const findClosestPosition = (currentPos, positions) => 
    positions.map(position => ({
        position,
        distance: google.maps.geometry.spherical.computeDistanceBetween(
            currentPos,
            position
        )
    })).sort((x, y) => x.distance - y.distance)[0].position;

const sview = map.getStreetView();

// 一度ストリートビューをONにしないとposition_changedイベントが発火しない模様
sview.setVisible(true); 
sview.setVisible(false);

// ペグマン(ストリートビューの位置を指定するためにドラッグするアレ)を移動したら向きを変える
google.maps.event.addListener(sview, 'position_changed', () => {

    // ペグマンの位置を特定
    /** @type {!google.maps.LatLng} */
    const pos = sview.getPosition();

    sview.setPov({
        heading: google.maps.geometry.spherical.computeHeadingFrom(
            pos, findClosestPosition(pos))
        pitch: -10
    });
});


ここで重要なのは次の2点です。

要点1: 位置情報計算用のユーティリティ関数を使え

この2つの関数を用いることで、面倒な計算が一発でできます。

google.maps.geometry.spherical.computeHeading
A→Bの向きを計算する
google.maps.geometry.spherical.computeDistanceBetween
A→Bの距離を計算する

同等品を自前で書くのはメンドクサイので、大いに助かりました。


ところで、この関数はオプションライブラリ扱いです。 URLにlibraries=geometryを付ける必要があるのでご注意を。詳しくはこちら。

http://developers.google.com/maps/documentation/javascript/libraries

要点2: StreetViewPanoramaのposition_changedイベントを使え

mousemoveイベントはペグマンのドラッグ時には発生しないので使えません。

ではどうするかというと、StreetViewPanoramaのposition_changedイベントを使います。 イベントオブジェクトにはLatLngオブジェクトが設定されていないので、 StreetViewPanorama.getPosition()でペグマンの位置を取得する必要があります。

ストリートビューを開く前に1度行えば充分ならば、visible_changedイベントでいいんじゃね?』という話もありますが、ペグマンの向きも動的に変えたいのでposition_changedの方が望ましいと思います。

ただし、どうやら一度ストリートビューを開かないとposition_changedが発火しないみたいです。 だからサンプルコードにある通り、ストリートビューを強制的にON→OFFしています。