Google Maps: перемещение карты и маркеров

В одном из предыдущих сообщений я писал про то, как вывести карту с помощью Google Maps API и поставить на ней маркер. Тема, как выяснилось, весьма востребованная, и я решил начать по ней серию публикаций, где буду рассказывать о том, как сделать различые действия с Google Maps. Итак, сегодня речь пойдет о перемещении самой карты и маркеров на ней при наступлении каких-либо внешних событий.

Для начала немного перепишем код, который приводился в прошлом примере, — сделаем его в ООП-стиле:

MyMap = function () { 
self = this; // потребуется для ссылки на экземпляр MyMap во вложенных функциях
this.map = new google.maps.Map(document.getElementById("map_canvas"), { zoom: 10, center: {lat: 55.763585, lng: 37.560883}, // можно не создавать объект LatLng явно, а использовать литерал такого вида mapTypeId: google.maps.MapTypeId.ROADMAP });
this.markers = new Array;
this.markers[0] = new google.maps.Marker({ position: new google.maps.LatLng(55.763525,37.560893), // опять обходимся без дополнительной переменной
map: this.map, title: 'Пробная точка!' }); // сюда будет добавляться новый код
};
myMap = new MyMap;
google.maps.event.addDomListener(window, 'load', myMap);

Здесь мы создали объект myMap, внутри которого хранится экземпляр карты, с которой мы работаем и массив маркеров на карте (пока с одним элементом). Кроме того, для того, чтобы повесить код отображения на событие onload, мы воспользовались методом google.maps.event.addDomListener, что является более правильным решением, чем использование window.onload, так как позволяет избежать переопределения обработчика, если он был задан где-то еще.

Шаг 1. Узнаем коордианты нужных объектов

Поскольку все объекты на карте привязаны к географическим коордианам, нам нужно сначала научится узнавать координаты интересущего нас места. Сделать это можно двумя способами: зайти на основной сайт Google Maps, найти там нужный объект, щелкнуть по нему правой кнопкой мыши и выбрать "Что это". Появится краткая информация об объекте, включая два числа в самой нижней строке. Это и будут его координаты.

Второй вариант — чуть более сложный, но он позволяет получить координаты не только точки, но и протяженных объектов в виде JSON. Для этого нужно выполнить запрос по адресу http://maps.googleapis.com/maps/api/geocode/json?address=название_объекта&sensor=false&language=ru, где вместо название_объекта подставляем требуемое название, например, http://maps.googleapis.com/maps/api/geocode/json?address=Нижний+Новогород&sensor=false&language=ru В полученном JSON можно использовать либо location (координаты центра), либо bounds (объект Rectangle с координатами границ протяженного объекта), либо viewport (координаты области просмотра для отображения объекта).

Шаг 2. Перемещение видимой области карты

Для перемещения видимой области в Google Maps API у объекта Map есть несколько методов: panTo, panToBounds и panBy, fitBounds, setCenter. Метод panTo плавно перемещает центр карты на указанные географические коордианты (объект LatLng), setCenter делает то же самое без анимации, panToBounds — смещает карту так, чтобы уместить в ней объект с заданными границами, fitBounds делает то же самое, но при необходимости еще меняет zoom (что полезно для больших объектов), и panBy — смещает карту на указанное количество пикселей.

Мы будем делать перемещения по нажатию кнопок, поэтому сначала добавим в наш код сами кнопки (их нужно поместить после тега div):

<button id="moscow">Москва</button>
<button id="peterburg">Санкт-Петербург</button>
<button id="n_novgorod">Нижний Новогород</button><br />
<button id="map_left">← Влево</button>
<button id="map_right">Вправо →</button>

Теперь добавим обработчики события onclick, по которым будет происходить перемещение. Опять воспользуемся google.maps.event.addDomListener. Этот код нужно добавить после комментария "сюда будет добавляться новый код".

this.MoveMapTo = function(lat,lng) { 
 return function(e) {
   self.map.panTo(new google.maps.LatLng(lat,lng));
 }
}
google.maps.event.addDomListener(document.getElementById('moscow'), 'click', this.moveMapTo(55.763525,37.560893));
google.maps.event.addDomListener(document.getElementById('peterburg'), 'click', this.moveMapTo(59.939956, 30.344911));
google.maps.event.addDomListener(document.getElementById('n_novgorod'), 'click', this.moveMapTo(56.325418, 43.935667));

Поскольку в обработчик события onclick нельзя передавать параметры, мы применили возможность JavaScript под названием замыкание (closure). Работает оно следующим образом: возвращает анонимную функцию, где в качестве некоторых переменных используются переменные вышестоящей функции (в данном случае это параметры lat и lng), которые в момент создания "запоминаются". Вместо замыкания можно было написать три обычных функции, которые делали бы panTo на жестко прописанные в них координаты, и передать их имена вместо вызова this.moveMapTo, но это менее красивое и профессиональное решение.

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

google.maps.event.addDomListener(document.getElementById('map_left'), 'click', function () { 
 self.map.panBy(25,0);
});
google.maps.event.addDomListener(document.getElementById('map_right'), 'click', function () {
 self.map.panBy(-25,0);
});

Шаг 3. Перемещение маркера

Теперь попробуем переместить маркер, который мы добавили на карту (он хранится в массиве markers внутри объекта myMap) по таймеру. Делается это с помощью метода setPosition, которому в качестве параметра передается объект LatLng с новыми координатами. В нашем случае мы будем постоянно смещать маркер чуть-чуть на восток относительно его текущей позиции (увеличивать долготу) и случайным образом немного изменять его широту. Для этого получаем текущие координаты маркера с помощью getPosition().lat() и getPosition().lng(), прибавляем к ним нужные значения, создаем на основе посчитанных данных новый объект LatLng и передаем его в качестве параметра методу setPosition.

setInterval(function() { 
 self.markers[0].setPosition( new google.maps.LatLng(self.markers[0].getPosition().lat()+Math.random()*0.0002-0.0001, self.markers[0].getPosition().lng()+0.0001));
},100);

Шаг 4. Проверка, что объект находится в области видимости

В этом примере мы добавим еще один таймер, который раз в пять секунд будет проверять, что наш маркер видим на карте, и если это так, будет центрировать карту на нем. Получить видимую область карты можно с помощью метода getBounds класса Map. Этот метод возвращает объект Rectangle, у которого есть метод contains, который позволяет проверить, лежит ли заданная точка внутри прямоугольника. В итоге получаем следующий код:

setInterval(function() { 
 if (self.map.getBounds().contains(self.markers[0].getPosition())) {
   self.map.panTo(self.markers[0].getPosition());
 }
},5000);

Вот и все на сегодня! Посмотреть полностью работающий пример можно в прикрепленном файле. Пишите в комментариях, какие еще примеры действий с картами вы хотели бы увидеть, возможно, я расскажу о них в следующих выпусках.