ジオメトリ ライブラリ

  1. 概要
  2. 球面ジオメトリのコンセプト
    1. 距離と面積の関数
    2. ナビゲーションの関数
  3. ジオメトリ エンコード
  4. ポリゴンとポリラインの関数
    1. containsLocation()
    2. isLocationOnEdge()

概要

このドキュメントでは、google.maps.geometry ライブラリ内でのみ利用可能な機能に関連するコンセプトについて説明しています。このライブラリは、Maps JavaScript API を読み込む際にデフォルトで読み込まれるものではないため、libraries ブートストラップ パラメータを使用して明示的に指定する必要があります。詳しくは、ライブラリの概要をご覧ください。

Maps JavaScript API のジオメトリ ライブラリには、地表のジオメトリ データを計算するユーティリティ関数が含まれています。このライブラリには、次の 3 つの名前空間があります。

  • spherical には、緯度と経度から、角度、距離、面積を計算できる球面ジオメトリ ユーティリティが含まれています。
  • encoding には、エンコード化ポリライン アルゴリズムに従って、ポリラインのパスをエンコード、デコードするユーティリティが含まれています。
  • poly には、ポリゴンやポリラインに関する計算を行うユーティリティ関数が含まれています。

google.maps.geometry ライブラリにはクラスがない代わりに、上記の名前空間に静的メソッドがあります。

球面ジオメトリのコンセプト

Maps JavaScript API 内の画像は 2 次元で「平面」です。しかし実際の地球は 3 次元なので、多くの場合、偏球または簡略化した球面として近似されます。Maps API では地球を球面として扱い、パソコンの画面のような 2 次元の平面に投影して表示します。

なお、2 次元に投影すると、正確に表示されない部分があります。 地図を投影すると必ず歪みが生じるので、単純なユークリッド幾何学はほとんど適用できません。たとえば、球面上の 2 つの地点の最短経路は直線ではなく、大きな円弧(測地線の一種)になります。また、球面上の三角形の内角の和は 180 度より大きくなります。

このような違いがあるため、球面上(またはその投影上)においては、球面幾何学の関数を使用して、距離や方向、面積などの構成要素を計算する必要があります。Maps API の google.maps.geometry.spherical 名前空間には、これらの球面幾何学の構成要素を計算するユーティリティ、および球面座標(緯度と経度)からスカラー値を計算する静的メソッドが含まれています。

距離と面積の関数

2 地点間の距離とは、2 つの地点を結ぶ最短パスの長さです。この最短パスを測地線と呼び、球面上の測地線はすべて大きな円弧のセグメントになります。この距離を計算するには computeDistanceBetween() を呼び出して、2 つの LatLng オブジェクトを渡します。

または computeLength() を使用して、複数の地点を通るパスの長さを計算することもできます。

結果として得られる距離はメートル単位で表現されます。

ポリゴンの面積を(平方メートルで)計算するには、computeArea() を呼び出して、閉ループを定義する LatLng オブジェクトの配列を渡します。

球面上のナビゲーションにおいては、固定の基準点(通常は真北)からの角度で方向を表します。Google Maps API 内では、真北(0 度)から時計回りに測った角度を方向として規定しています。2 地点間の角度を計算するには、computeHeading() メソッドを使い、fromto の 2 つの LatLng オブジェクトを渡します。

computeOffset() を使用すると、特定の角度、出発地点、移動距離(メートル単位)から、目的地の座標を計算できます。

interpolate() メソッドを使って、2 つの LatLng オブジェクトと、0~1 の値から、2 つの地点間にある目的地を算出することもできます。このメソッドは 2 点間の球面線形補間を行い、0~1 の値は出発地点から目的地に向かってパスに沿って進む距離を比で表したものです。

次のサンプルでは、地図上でクリックされた 2 つの地点から、2 つの地点を結ぶ測地線と「直線」の 2 つのポリラインを作成し、2 つの地点間を移動するときの角度を計算します。

TypeScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">

let marker1: google.maps.Marker, marker2: google.maps.Marker;
let poly: google.maps.Polyline, geodesicPoly: google.maps.Polyline;

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 4,
      center: { lat: 34, lng: -40.605 },
    }
  );

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(
    document.getElementById("info") as HTMLElement
  );

  marker1 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 40.714, lng: -74.006 },
  });

  marker2 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 48.857, lng: 2.352 },
  });

  const bounds = new google.maps.LatLngBounds(
    marker1.getPosition() as google.maps.LatLng,
    marker2.getPosition() as google.maps.LatLng
  );

  map.fitBounds(bounds);

  google.maps.event.addListener(marker1, "position_changed", update);
  google.maps.event.addListener(marker2, "position_changed", update);

  poly = new google.maps.Polyline({
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    map: map,
  });

  geodesicPoly = new google.maps.Polyline({
    strokeColor: "#CC0099",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    geodesic: true,
    map: map,
  });

  update();
}

function update() {
  const path = [
    marker1.getPosition() as google.maps.LatLng,
    marker2.getPosition() as google.maps.LatLng,
  ];

  poly.setPath(path);
  geodesicPoly.setPath(path);

  const heading = google.maps.geometry.spherical.computeHeading(
    path[0],
    path[1]
  );

  (document.getElementById("heading") as HTMLInputElement).value =
    String(heading);
  (document.getElementById("origin") as HTMLInputElement).value = String(
    path[0]
  );
  (document.getElementById("destination") as HTMLInputElement).value = String(
    path[1]
  );
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">
let marker1, marker2;
let poly, geodesicPoly;

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: { lat: 34, lng: -40.605 },
  });

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(
    document.getElementById("info")
  );
  marker1 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 40.714, lng: -74.006 },
  });
  marker2 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 48.857, lng: 2.352 },
  });

  const bounds = new google.maps.LatLngBounds(
    marker1.getPosition(),
    marker2.getPosition()
  );

  map.fitBounds(bounds);
  google.maps.event.addListener(marker1, "position_changed", update);
  google.maps.event.addListener(marker2, "position_changed", update);
  poly = new google.maps.Polyline({
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    map: map,
  });
  geodesicPoly = new google.maps.Polyline({
    strokeColor: "#CC0099",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    geodesic: true,
    map: map,
  });
  update();
}

function update() {
  const path = [marker1.getPosition(), marker2.getPosition()];

  poly.setPath(path);
  geodesicPoly.setPath(path);

  const heading = google.maps.geometry.spherical.computeHeading(
    path[0],
    path[1]
  );

  document.getElementById("heading").value = String(heading);
  document.getElementById("origin").value = String(path[0]);
  document.getElementById("destination").value = String(path[1]);
}

window.initMap = initMap;
サンプルを表示

サンプルを試す

エンコード メソッド

Maps JavaScript API 内のパスは通常、LatLng オブジェクトの Array として指定します。しかし、このような配列はそのまま渡すには大きすぎることが多々あります。代わりに Google のエンコード化ポリライン アルゴリズムを使うと、指定されたパスを圧縮し、後からデコードして復元することができます。

geometry ライブラリには、ユーティリティでポリラインのエンコードとデコードを行うための encoding 名前空間があります。

静的メソッド encodePath() は、指定されたパスをエンコードします。このメソッドには LatLng の配列か、Polyline.getPath() によって返される MVCArray を渡すことができます。

エンコードされたパスをデコードするには、decodePath() メソッドを呼び出して、エンコードした文字列を渡します。

以下のサンプルは、ミシシッピ州のオックスフォードの地図を表示します。 地図上でクリックすると、ポリラインに地点が追加されます。また、ポリラインを作成すると、それをエンコードした文字が下に表示されます。

TypeScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 14,
      center: { lat: 34.366, lng: -89.519 },
    }
  );
  const poly = new google.maps.Polyline({
    strokeColor: "#000000",
    strokeOpacity: 1,
    strokeWeight: 3,
    map: map,
  });

  // Add a listener for the click event
  google.maps.event.addListener(map, "click", (event) => {
    addLatLngToPoly(event.latLng, poly);
  });
}

/**
 * Handles click events on a map, and adds a new point to the Polyline.
 * Updates the encoding text area with the path's encoded values.
 */
function addLatLngToPoly(
  latLng: google.maps.LatLng,
  poly: google.maps.Polyline
) {
  const path = poly.getPath();

  // Because path is an MVCArray, we can simply append a new coordinate
  // and it will automatically appear
  path.push(latLng);

  // Update the text field to display the polyline encodings
  const encodeString = google.maps.geometry.encoding.encodePath(path);

  if (encodeString) {
    (document.getElementById("encoded-polyline") as HTMLInputElement).value =
      encodeString;
  }
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 14,
    center: { lat: 34.366, lng: -89.519 },
  });
  const poly = new google.maps.Polyline({
    strokeColor: "#000000",
    strokeOpacity: 1,
    strokeWeight: 3,
    map: map,
  });

  // Add a listener for the click event
  google.maps.event.addListener(map, "click", (event) => {
    addLatLngToPoly(event.latLng, poly);
  });
}

/**
 * Handles click events on a map, and adds a new point to the Polyline.
 * Updates the encoding text area with the path's encoded values.
 */
function addLatLngToPoly(latLng, poly) {
  const path = poly.getPath();

  // Because path is an MVCArray, we can simply append a new coordinate
  // and it will automatically appear
  path.push(latLng);

  // Update the text field to display the polyline encodings
  const encodeString = google.maps.geometry.encoding.encodePath(path);

  if (encodeString) {
    document.getElementById("encoded-polyline").value = encodeString;
  }
}

window.initMap = initMap;
サンプルを表示

サンプルを試す

ポリゴンとポリラインの関数

ジオメトリ ライブラリの poly 名前空間には、指定した地点がポリラインまたはポリゴンの内部か付近に存在するかどうかを確認するユーティリティ機能があります。

containsLocation()

containsLocation(point:LatLng, polygon:Polygon)

指定した地点がポリゴンの内部に含まれているかどうかを確認するには、その地点とポリゴンを google.maps.geometry.poly.containsLocation() に渡します。この関数は、指定の地点がポリゴンの内部か境界に存在すれば、true を返します。

以下のコードは、ユーザーがクリックした地点が、指定した三角形の内部にあれば「true」を、外部にあれば「false」をブラウザのコンソールに表示します。

function initialize() {
  var mapOptions = {
    zoom: 5,
    center: new google.maps.LatLng(24.886, -70.269),
    mapTypeId: 'terrain'
  };

  var map = new google.maps.Map(document.getElementById('map'),
      mapOptions);

  var bermudaTriangle = new google.maps.Polygon({
    paths: [
      new google.maps.LatLng(25.774, -80.190),
      new google.maps.LatLng(18.466, -66.118),
      new google.maps.LatLng(32.321, -64.757)
    ]
  });

  google.maps.event.addListener(map, 'click', function(event) {
    console.log(google.maps.geometry.poly.containsLocation(event.latLng, bermudaTriangle));
  });
}

google.maps.event.addDomListener(window, 'load', initialize);

以下は別のサンプルで、クリックした地点がバミューダ トライアングルの内部にあれば青い三角を、外部にあれば赤い丸を描画します。

サンプルを表示

isLocationOnEdge()

isLocationOnEdge(point:LatLng, poly:Polygon|Polyline, tolerance?:number)

任意の地点がポリラインの線上か付近、またはポリゴンの境界線上か付近に存在するかどうかを確認するには、その地点とポリラインまたはポリゴン、そして角度で指定した許容値(省略可)を google.maps.geometry.poly.isLocationOnEdge() に渡します。この関数は、指定した地点と、ラインまたは境界線との最短距離が指定の許容値以下であれば、true を返します。なお、デフォルトの許容値は 10-9 度です。

function initialize() {
  var myPosition = new google.maps.LatLng(46.0, -125.9);

  var mapOptions = {
    zoom: 5,
    center: myPosition,
    mapTypeId: 'terrain'
  };

  var map = new google.maps.Map(document.getElementById('map'),
      mapOptions);

  var cascadiaFault = new google.maps.Polyline({
    path: [
      new google.maps.LatLng(49.95, -128.1),
      new google.maps.LatLng(46.26, -126.3),
      new google.maps.LatLng(40.3, -125.4)
    ]
  });

  cascadiaFault.setMap(map);

  if (google.maps.geometry.poly.isLocationOnEdge(myPosition, cascadiaFault, 10e-1)) {
    alert("Relocate!");
  }
}

google.maps.event.addDomListener(window, 'load', initialize);