Картографический сервис – зачем это? Ну например, я 10 лет жил в нашей маленькой провинции, а потом взял и понаехал в Москву, и всё для меня так ново. А где магазины, боулинг, кафешки, парки отдыха – надо знать же, где тратить московскую зарплату. Но вот беда, как узнать? Раньше был справочник «Желтые страницы» и там была карта и всё по адресам. Чтобы найти что-то уходило масса времени. Сейчас стало всё в разы проще. Вот прекрасный пример: http://www.pushkino.org/. Но это далеко не всё.
Я могу отслеживать погоду, пожары, пробки (кстати!) в реальном времени.
Мой заказчик может не вводить свой адрес, а попросту отметить его на карте и я буду знать куда доставить ему товар – какое классное решение, не надо всего этого – «Проспект маршала Блюхера, 43, г. Санкт-Петербург, Россия».
Задача для примера
Всё лучше узнавать практически, так что сделаем задачу для примера, чтобы обрести навыки. Вот примерный план работ:
- Вывести карту (надо же!)
- Задать город
- Переместить карту к городу
- Маркером указать адрес
- Добавить информации
- Вывести карту (надо же!)
- Сохранить маркер с иноформацией (при клике на него вывести ее)
- Избежать нагромождения (т.е. сделать кластеризацию) маркеров.
Как делать?
Ключ API
Для v.3 не нужен
Map\Marker\InfoWindow
Для работы нам понадобится 3 основных объекта. Первое – это карта.
Карта создается очень просто. У нас есть какой-то определенный контейнер:
<div id="map_canvas"></div>
Подключаем скрипт:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
Инициализируем карту:
function initialize() {
var myLatlng = new google.maps.LatLng(-34.397, 150.644);
var myOptions = {
zoom: 8,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
center: myLatlng – это координаты центра карты
zoom – это увеличение при инициализации
mapTypeId – тип (политическая, физическая, гибрид)
Карта готова!
Второе — это метки:
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title:"Hello World!"
});
position – собственно координаты метки
map – на какую карту метку поместить
title – при наведении мыши будет писать “Hello World!”.
InfoWindow
var contentString = '<div id="content">Тут всё то про что должно быть рассказано</div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Uluru (Ayers Rock)'
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map,marker);
});
content – содержимое в метке
google.maps.event.addListener(marker, 'click', function() { infowindow.open(map,marker); });
— при клике на метку, показать окно с информацией, на карте map с привязкой к marker.Geocoding
Geocoding – это просто отличная библиотека, которая позволяет делать всего 2 вещи:
- По наименованию чего-то, найти это на карте и сообщить координаты
- По координатам, сообщить всё что находится на этих координатах.
Запрос выглядит так. Например, мы хотим узнать где находится Иваново. Пишем запрос:
http://maps.googleapis.com/maps/api/geocode/json?address=Иваново&sensor=false&language=ru
И в ответе приходит:
{
"status": "OK",
"results": [
{
"types": [ "locality", "political" ],
"formatted_address": "город Иваново, Ивановская область, Россия", - полный адрес
"address_components": [
{ - составляющие адреса
"long_name": "город Иваново",
"short_name": "город Иваново",
"types": [ "locality", "political" ]
},
{
"long_name": "Ивановский район",
"short_name": "Ивановский район",
"types": [ "administrative_area_level_2", "political" ]
}, {
"long_name": "Ивановская область",
"short_name": "Ивановская область",
"types": [ "administrative_area_level_1", "political" ]
}, {
"long_name": "Россия",
"short_name": "RU",
"types": [ "country", "political" ]
} ],
"geometry": {
"location": { - местонахождение
"lat": 56.9924086,
"lng": 40.9677888
},
"location_type": "APPROXIMATE", "viewport": { - размеры
"southwest": {
"lat": 56.9699256,
"lng": 40.9265167
},
"northeast": {
"lat": 57.0148916,
"lng": 41.0090609
}
},
"bounds": { - границы
"southwest": {
"lat": 56.9699256,
"lng": 40.9265167
},
"northeast": {
"lat": 57.0148916,
"lng": 41.0090609
}
}
}
}
] }
Вся прелесть в том, что можно в address параметре передавать значение на любом языке(Ivanovo, Іваново, <тут была арабская вязь>), еще лучше, что для Санкт-Петербурга прокатывает «Спб» и «Питер». Правда есть и недочеты: мой родной город Ивано-Франковск упорно называет Ивано-Франковськ, на украинский манер.
Вторая возможность, это по координатам узнать адрес:
http://maps.googleapis.com/maps/api/geocode/json?latlng=55.75320193022759,37.61922086773683&sensor=false&language=ru
получаем:
{
"status": "OK",
"results": [ {
"types": [ "street_address" ],
"formatted_address": "Красная пл., 3, город Москва, Россия, 109012",
"address_components": [
{
"long_name": "3",
"short_name": "3",
"types": [ "street_address" ]
}, {
"long_name": "Красная пл.",
"short_name": "Красная пл.",
"types": [ "route" ]
}, {
"long_name": "Тверской",
"short_name": "Тверской",
"types": [ "sublocality", "political" ]
}, {
"long_name": "город Москва",
"short_name": "город Москва",
"types": [ "locality", "political" ]
}, {
"long_name": "АО Центральный",
"short_name": "АО Центральный",
"types": [ "administrative_area_level_2", "political" ]
}, {
"long_name": "Москва",
"short_name": "Москва",
"types": [ "administrative_area_level_1", "political" ]
}, {
"long_name": "Россия",
"short_name": "RU",
"types": [ "country", "political" ]
}, {
"long_name": "109012",
"short_name": "109012",
"types": [ "postal_code" ]
} ],
"geometry": {
"location": {
"lat": 55.7546971,
"lng": 37.6215214
},
"location_type": "ROOFTOP",
"viewport": {
"southwest": {
"lat": 55.7515495,
"lng": 37.6183738
},
"northeast": {
"lat": 55.7578447,
"lng": 37.6246690
}
}
}
},
{ ...
Супер! Для того чтобы указать свой адрес, можно просто кликнуть на свой дом, добавить квартиру – и всё. Иногда это не срабатывает, например, если дома стоят вплотную друг к другу и считываются как 1 объект, а не 2-3, адрес у них будет один. Особенно плохо, когда они находятся на пересечении улиц, и один дом относится к одной улице, а второй – к перпендикулярной, но думаю по необходимости – можно указать улицу, а дом и квартиру уже вбить. Очень удобное для смартфонов решение.
Кстати, не используйте jquery $.getJSON для получения данных, используйте класс Geocoder (http://code.google.com/apis/maps/documentation/javascript/reference.html#Geocoder), он работает лучше (т.е. это означает что getJSON у меня не работает).
А теперь о не очень хорошем. Geocoder – насколько клевая функция, что пользоваться ею можно только 2500 запросов в день. Google предлагает Google API Key Premier от 10000$ в год, и тогда ограничение будет в 100 тыс. запросов в день, причем куча всяких «клевых» дополнений, но я их не могу себе позволить.
Markercluster
Когда слишком много маркеров — это выглядит конечно ужасно. Поэтому хорошо бы делать кластеризацию всех этих маркеров. Тут на Хабре я видел уже обсуждение по этому поводу: http://habrahabr.ru/blogs/google/28621/
В общем, есть отличный инструмент (а тут их целый набор http://code.google.com/apis/maps/articles/toomanymarkers.html) который помогает сделать так, чтобы толпы маркеров не пугали нас.
До:
После:
Это именно то что нам надо.
Эту библиотеку можно скачать тут: http://google-maps-utility-library-v3.googlecode.com/svn/trunk/
Как использовать.
Добавляем библиотеку
<script type="text/javascript" src="/Media/script/map/markerclusterer_packed.js"></script>
Составляем массив маркеров, не добавляя в карту:
var markers = [];
var marker = new google.maps.Marker({
position: latlng
});
markers.push(marker);
markerClusterer = new MarkerClusterer(_this.map, markers,
{
maxZoom: 13,
gridSize: 50,
styles: null
});
maxZoom – максимальный зум при котором мы еще группируем маркеры, дальше – уже нет.
gridSize – размер ячеек сетки, чем меньше значение, тем меньше сетка группировки
styles – дополнительные стили
Код из примера
Я не буду тут расписывать что как собрать, собственно все инструменты готовы, дам ссылки на исходники, и прокомментирую некоторые вещи.
Cерверного кода (asp.net mvc) там очень мало, всего 4 запроса:
- собственно страница
- получить все маркеры (в json)
- загрузить файл (через ajaxUploader) и получить ссылку для картинки
- сохранить в базу данных маркер (на выходе json result = ok)
Основной код jquery ( тут полностью: cocosanka.ru/media/script/map/map.js ) Там есть комментарии, и всё такое.
Некоторые функции требующие пояснения:
Вычисление значения Zoom по границам
(взято отсюда: http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/43958790eafe037f/66e889029c555bee?fwc=2)
this.getZoom = function (bounds) {
var width = $(".map").width();
var height = $(".map").height();
var dlat = Math.abs(bounds.getNorthEast().lat() - bounds.getSouthWest().lat());
var dlon = Math.abs(bounds.getNorthEast().lng() - bounds.getSouthWest().lng());
var max = 0;
if (dlat > dlon) {
max = dlat;
} else {
max = dlon;
}
var clat = Math.PI * Math.abs(bounds.getSouthWest().lat() + bounds.getNorthEast().lat()) / 360.;
var C = 0.0000107288;
var z0 = Math.ceil(Math.log(dlat / (C * height)) / Math.LN2);
var z1 = Math.ceil(Math.log(dlon / (C * width * Math.cos(clat))) / Math.LN2);
//18 – это максимальный zoom для google.maps
return 18 - ((z1 > z0) ? z1 : z0);
}
Функция для «прыжка» маркера:
this.toggleBounceMarker = function()
{
if (_this.setMarker.getAnimation() != null) {
_this.setMarker.setAnimation(null);
} else {
_this.setMarker.setAnimation(google.maps.Animation.BOUNCE);
}
}
Получение адреса:
this.SetAddresses = function (results)
{
$(".address_list").show();
$(".address_list").empty();
var addressText = _this.ComposeAddress(results[0]); ...
}
//Составить строку адреса по первому результату
this.ComposeAddress = function (item) {
retAddress = "";
$.each(item.address_components, function (i, address_item) {
var isOk = false;
$.each(address_item.types, function (j, typeName) {
//не будем брать значения адреса улицы и локали (города) - город потом будет в administrative_level_2
if (typeName != "street_address" && typeName != "locality") {
isOk = true;
}
});
if (isOk) {
if (retAddress == "") {
retAddress = address_item.long_name;
} else {
retAddress = retAddress + ", " + address_item.long_name;
}
}
});
return retAddress;
}
Итого
Google Maps API – очень классная и удобная штука, которая легка в использовании и понимании. Единственно, что плохо – так это слабое покрытие регионов в России, так что сервисам, которые предполагается использовать в глубинке google.maps пока мало интересен, а вот для больших городов (особенно Москва и Питер), а также для Украины – всё отлично.
Geocoding – очень полезная вещь и при правильном использовании может стоить тех денег, что за нее просят (ну или Microsoft или Яндекс
Пример\исходники
На живой пример можно глянуть тут: http://cocosanka.ru/map (может перестать работать если будет достигнут лимит в Geocoding). Вводите город, потом перетаскиваете маркер, потом загружаете картинку и сохранить. При клике на маркеры выводятся картинки.
Исходники: https://bitbucket.org/chernikov/citylocator