Генерация слоя активных областей из YMapsML

Приложение, предложенное разработчиком twirl, позволяет из YMapsML-файла получить набор тайлов, необходимых для создания слоя активных областей.

Скачать приложение можно по адресу: http://github.com/twirl/hsTiler.

Подготовка YMapsML-документа

Допустим, у нас есть список точек разных типов, которые мы хотим показать на карте в виде значка, используя технологию активных областей. Для примера возьмем список городов-героев:

[
    { "name": "Ленинград", "location": [30.313497,59.938531] },
    { "name": "Сталинград", "location": [44.514208,48.708898] },
    { "name": "Севастополь", "location": [33.558616,44.585098] },
    { "name": "Одесса", "location": [30.712384,46.468691] },
    { "name": "Киев", "location": [30.531113,50.402398] },
    { "name": "Москва", "location": [37.609218,55.753559] },
    { "name": "Брестская крепость", "location": [23.654573,52.086152] },
    { "name": "Новороссийск", "location": [37.7767,44.720479] },
    { "name": "Керчь", "location": [36.464342,45.30667] },
    { "name": "Минск", "location": [27.567338,53.898087] },
    { "name": "Тула", "location": [37.617384,54.193212] },
    { "name": "Мурманск", "location": [33.077819,68.963322] },
    { "name": "Смоленск", "location": [31.999095,54.7798] }
]

Сформируем по этому списку YMapsML-документ (см. описание формата YMapsML):

<?xml version="1.0" encoding="UTF-8"?>
<ymaps xmlns="http://maps.yandex.ru/ymaps/1.x" xmlns:gml="http://www.opengis.net/gml">
     <Representation xmlns="http://maps.yandex.ru/representation/1.x"></Representation>

    <GeoObjectCollection>
        <gml:featureMembers>
            <GeoObject>
                <gml:name>Ленинград</gml:name>
                <gml:Point>
                    <gml:pos>30.313497 59.938531</gml:pos>
                </gml:Point>
            </GeoObject>
            <GeoObject>
                <gml:name>Сталинград</gml:name>
                <gml:Point>
                    <gml:pos>44.514208 48.708898</gml:pos>
                </gml:Point>
            </GeoObject>
            <!-- Перечисление остальных 10 городов опущено -->
        </gml:featureMembers>
    </GeoObjectCollection>
</ymaps>

Зададим значок, которым будут отмечены города. Для этого опишем в теге Representation стиль метки:

<Representation xmlns="http://maps.yandex.ru/representation/1.x">
    <Style gml:id="hero">
        <iconStyle>
            <href>icons/hero.png</href>
            <size x="27" y="26"/>
            <offset x="-11" y="-28"/>
        </iconStyle>
    </Style>
</Representation>

А для объекта GeoObjectCollection укажем стиль коллекции:

<GeoObjectCollection>
    <style>#hero</style>
    <gml:featureMembers>
        <!-- Перечисление городов опущено -->
    </gml:featureMembers>
</GeoObjectCollection>

Коллекции могут быть вложенными, то есть возможно задать разные значки группам объектов. Например, выделим Брестскую крепость в отдельную коллекцию и назначим этой коллекции друой значок:

<?xml version="1.0" encoding="UTF-8"?>
<ymaps xmlns="http://maps.yandex.ru/ymaps/1.x" xmlns:gml="http://www.opengis.net/gml">
     <Representation xmlns="http://maps.yandex.ru/representation/1.x">
    <Style gml:id="hero">
        <iconStyle>
            <href>icons/hero.png</href>
            <size x="27" y="26"/>
            <offset x="-11" y="-28"/>
        </iconStyle>
    </Style>
    <Style gml:id="hero-keep">
        <iconStyle>
            <href>icons/hero-keep.png</href>
            <size x="14" y="13"/>
            <offset x="-5" y="-14"/>
        </iconStyle>
    </Style>
     </Representation>

    <GeoObjectCollection>
        <gml:featureMembers>
            <GeoObjectCollection>
                <style>#hero</style>
                <gml:featureMembers>
                    <GeoObject>
                        <gml:name>Ленинград</gml:name>
                        <gml:Point>
                            <gml:pos>30.313497 59.938531</gml:pos>
                        </gml:Point>
                    </GeoObject>
                    <!-- Перечисление остальных 10 городов опущено -->
                </gml:featureMembers>
           </GeoObjectCollection>
           <GeoObjectCollection>
                <style>#hero-keep</style>
                <gml:featureMembers>
                    <GeoObject>
                      <gml:name>Брестская крепость</gml:name>
                      <gml:Point>
                          <gml:pos>23.654573 52.086152</gml:pos>
                      </gml:Point>
                  </GeoObject>
                </gml:featureMembers>
           </GeoObjectCollection>
        </gml:featureMembers>
    </GeoObjectCollection>
</ymaps>

Стиль при необходимости можно назначить и отдельным геообъектам. Объектам также можно назначить приоритет (z-index). Для этого в теге GeoObject необходимо указать его численное значение:

<gml:metaDataProperty>
    <AnyMetaData>
        <priority>10</priority>
    </AnyMetaData>
</gml:metaDataProperty>

В теге AnyMetaData можно указывать любые данные, они будут преобразованы в JSON и доступны из JavaScript.

Подготовка слоев тайлов

Специальный скрипт (см. http://github.com/twirl/hsTiler) позволяет по YMapsML-документу подготовить два слоя тайлов – с картинками и js-описаниями. Для его использования необходимо:

  1. Выложить скрипты на сервер.
  2. В файле index.php указать:
    1. путь к YMapsML-файлу;
    2. путь для сохранения файлов;
    3. шаблоны имени файлов;
    4. диапазон масштабов
  3. Запустить index.php из браузера или из командной строки.

Если на сервере разрешен allow_url_fopen, то во всех путях можно указывать URL.

Скрипт подготовит два слоя тайлов, которые после этого нужно выложить в публичный доступ по http.

Отображение слоев активных областей на карте

При подготовке тайлов автоматически создается html-файл с примером подключения слоя активных областей. Этот код выглядит примерно так:

// Вспомогательная функция обработки шаблонов
// Обрабатывает подстановки вида %x, %y, %z
function processTemplate (template, tile, zoom) {
    return template.replace(/%x/ig, tile.getX()).replace(/%y/ig, tile.getY()).replace(/%z/ig, zoom);
}

// Источник тайлов с изображениями
var ds = new YMaps.TileDataSource('шаблон имени png-тайла', 1, 0);
ds.getTileUrl = function (tileNumber, zoom) {
    return processTemplate(
        YMaps.TileDataSource.prototype.getTileUrl.call(this, tileNumber, zoom),
        tileNumber,
        zoom
    );
};
// Добавляет "картиночный" слой на карту
map.addLayer(new YMaps.Layer(ds));

// Источник тайлов с описаниями активных областей
var os = new YMaps.Hotspots.ObjectSource('шаблон имени js-тайла', 'шаблон ключа');
os.getTileUrl = function (tileNumber, zoom) {
    return processTemplate(
        YMaps.Hotspots.ObjectSource.prototype.getTileUrl.call(this, tileNumber, zoom),
        tileNumber,
        zoom
    );
};
os.getKey = function (tileNumber, zoom) {
    return processTemplate(
        YMaps.Hotspots.ObjectSource.prototype.getKey.call(this, tileNumber, zoom),
        tileNumber,
        zoom
    );
};
// Добавляет слой активных областей на карту
var hLayer = new YMaps.Hotspots.Layer(os);
map.addLayer(hLayer);

Слой активных областей обсчитывает действия пользователя и генерирует следующие события: MouseEnter, MouseMove, MouseLeave, MouseUp, MouseDown, Click, DblClick. В обработчик каждого из этих событий приходит следующая информация:

  1. Ссылка на сам слой.
  2. Геообъект на котором произошло событие.
  3. Событие мыши – экземпляр класса YMaps.MouseEvent.
  4. Геометрическая фигура, над которой произошло событие (в нашем случае – пиксельные координаты значка на текущем масштабе).

Обрабатывать события можно стандартными средствами API Яндекс.Карт (см. События).

YMaps.Events.observe(hLayer, hLayer.Events.Click, function () {
    alert('Щёлк!');
});

По умолчанию слой активных областей ведет себя следующим образом:

  1. При наведении курсора на значок меняет тип курсора на pointer.
  2. При наведении на значок показывает всплывающую подсказку. По умолчанию текст всплывающей подсказки берется из тега <name> соответствующего GeoObject.
  3. При щелчке мыши по значку показывает балун (хвостик балуна находится в точке щелчка). Текст балуна по умолчанию следующий: <h3>$[name]</h3><p>$[description]</p>, где $[name] и $[description] берутся из тегов <name> и <description> соответствующего геообъекта.

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

Для этого потребуется:

  1. Определить пользовательский тип объекта-активной области. Каждому GeoObject из YMapsML в JavaScript соответствует экземпляр класса YMaps.Hotspots.Object. Унаследуемся от этого класса:
    // Функция наследования
    var extend = function (child, parent) {
                var c = function () {};
                c.prototype = parent.prototype;
                c.prototype.constructor = parent;
                return child.prototype = new c;
            };
            
    var myObject = function () {
       YMaps.Hotspots.Object.apply(this, arguments);
    }
    extend (myObject, YMaps.Hotspots.Object);
  2. Определить функцию requestBalloonData в пользовательском классе myObject
    myObject.prototype.requestBalloonData = function (callback) {
       // Функции callback требуется передать текст балуна и точку его привязки к карте
       // Данные геообъекта доступны через функцию getData()
       var data = '<iframe style="width: 850px; height: 550px;" src="http://ru.wikipedia.org/wiki/' + this.getData().name + '"/>',
           parts = this.getData().Point.pos.split(' '),
           position = new YMaps.GeoPoint(parts[0], parts[1]);
       // Передает функции-обработчику содержимое балуна и точку его привязки
       callback(data, position);
    }
  3. Указать источнику данных пользовательский класс объектов вместо стандартного:
    // os - экземпляр класса YMaps.Hotspots.ObjectSource
    os.createObject = function (layer, data, shapes, priority) {
       return new myObject(layer, data, shapes, priority);
    };