среда, 4 июля 2007 г.

Google Mapplets: Концепция и примеры. Практикум

К настоящему моменту мы уже попробовали работать с картами Google (см. статью Google Mapplets: Начинаем работу), вошли во вкус и убедились, что предоставляемые Google Maps API возможности делают создание картографических сервисов как никогда простым и доступным для всех желающих. А раз так, и мы за первые же полчаса знакомства с новым сервисом смогли разместить карту на своем сайте, стоит подробнее остановиться на том, что еще мы можем разместить на нашей карте. Для того, чтобы мы могли с гордостью говорить "наша карта", надо к карте Google добавить хоть немного от нас самих. Например, поставить флажок "здесь был я". И еще оформить этот флажок так, чтобы каждый догадался, что я это именно я, а не кто-то другой. Спасибо Google, они предвосхитили наше скромное желание осчастливить современников столь ценной информацией и предоставили подробное описание. Правда, на английском, но это не вопрос, сейчас мы быстренько переведем и тут же воспользуемся советами.

В нижеследующих примерах мы будем приводить только код JavaScript. Вы можете вставить его в XML описание маплета и опубликовать в Google или вставить в обычную html страницу и использовать на своем сайте. После каждого примера мы будем давать ссылку на полный файл спецификации маплета в формате XML.

Очень простой пример
Следующий код показывает карту с центральной точкой в Пало Альто, Калифорния и устанавливает масштабный коэффициент в значение 13.

var map = new GMap2();
map.setCenter(new GLatLng(37.4419, -122.1419), 13);

Скачать basics.xml.

Объект GLatLng представляет точку на карте, заданную координатами в формате (широта,долгота). Здесь lat сокращение от latitude (широта) и lng - от longitude (долгота). Для долготы обычно используется сокращение lon, так что если на GPS навигаторе или где-то еще увидите аббревиатуру lon, то знайте, что это в точности то же самое, что Google называет lng. Если Вы не знаете координат интересующей Вас точки, Вы можете воспользоваться операцией геокодирования для определения координат искомого места по его адресу. Эта операция будет рассмотрена ниже. От себя могу рекомендовать запустить Google Earth, найти нужное место и навести на него указатель, после чего внизу карты отобразятся искомые координаты. Также можно получить координаты с GPS-приемника. Если у Вас нет GPS-навигатора или Вы еще не успели посетить интересующее Вас место, можете найти подходящий трэк в интернете. Последний способ подходит для любителей геокэшинга и подобных забав, когда по космоснимкам и картам сориентироваться сложно и проще определить искомую точку на местности.

Перемещение и анимация карты
Следующий пример устанавливает центр карты в Пало Альто, ожидает 2 секунды и перемещает карту.
Метод panTo центрирует карту в указанной точке. Если эта точка находится в видимой части карты, карта перемещается плавно, а если точка на карте не видна, происходит "перескок".

var map = new GMap2();
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
window.setTimeout(function() {
map.panTo(new GLatLng(37.4569, -122.1569));
}, 2000);

Скачать movement.xml.

Добавление окна информации
Для создание информационного окна следует вызвать метод openInfoWindowHtml, передав ему в качестве аргументов позицию и отображаемый гипертекст. Нижеследующий пример показывает окно информации, привязанное к центру карты с простым сообщением "Hello World".

var map = new GMap2();
map.getCenterAsync(function(center) {
map.openInfoWindowHtml(center, "Hello World");
});

Скачать infowindow.xml.

Добавление меток
Пример отображает 10 отметок в случайных позициях на карте.

var map = new GMap2();

map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

for (var i = 0; i < 10; i++) {
var point = new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
map.addOverlay(new GMarker(point));
}
});

Скачать markers.xml.

Рисование ломаных линий (полилиний)
Предлагаемый пример рисует ломаную линию по 5-ти случайным точкам. Линия оформлена красным цветом, шириной 5 пикселов и с прозрачностью 70%.

var map = new GMap2();

map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

var points = [];
for (var i = 0; i < 5; i++) {
points.push(new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random()));
}

map.addOverlay(new GPolyline(points, "#ff0000", 5, 0.7));
});

Скачать polyline.xml.

Рисование многоугольника (полигона)
В примере рисуется многоугольник с 3-мя случайными вершинами. Обрамляющая линия красного цвета, шириной 5 пикселей, прозрачность 70%. Многоугольник закрашен синим цветом с прозрачностью 40%.

var map = new GMap2();

map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

var points = [];
for (var i = 0; i < 3; i++) {
points.push(new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random()));
}
points.push(points[0]); // Close the polygon

map.addOverlay(new GPolygon(points, "#ff0000", 5, 0.7, "#0000ff", 0.4));
});

Скачать polygon.xml.

Обработка событий
Для обработки событий используется метод GEvent.addListener. Аргументы этого метода следующие: карта, обрабатываемое событие и вызываемая при инициировании события функция. Наш пример отображает широту и долготу центра карты после перемещения карты пользователем.

var map = new GMap2();
GEvent.addListener(map, "moveend", function() {
map.getCenterAsync(function(center) {
map.openInfoWindowHtml(center, center.toString());
});
});
map.setCenter(new GLatLng(37.4419, -122.1419), 13);

Скачать events.xml.

Обработка клика
Чтобы обработать событие, происходящее при клике пользователя на карте нужно зарегистрировать обработчик события "click" в экземпляре объекта GMap2. При инициировании события его обработчик получает два аргумента: метка (если пользователь щелкнул на метке, если метки нет, то аргумент имеет значение null) и объект GLatLng с координатами точки клика на карте.
Примечание: Метки это встроенные дополнительные слои, которые поддерживают событие "click". Другие типы слоев, например, GPolyline, на клики не реагируют.
Продемонстрируем создание (если в точки клика нет метки) и удаление (если в точке клика уже есть метка) меток на карте следующим примером.

var map = new GMap2();

GEvent.addListener(map, "click", function(marker, point) {
if (marker) {
map.removeOverlay(marker);
} else {
map.addOverlay(new GMarker(point));
}
});

Скачать clicks.xml.

Отображения окна информации после клика на метке
В этом примере мы показываем собственное окно с информацией для каждого маркера после клика на нем. Мы воспользуемся отдельной функцией JavaScript для отображения собственного содержимого в окне информации для каждого маркера.

var map = new GMap2();

// Creates a marker at the given point with the given number label
function createMarker(point, number) {
var marker = new GMarker(point);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml("Marker #" + number);
});
return marker;
}

// Add 10 markers to the map at random locations
map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

for (var i = 0; i < 10; i++) {
var point = new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
map.addOverlay(createMarker(point, i + 1));
}
});

Скачать markerinfowindow.xml.

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

marker.openInfoWindowHtml("Marker #" + number, {disableGoogleLinks:true});

Скачать markerinfowindowsnolinks.xml.

Использование собственных иконок для меток
В приведенном ниже примеры мы создадим новый тип иконки, используя маркеры типа Google Ride Finder "mini". Для иконок укажем выводимое изображение, затененное изображение, позицию привязки иконки к объектам карты и размещение окна информации относительно иконки.

var map = new GMap2();

// Create our "tiny" marker icon
var tinyIcon = new GIcon();
tinyIcon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
tinyIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
tinyIcon.iconSize = new GSize(12, 20);
tinyIcon.shadowSize = new GSize(22, 20);
tinyIcon.iconAnchor = new GPoint(6, 20);
tinyIcon.infoWindowAnchor = new GPoint(5, 1);

// Add 10 markers to the map at random locations
map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

for (var i = 0; i < 10; i++) {
var point = new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
map.addOverlay(new GMarker(point, {icon:tinyIcon}));
}
});

Скачать icon.xml.

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

var map = new GMap2();

// Create a base icon for all of our markers that specifies the
// shadow, icon dimensions, etc.
var baseIcon = new GIcon();
baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
baseIcon.iconSize = new GSize(20, 34);
baseIcon.shadowSize = new GSize(37, 34);
baseIcon.iconAnchor = new GPoint(9, 34);
baseIcon.infoWindowAnchor = new GPoint(9, 2);
baseIcon.infoShadowAnchor = new GPoint(18, 25);

// Creates a marker whose info window displays the letter corresponding
// to the given index.
function createMarker(point, index) {
// Create a lettered icon for this point using our icon class
var letter = String.fromCharCode("A".charCodeAt(0) + index);
var letterIcon = new GIcon(baseIcon);
letterIcon.image = "http://www.google.com/mapfiles/marker" + letter + ".png";
return new GMarker(point, {icon:letterIcon});
}

// Add markers A - J to the map at random locations
map.getBoundsAsync(function(bounds) {
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();

for (var i = 0; i < 10; i++) {
var point = new GLatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
map.addOverlay(createMarker(point, i));
}
});

Скачать iconclass.xml.

Перетаскиваемые метки
Перетаскиваемые метки это интерактивные объекты, на которых можно кликать и перетаскивать в новое место. Приведем пример размещения перетаскиваемой метки на карте и обработки событий. Метки такого типа реагируют на три типа событий - клик (click), начало перетаскивания (dragstart) и завершение перетаскивания (dragend). По умолчанию создаваемые метки чувствительны к клику, но их нельзя перетаскивать. Для того, чтобы метку можно было перетаскивать, необходимо установить значение true для соответствующего свойства, именуемого draggable. Перетаскиваемые метки по умолчанию подвижны, это поведение можно отключить, если установить свойство bouncy равным false.

var map = new GMap2();
var center = new GLatLng(37.4419, -122.1419);
map.setCenter(center, 13);

var marker = new GMarker(center, {draggable: true, bouncy: true});
map.addOverlay(marker);

GEvent.addListener(marker, "dragstart", function() {
map.closeInfoWindow();
});

GEvent.addListener(marker, "dragend", function() {
marker.openInfoWindowHtml("Just bouncing along...");
});

Скачать draggablemarker.xml.

Получение данных с другого сайта
Для получения данных с другого сайта можно воспользоваться любым из вызовов Google Gadgets API:

  • _IG_FetchContent(url, callback) - Возвращает текстовое содержимое указанного адреса. Функция используется для работы с документами HTML.

  • _IG_FetchXMLContent(url, callback) - Возвращает XML содержимое указанного адреса и оперирует с полученными данными XML как с объектом DOM.


Предположим, Вы разместили по адресу http://mapgadgets.googlepages.com/cta_red.xml следующий файл с описанием в формате XML набора координат. Некоторые точки имеют названия, остальные безымянные.

<?xml version="1.0" encoding="UTF-8" ?>
<points>
<point lng="-87.67283499240875" lat="42.019110918045044">Howard</point>
<point lng="-87.66907453536987" lat="42.01585473134908">Jarvis</point>
<point lng="-87.66744375228882" lat="42.014483688722116" />
<point lng="-87.66716480255127" lat="42.014228607763144" />
<point lng="-87.66695022583008" lat="42.01400541108485" />
<point lng="-87.66682147979736" lat="42.01379815632509" />
<point lng="-87.66662836074829" lat="42.01357495813621" />
<point lng="-87.66645669937134" lat="42.0133198735327" />
<point lng="-87.66634941101074" lat="42.013080730787806" />
<point lng="-87.66611337661743" lat="42.01263432859157" />
<point lng="-87.6660704612732" lat="42.012283581810934" />
<point lng="-87.66602754592896" lat="42.01188500357604" />
<point lng="-87.66602754592896" lat="42.008010693009815">Morse</point>
<!-- ... -->
</points>


Воспользуемся методом _IG_FetchXmlContent() для получения и разбора XML файла, после чего нарисуем линию и добавим метки для каждой именованной точки.

var map = new GMap2();

// Go to Chicago
map.setCenter(new GLatLng(41.882853, -87.642059), 11);

// Render the red train line and stations from a remote XML file
var url = "http://mapgadgets.googlepages.com/cta_red.xml";
_IG_FetchXmlContent(url, function(response) {
var trainline = [];
var points = response.getElementsByTagName("point");

for (var i = 0; i < points.length; i++) {
var point = points.item(i);
var lat = point.getAttribute("lat");
var lng = point.getAttribute("lng");
var latlng = new GLatLng(lat, lng);

trainline.push(latlng);
if (point.firstChild) {
var station = point.firstChild.nodeValue;
var marker = createMarker(latlng, station);
map.addOverlay(marker);
}
}

var polyline = new GPolyline(trainline, "#ff0000", 3, 1);
map.addOverlay(polyline);
});

// Creates a marker at the given point with the given name
function createMarker(point, name) {
var marker = new GMarker(point);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(name);
});
return marker;
}

Скачать remotecontent.xml.

Примечание: Метод _IG_FetchXXX автоматически кэширует контент для уменьшения нагрузки на Ваши серверы. Для изменения интервала кэширования или полного отключения кэширования смотрите секцию Refreshing the Cache в документации на Gadgets API.

Этот способ работает только для "настоящих" маплетов, но мы его все равно использовать не будем, поскольку то же самое можно сделать намного проще, как будет описано ниже.

Добавление слоев KML и GeoRSS
Вместо того, чтобы добавлять точки программно на Javascript, рекомендуется сохранять данные в форматах KML или GeoRSS и оперировать ими как целыми наборами, не занимаясь отрисовкой каждой точки по отдельности. Для представления KML или GeoRSS слоев существует специальный класс GGeoXml. Объекты типа GGeoXml добавляются на карту методом addOverlay и удаляются методом removeOverlay.

В нижеследующем примере мы отобразим на карте список точек из файла http://mapgadgets.googlepages.com/cta.kml. Заметьте, насколько этот способ проще, чем предыдущий пример, когда мы делали это вручную с помощью функции _IG_FetchXmlContent.

var map = new GMap2();
var geoXml = new GGeoXml("http://mapgadgets.googlepages.com/cta.kml");

map.setCenter(new GLatLng(41.882853, -87.642059), 11);
map.addOverlay(geoXml);

Скачать ggeoxml.xml.

Геокодирование
Для выполнения операции геокодирования на языке JavaScript доступен объект типа GClientGeocoder. Метод getLatLng применяется для трансляции адреса в объект GLatLng. Геокодирование требует отправки специального запроса на сервер Google, что требует некоторого времени. Для предотвращения зависания приложения на время отправки запроса применяется функция обратного вызова, которая будет выполнена после получения результата запроса. В примере ниже мы геокодируем адрес, добавляем метку в нужную точку и отображаем информационное окно с адресом.

var map = new GMap2();
var geocoder = new GClientGeocoder();
showAddress("76 9th ave new york");

function showAddress(address) {
geocoder.getLatLngAsync(
address,
function(latlng) {
if (!latlng) {
alert(address + " not found");
} else {
map.setCenter(latlng, 15);
var marker = new GMarker(latlng);
map.addOverlay(marker);
marker.openInfoWindowHtml(address);
}
}
);
}

Скачать geocoding.xml.

Для получения информации о прямом доступе к геокодеру смотрите секцию Geocoding HTTP Request в стандартном Google Maps API.

Сохранение настроек пользователя
Метод setprefs из Google Gadgets API позволяет сохранять настройки пользователя в течении сеанса работы и между сеансами. Чтобы эта возможность была доступна, необходимо включить в маплет тэг <Require feature="setprefs"/> внутри тэга <ModulePrefs> для того, чтобы была загружена библиотека работы с пользовательским настройками (setprefs library).
Программно сохранить нужное значение можно с помощью тэга
<UserPref>. Можно запретить пользователю редактировать это значение, установив его тип равным "hidden".
Создание объекта, содержащего методы установки и получения значений настроек пользователя, производится вызовом конструктора _IG_Prefs(__MODULE_ID__).
Подробная информация о способах управления настройками пользователя приведена в секции Working with Userpref Data Types в документации Google Gadgets API.
Приведем простой пример, который отображает кнопки для увеличения и сброса значения счетчика. Если Вы перезагрузите маплет, то убедитесь, что значение счетчика сохранилось.

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs
title="Set Userprefs Demo">
<Require feature="sharedmap" />
<Require feature="setprefs" />
</ModulePrefs>
<UserPref
name="counter"
default_value="0"
datatype="hidden"/>
<Content type="html">
<![CDATA[
<input type=button value="Increment Counter" onClick="incrementCounter();">
<input type=button value="Reset Counter" onClick="resetCounter();">
<div id="message" style="margin-top:1em"></span>

<script>
// Get user preferences
var prefs = new _IG_Prefs(__MODULE_ID__);
var message = document.getElementById("message");
showCounter();

// Show the current value of the counter
function showCounter() {
var counter = prefs.getInt("counter");
message.innerHTML = "The counter value is " + counter;
}

// Increment value of "counter" user preference
function incrementCounter() {
var counter = prefs.getInt("counter");
prefs.set("counter", counter + 1);
showCounter();
}

// Reset value of "counter" userpref to 0
function resetCounter(){
prefs.set("counter", 0);
showCounter();
}
</script>
]]>
</Content>
</Module>

Скачать setprefs.xml.

Следующая статья научит нас правильно создавать "настоящие" маплеты и расскажет об отличиях таких маплетов от стандартного использования Google Maps API. Читаем Google Mapplets: Отличие "настоящих" маплетов от стандартного Maps API.

4 комментария:

walks-ru комментирует...

Алексей!
Подскажите, Пожалуйста!!
"Вы можете вставить его в XML описание маплета и опубликовать в Google или вставить в обычную html страницу и использовать на своем сайте."

не могу сообразить, а как собственно вставлять куски XML/JavaScript (а именно remotecontent.xml) в обычную html... ( http://walks.ru/hlv/tt_w.html )

Печников Алексей комментирует...
Этот комментарий был удален автором.
walks-ru комментирует...

> basic3.html
это я и в прошлый раз пробовал, но мое непонимание проблемы осталось на прежнем уровне, и пример этому тут:
http://walks.ru/hlv/tt_remote.html

мое знание JS на минимальном уровне и правильно оформить функции представленные в .xml виде - в .html у меня не получается
Покажите на примере

Olegi4 комментирует...

Если нет никаких высоких требований, а задача достаточно проста, то можно использовать сервисы типа http://mapspread.com/ , http://simplegeocoder.blogspot.com/


(C) Alexey Pechnikov aka MBG, mobigroup.ru