Google Maps: выделение областей на карте

Владимир | | HTML, JavaScript, Web разработка.

google maps circle logo

О сервисах Google Maps и Яндекс.Карты слышали, наверное, практически все пользователи интернета. Но если для рядового пользователя их возможности ограничиваются стандартным интерфейсом, то для разработчиков всё гораздо интереснее.

Оба сервиса предоставляют API, который позволяет получить дополнительную информацию, изменить внешний вид и работу карт.

Сегодня я хочу показать небольшой пример, позволяющий посетителю выделить область на карте (использоваться будут Google Maps). В дальнейшем, эту область можно сохранить в базе данных вашего сайта.

Прежде всего, сформулируем задачу.

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

Т.к. для обоих случаев API практически одинаков, рассмотрим вариант с окружностью.

Итак, принцип работы будет следующий.

1) Посетитель кликает по произвольной точке на карте, мы ставим на неё маркер (чтобы удобнее было ориентироваться) и считаем её центром области.

2) Посетитель кликает по любой точке на границе области. Мы рассчитываем расстояние от неё до центра (радиус) и строим окружность.

Если посетитель захочет изменить область, то ему будет достаточно снова сделать два клика. Мы убираем старую окружность и строим новую.

Поэкспериментировать с этим примером можно на демонстрационной страничке или, если хотите, качайте архив с исходными файлами.

Source

Переходим к реализации.

Прежде всего, создадим страницу с картой.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Окружности на Google Maps</title>
</head>
<body>
    <div id="my_map" style="width:600px;height:400px"></div>
    <script src="main.js"></script>
</body>
</html>

Здесь всё предельно просто. Создаём блок для карты (my_map), указываем его размеры (600х400 px) и подключаем файл со скриптами (main.js), которые будут выполнять всю работу.

Рассмотрим подробнее main.js.

В первую очередь нам нужно подключить карту.

var map, circle, circleOptions, setCenter, marker;

function initialize() {
    var myLatlng = new google.maps.LatLng(50.45127, 30.523368); //Kiev
    var myOptions = {
        zoom: 9,
        center: myLatlng,
        mapTypeId: google.maps.MapTypeId.HYBRID
    }
    map = new google.maps.Map(document.getElementById("my_map"), myOptions);
}
  
function loadScript() {
    var script = document.createElement("script");
    script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=initialize";
    document.body.appendChild(script);
}

window.onload = loadScript;

Работает этот код следующим образом. При возникновении события window.onload (завершена загрузка страницы) будет вызвана функция loadScript, которая добавит в конец страницы тег script, подключающий Google Maps.

Обратите внимание на GET параметр callback. В нём передаётся название функции (initialize), которая будет вызвана сразу после загрузки скрипта.

В этой функции (initialize) мы указываем настройки (координаты центра, приближение, тип карты) и создаём карту (строка 10).

В результате внутри блока my_map появится обычная карта Google.

Переходим к созданию окружности.

Прежде всего, немного теории.

С точки зрения API Google Maps, окружность (также как и многоугольник) является оверлеем (overlay). Для её создания используется класс google.maps.Circle, а однозначно задать её размещение можно с помощью центральной точки — center (объект типа LatLng в котором хранится широта и долгота) и радиуса — radius (в метрах).

Кроме того, в большинстве случаев желательно указать цвет окружности, её заливку и прозрачность. Параметры – fillColor, fillOpacity, strokeColor и strokeOpacity.

В нашем случае посетитель строит окружность с помощью кликов по карте, значит, нужно установить обработчик события click и в нём выполнять настройку и создание окружности.

Обработчик события click получит параметр event, который содержит координаты точки, по которой кликнул посетитель.

После второго клика мы получим координаты второй точки и сможем рассчитать расстояние между ними, т.е. радиус окружности.

Теперь взгляните на код.

function initialize() {
    ......
    setCenter = true;
    
    circleOptions = {
        fillColor:"#00AAFF",
        fillOpacity:0.5,
        strokeColor:"#FFAA00",
        strokeOpacity:0.8,
        strokeWeight:2,
        clickable:false
    }

    google.maps.event.addListener(map, 'click', function(event) {
        if (setCenter) {
            if (marker != undefined) {
                marker.setMap(null);
            }
			marker = new google.maps.Marker({
				position:event.latLng,
				clickable:false
			});
			marker.setMap(map);
            circleOptions.center = event.latLng;
            setCenter = false;
        }
        else {
            //рассчитываем расстояние между точками
            var radius = distHaversine(circleOptions.center, event.latLng)
            circleOptions.radius = radius*1000;
            if (circle != undefined) {
                circle.setMap(null);
            }
            circle = new google.maps.Circle(circleOptions);
            circle.setMap(map);
            setCenter = true;
        }
    });
}

Нам нужно отличить первый клик от второго. Для этого используем переменную setCenter. Если она равна true, то в данный момент выполняется установка центра окружности.

В переменной circleOptions сохраняем хеш с начальными настройками окружности (цвет, прозрачность и т.п.).

После этого устанавливаем обработчик события click (строка 14).

Логика работы здесь следующая. Если setCenter == true (установка центра окружности), то мы убираем старый маркер (строки 16-18) и создаём новый в указанной точке (строки 19-23).

Затем, сохраняем центр окружности (параметр circleOptions.center) и присваиваем setCenter значение false (т.е. запоминаем, что следующий клик по карте укажет радиус окружности).

После второго клика мы рассчитываем расстояние (функция distHaversine, о ней чуть ниже) и устанавливаем параметр circleOptions.radius.

Затем убираем старую окружность (если она есть) и создаём новую (строки 31-35).

В результате на карте появится новая окружность.

Обратите внимание, что переменные map, circle, circleOptions, setCenter и marker объявлены в глобальной области видимости. Это необходимо, чтобы их значения сохранялись между вызовами обработчиков событий.

Теперь разберём функцию расчета расстояний между точками.

rad = function(x) {return x*Math.PI/180;}

distHaversine = function(p1, p2) {
    var R = 6371; // earth's mean radius in km
    var dLat  = rad(p2.lat() - p1.lat());
    var dLong = rad(p2.lng() - p1.lng());
    
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    var d = R * c;
    
    return d.toFixed(3);
}

Это реализация формулы гавернсинусов на JavaScript. Взял я её здесь. Формула позволяет определить расстояние между двумя точками на поверхности сферы (длину дуги).

Если вас интересует эта тема, почитайте статью Вычисление расстояния и начального азимута между двумя точками на сфере.

В нашем случае удобно, что приведённая функция правильно работает с объектом LatLng.

И в заключение, пару замечаний о хранении данных.

После кликов посетителя координаты области будут находиться в объекте circleOptions. Их можно передать на сервер, например, с помощью AJAX запроса. Таким образом, при повторном входе посетителя на сайт, можно будет восстановить область.

Вообще, применений этих возможностей масса, главное творчески подойти к их использованию 😉

Успехов!

Интересно почитать

Муляжи блюд — реклама кафе, которая убивает наповал.

  • Отлично! хотелось бы еще тьюториалов о хранении (с примерами и кодом) введенных данных

  • не знал…

  • Carambaaaaa

    Было бы весьма интересно, если бы выделялись организации находящиеся в этой окружности

    • Для реализации нужен список организаций с координатами. Определить, попадают ли координаты внутрь окружности достаточно просто.

      • Carambaaaaa

        Владимир, не могли бы вы привести пример реализации ?

        • Мог бы, но мне нужен список организаций с координатами. Выдумывать их просто лень 🙂

        • Carambaaaa

          в kml файле пойдет?

        • Да, конечно.

        • Carambaaaa

          zalil.ru/31238736

        • Да, конечно.

  • Famalinka

    Здравствуйте, наконец то я нашла человека, который РЕАЛЬНО РУЛИТ в картах гугл :)!! Скажите пожалуйста уважаемый автор у меня есть к вам вопрос по картам, который бы я не хотела демонстрировать на всеобщее обозрение, вы можете ответить мне вне комментов?? как с вами связаться? 

  • ndr334

    А есть возможность выделить зоны сложной формы? например районы города и подсветить их разными цветами? Особый шик, если можно ещё сделать другой слой с выделением например зон на том же участке карты, но по другому принципу, скажем зонами ответственности жеков. и переключалку сделать между этими режимами?

    • Да, есть. Для этого можно использовать объект Polygon (многоугольник). Вам нужно задать координаты, цвета заливки и границы, прозрачность. Пример.

      • ndr334

        Спасибо огромное!! а переключение между наложениями полигонов реально реализовать?

        • Да, можно. В примере показан код создания полигона, но вы можете его убрать и создать новый.
          Для удаления можно использовать следующий код:

          bermudaTriangle.setMap(null);

  • Алексей Трикоз

    Владимир, просьба подсказать откуда начать — встала задача автоматически определять координаты точки на произвольном расстоянии от начала маршрута и никак не могу понять, с какого бока подойти к решению задачи.

    • А в каком виде маршрут задан?
      У вас есть координаты исходной точки и расстояние от неё. Ещё нужен вектор (направление).

  • Anatoli Rijov

    Здравствуйте Владимир , подскажите как решить проблему. Мне нужно по координатам выделить контур объекта на карте , отметить поворотные точки и выделить цветом объект (роща , ущелье , аллея и т.д.) и все сохранить на ПК . Это вообще возможно ? У меня получается сделать Screen и перевести в Paint карту только с одной точкой координат .

    • Не уверен, что правильно понял задачу, но раз Вам нужно обвести объект и есть координаты вершин, то, скорее всего, нужно использовать контур (полигон).
      Сделать это можно так:

      var myPoints = [
      {lat: 44.138973, lng: 39.096786},
      {lat: 44.138120, lng: 39.097465},
      ... //остальные координаты
      {lat: 44.138973, lng: 39.096786} //последняя точка должна совпадать с первой, т.е. нужно замкнуть контур
      ];

      // создаем контур
      var myObject = new google.maps.Polygon({
      paths: myPoints,
      strokeColor: '#FFFF00', //цвет линии обводки
      strokeOpacity: 0.8, // прозрачность линии обводки
      strokeWeight: 3, //толщина линии обводки
      fillColor: '#FF00FF', //этим цветом будет заполнен контур
      fillOpacity: 0.2 //прозрачность заливки контура
      });
      myObject.setMap(map); //размещаем контур на карте

      • Anatoli Rijov

        Спасибо большое за подробную информацию , единственное , что мне непонятно в какой карте или приложении , все это можно реализовать .

        • Google maps, аналогичные возможности есть у Яндекс.Карт. Также можно использовать Open Street Map, но там немного сложнее.

        • Anatoli Rijov

          Я уже неделю ищу и в Google Maps и Яндекс.Карты там не нашел таких функций , пожалуйста подскажите , где строка ввода координат .

        • Похоже я с самого начала Вас не так понял. Пример в этой статье и в комментарии показывает использование программного интерфейса (API) карт. Т.е. нужно создать файл index.html с блоком для карты и написать JavaScript код, который подключит карту и нарисует на ней многоугольник.

          Вы, похоже, хотите рисовать с помощью визуальных инструментов. Попробуйте использовать «народную карту» Яндекса — https://n.maps.yandex.ru/

  • Анна Гордина

    Владимир, добрый день! А можно чуть упростить пример — нужен круг с центром в определенной точке координат и радиусом 50км, например, сразу при загрузке карты?
    В любом случае спасибо за статью!