Библиотека Geometry

  1. Обзор
  2. Основные понятия сферической геометрии
    1. Функции расстояния и площади
    2. Функции навигации
  3. Кодирование геометрических элементов
  4. Функции многоугольников и ломаных линий
    1. containsLocation()
    2. isLocationOnEdge()

Обзор

Понятия, используемые в настоящем документе, относятся к компонентам, доступным только в библиотеке google.maps.geometry. Эта библиотека не загружается по умолчанию при загрузке Maps JavaScript API. Ее следует явно указать с помощью параметра начальной загрузки libraries. Подробная информация приведена в разделе Обзор библиотек.

Библиотека геометрии Maps JavaScript API предоставляет вспомогательные функции для расчета геометрических данных на поверхности Земли. Библиотека содержит три следующих пространства имен.

  • spherical – содержит средства сферической геометрии, позволяющие рассчитывать углы, расстояния и площади на основе координат широты и долготы.
  • encoding – содержит служебные программы для кодирования и декодирования ломаных линий на основе алгоритма кодированной ломаной линии.
  • poly – содержит вспомогательные функции для расчетов, связанных с многоугольниками и ломаными линиями.

В библиотеке google.maps.geometry отсутствуют классы. Вместо них имеются статические методы для описанных выше пространств имен.

Основные понятия сферической геометрии

Изображения в Maps JavaScript API являются двухмерными и плоскими. Земля же представляет собой трехмерный объект, и ее форму принято называть сплюснутым сфероидом или просто сферой. В Maps API используется сферическое приближение. Для того чтобы представить Землю на плоскости (например, экране компьютера), в интерфейсе используются проекции.

В двухмерных проекциях изображение может быть обманчивым. Поскольку проецирование карты обязательно сопряжено с некоторым искажением, простая евклидова геометрия далеко не всегда применима. Например, кратчайшее расстояние между двумя точками на сфере представляет собой не прямую линию, а дугу большого круга (геодезическую линию), а сумма углов треугольника на поверхности сферы составляет больше 180 градусов.

Вследствие этих различий для вычисления расстояния, направления и площади на сфере (или ее проекции) в геометрических функциях используется сферическая геометрия. Инструменты для расчета этих объектов сферической геометрии содержатся в пространстве имен google.maps.geometry.spherical Maps API. Это пространство имен предоставляет статические методы для расчета скалярных значений по сферическим координатам (широте и долготе).

Функции расстояния и площади

Расстояние между двумя точками равно длине наименьшего из отрезков, проходящего через них. Кратчайший путь называется геодезической линией. Все геодезические линии на сфере являются сегментами большой окружности. Чтобы рассчитать это расстояние, вызовите метод computeDistanceBetween() и передайте ему два объекта LatLng.

Если имеется несколько местоположений, для расчета длины пути можно использовать метод computeLength().

Результаты расчета расстояния выражаются в метрах.

Для расчета площади многоугольной области (в квадратных метрах) вызовите метод computeArea() и передайте ему массив объектов LatLng, определяющих замкнутый контур.

При навигации на сфере направление представляет собой угол относительно фиксированного ориентира, обычно истинного (географического) севера. В Google Maps API направления определяются в градусах и измеряются как отклонения по часовой стрелке от истинного севера (0 градусов). Для расчета направления между двумя точками следует использовать метод computeHeading(), передав ему объекты from и to LatLng.

Если известны направление, исходное местоположение и расстояние пути (в метрах), можно вычислить координаты конечной точки с помощью метода computeOffset().

Если заданы два объекта LatLng и значение от 1 до 0, также можно вычислить конечную точку между ними с помощью метода interpolate(), который осуществляет сферическую линейную интерполяцию между этими двумя местоположениями. При этом значение задает дробное расстояние, которое нужно пройти от исходной до конечной точки.

Далее в примере показано, как при нажатии на две точки на карте создаются две ломаные линии – одна геодезическая и одна "прямая" линия, соединяющая две точки, – и вычисляется направление пути между этими двумя пунктами:

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 пути часто задаются в виде массива Array в объекте LatLng. Однако работать с таким массивом часто бывает неудобно. Вместо этого можно использовать алгоритм кодирования ломаной линии Google для сжатия указанного пути, который затем можно восстановить с помощью декодирования.

Библиотека geometry содержит пространство имен encoding для инструментов кодирования и декодирования ломаных линий.

Статический метод encodePath() кодирует указанный путь. Можно передать массив объектов LatLng или MVCArray (который возвращается методом Polyline.getPath()).

Для декодирования закодированного пути нужно просто вызвать метод 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);