В сьогоднішньому пості розглянемо роботу з Google Maps API v3 чере JavaScript.
- Розмістимо на веб-сторінці карту частини Західної України
- Встановимо позначки в потрібних місцях
- Прив’яжемо до позначок віконця з інформацією, що будуть вигулькувати при клацанні мишею
- Проведемо на карті автомобільний маршрут за допомогою служби Google Direction Service
- Опрацюємо подію завершення завантаження мапи
Робота з Google Maps API v3 через JavaScript детально описана в навчальній інструкції. Тут буде дано робочий приклад коду та пояснено тонкощі його роботи.
Набільша проблема виникає з проведенням маршруту. Справа в тому, що служба Google Direction Service встановлює квоти та обмеження на її використання, і при частих запитах спрацьовує правильно “через раз”. Оновлюючи сторінку, бачиш, що іноді проводить маршрут нормально, а буває, що повертає неправильні результати із зайвими прямимим лініями:
Щоб уникнути цієї проблеми, я раджу:
- Брати для позначення траси мінімально можливу кількість точок. В нашому прикладі цілком достатньо 3-х точок, хоча дорога не близька. Це не значить, що буде 2 відрізки між 3-ма точками. Direction Service сам передасть багато проміжних точок, потрібних для креслення правильної траси.
- Робити затримку між звертаннями до Google Direction Service
З мого досвіду, в результаті таких заходів “глюки” трапляються вкрай рідко, хоча іноді все-таки трапляються.
Тепер розміщую код з коментарями, а далі напишу пояснення.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
html, body { height: 100%; margin: 0; padding: 0;}
#splash {
height: 20px;
padding: 5px;
}
#map-canvas {height: 800px;}
</style>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
<script type="text/javascript">
function initialize() {
var center = {lat: 50.00, lng: 25.50}; // Центр мапи: географічна широта і довгота
var markers = [ // Точки, в яких будемо ставити позначки
{
"title": 'Львів', // Назва (підказка, що випливає при наведенні курсора миші)
"lat": '49.840221', // Географічна широта
"lng": '24.017666', // Географічна довгота
"description": 'славне місто Львів', // Текст для інформаційного віконця
"mark": true, // Ознака - встановлювати позначку в цій точці
"forpath": true // Ознака - використовувати точку для прокладання маршруту
},
{
"title": 'Золочів',
"lat": '49.810612',
"lng": '24.895924',
"description": 'місто Золочів',
"mark": true,
"forpath": false
},
{
"title": 'Тернопіль',
"lat": '49.541226',
"lng": '25.597669',
"description": 'файне місто Тернопіль',
"mark": true,
"forpath": false
},
{
"title": 'Волочиськ',
"lat": '49.526194',
"lng": '26.164845',
"description": 'Волочиськ',
"mark": true,
"forpath": false
},
{
"title": 'Хмельницький',
"lat": '49.412735',
"lng": '26.992933',
"description": 'Проскурів-Хмельницький',
"mark": true,
"forpath": true
},
{
"title": 'Старокостянтинів',
"lat": '49.760959',
"lng": '27.214039',
"description": 'місто Старокостянтинів',
"mark": true,
"forpath": false
},
{
"title": 'Шепетівка',
"lat": '50.181513',
"lng": '27.049407',
"description": 'місто Шепетівка',
"mark": true,
"forpath": true
}
];
// Параметри мапи
var mapOptions = {
center: center, // Центр
zoom: 8 // Масштаб
};
// Завантаження мапи - змінна map зберігає посилання на нашу карту
var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var latlngbounds = new google.maps.LatLngBounds(); // Об’єкт для корекції прив’язки
var infoWindow = new google.maps.InfoWindow(); // Об’єкт - інформаційне віконце
var lat_lng = new Array(); // Масив, в який ми відберемо лише ті точки, що використовуються для прокладання маршруту
// Перебір масиву точок
for (var i = 0; i < markers.length; i++) {
var data = markers[i];
var myLatlng = new google.maps.LatLng(data.lat, data.lng); // Створюємо об’єкт - точка на мапі
if (data.mark) { // Якщо потрібно - встановлюємо маркер (позначку)
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: data.title
});
latlngbounds.extend(marker.position);
// Встановлення обробника події 'click' для відображення інформаційного віконця
// В кожній ітерації для створюваної функції-обробника треба зберігати копії об’єктів marker і data
// Для цього застосовуємо "вираз з функцією негайного виконання" (IIFE or IEFE)
(function (marker, data) {
google.maps.event.addListener(marker, "click", function (e) {
infoWindow.setContent(data.description);
infoWindow.open(map, marker);
});
})(marker, data); // Передача актуальних параметрів в кожній ітерації
}
if (data.forpath) { // Якщо точка має брати участь в прокладені маршруту, зберігаємо її в масиві lat_lng
lat_lng.push(new google.maps.LatLng(data.lat, data.lng));
}
}
// Автокорекція центру і прив’язки відповідно до створених позначок
map.setCenter(latlngbounds.getCenter());
map.fitBounds(latlngbounds);
// Прокладання маршруту
var path = new google.maps.MVCArray(); // Об’єкт для зберігання даних маршруту
var service = new google.maps.DirectionsService(); // Об’єкт для виклику служби Google Direction Service
var poly = new google.maps.Polyline({ // Об’єкт, що креслить
map: map,
strokeColor: '#F3443C' // Колір "олівця"
});
// Перебір масиву точок, відібраних для креслення
for (var i = 0; i < lat_lng.length; i++) {
(function(i) { // для того, щоб функція setTimeout працювала в циклі,
// її також необхідно обгорнути у "вираз з функцією негайного виконання"
if ((i+1) < lat_lng.length) { // опрацьовується пара точок в кожній ітерації, i-та та (i+1)-ша,
// тому останню ітерацію пропускаємо
var src = lat_lng[i]; // початок ділянки шляху
var des = lat_lng[i+1]; // кінець ділянку шляху
window.setTimeout(function() {
// Виклик Google Direction Service
service.route({
origin: src,
destination: des,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function(result, status) { // Отримання результату
if (status == google.maps.DirectionsStatus.OK) {
// Якщо відповідь нормальна, опрацьовуємо її
for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++) {
// Зберігаємо точки, по яких треба креслити
path.push(result.routes[0].overview_path[i]);
}
poly.setPath(path); // і креслимо
}
});
}, i * 700); // викликаємо із затримкою 0.7 секунди на кожній ітерації
}
})(i); // передаємо актуальне значення лічильника циклу
}
// Коли настане подія 'idle' - сховати повідомлення про те, що мапа завантажується
google.maps.event.addListenerOnce(map, 'idle', function(){
document.getElementById("splash").style.visibility = 'hidden';
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="splash">Будь ласка, зачекайте хвилинку, завантажується мапа...</div>
<div id="map-canvas"></div>
</body>
</html>
Отже, мінімальна розмітка HTML5, в тілі два елементи DIV. В першому напис “Зачекайте хвилинку…”, другий служить контейнером для карти. Карта має розмір, визначений контейнером, тому для контейнера необхідно задати розміри (чи, принаймні, висоту) в стилях.
#map-canvas {height: 800px;}
Підключення Google Maps API
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
Весь javascript-код містить функція initialize, рядок
google.maps.event.addDomListener(window, 'load', initialize);
встановлює обробник події “завантаження документу” для запуску цієї функції.
Точки на мапі задаються парами географічних координат: широтою(Latitude) і довготою (Longtitude) в градусах. На початку функції визначається точка центру мапи center і масив markers точок, які будуть використані для позначок і креслення маршруту. Зверніть увагу, що всього в масиві 7 точок, а для проведення маршруту будуть використані лише 3 з них (це задаємо ключем forpath).
Далі задаються параметри карти. Параметрів може бути багато, обов’язкових 2: center — центр карти і zoom масштаб (можна задавати від 0 до 20). Завантажується мапа, посилання на неї присвоюється змінній map.
Далі перебирається масив markers, встанолюються позначки за допомогою методів Google Maps API і відбираються в окремий масив lat_lng точки, необхідні для прокладання маршруту (див. коментарі).
Встановлення для позначки обробника події click обгорнуте у вираз з функцією негайного виконання. Це пов’язано з особливостями роботи циклів і замикань (функцій у функціях) у JavaScript. Якщо написати просто код
google.maps.event.addListener(marker, "click", function (e) {
infoWindow.setContent(data.description);
infoWindow.open(map, marker);
});
то для всіх створених позначок обробники події click будуть звертатися за посиланнями data і marker до однієї й тої самої області пам’яті, сформованої після закінчення циклу ітерацій (при i = 7, тобто до елемента масиву markers7, який не існує).
Щоб зрозуміти роботу функцій-замикань і виразів з функцією негайного виконання, раджу курити це і це, або це, або щось подібне. А може, я сам згодом напишу нашою мовою :)
Далі викликаються функції автокорекції центру і прив’язки карти відповідно до встановлених позначок і починається новий цикл перебору точок з масиву lat_lng, відібраних для креслення маршруту.
На кожному кроці визначається пара точок початок-кінець і передається в Google Direction Service. На основі відповідей цієї служби креслиться маршрут.
Як було сказано вище, між викликами служби організована затримка за допомогою функції setTimeout. Щоб функція setTimeout працювала в циклі правильно, її також треба обгорнути у вираз з функцією негайного виконання.
В кінці додається обробник для події ‘idle’, який ховає перший DIV з написом “Зачекайте хвилинку…”
Ось, що вийшло: