В рамках большого интерактивного веб-ориентированного проекта (подробнее о котором возможно в другом посте) я занимаюсь разработкой картографического движка, реализованного на HTML5 CANVAS. Его разработка дошла до стадии беты и, с одобрения моего руководства, появилось желание продемонстрировать данные карты широкой публике.

image

Общие сведения


Движок разрабатывался без использования каких-либо специализированных библиотек или фреймворков. Единственная используемая библиотека – jQuery.

Изображения карт – тайлы – сгенерированы с помощью нашей утилиты. Тут еще есть к чему стремиться, так как их оптимизацией мы еще не занимались.

Все отрисовывается на CANVAS’e, за исключением таких элементов как панель дополнительных инструментов и popup’ов меток (хотя в демо по ссылке ниже их все равно нет).

Реализация


Реализация модульная и состоит из следующих основных частей, назначение которых думаю понятно из их названий: CanvasDragger, CanvasEventer, CanvasImgLoader, CanvasMapper, CanvasMarker, CanvasMiniMapper, CanvasResizer, CanvasTools, CanvasZoomer.

Для того чтобы подключить карты достаточно в нужном месте html’a написать следующую строчку:
<canvas id="map2d"></canvas>

Далее в коде JS производим инициализацию (как пример):
$(function() {
    mWrap = new MapsWrapper({
        mapDivId: "map2d" // тут указываем ID canvas’a, в котором будет рисоваться карта
    });
});

MapsWrapper = function(properties) {
    this.initialize(properties);
};
$.extend(MapsWrapper.prototype, {
    v2DMapDiv                : null,
    v2DMapComponent   : null,

    initialize: function(prop){
        this.v2DMapDiv = prop.mapDivId;
        this.initMap();
    },

    initMap: function(){
        var GlobalParams = {
            staticMapUrl: ["http://gate.looxity.ru:8088/map.html", "http://zain.looxity.ru:8088/map.html", "http://kaph.looxity.ru:8088/map.html"],
            initCrd     : {x: 7445, y: 9925},
            initZoom    : 0.25,
            zoomList    : [1, 0.5, 0.25, 0.1, 0.05, 0.025],
            miniMap     : true,
            tools       : {scaler: true, polygoner: true}
        };
        this.v2DMapComponent = new CanvasMapper (this.v2DMapDiv);
        this.v2DMapComponent.initialize(GlobalParams);
    }
});


Остановимся поподробнее на параметрах:
  • staticMapUrl – хосты, с которых подгружаются тайлы карты
  • initCrd – начальные координаты в проекции Гаусса-Крюгера, в данном случае примерно соответствуют нулевому километру автодорог, что рядом с Манежной площадью.
  • miniMap – подключение модуля миникарты
  • tools – подключение модуля дополнительных инструментов

Внутренняя механика


Или что скрывается за тем или иным действием пользователя. Пройдемся по основным событиям.

Стартуем

При инициализ��ции карт рассчитывается количество тайлов, которое нужно показать, чтобы полностью покрыть canvas. Зная размеры canvas’a, при заданном размере тайлов в 256х256, проделываем данную операцию.

Двигаемся

Далее когда происходит движение карты – dragg – проверяем ситуацию если мы передвинули карту на такое расстояние, что нужно подгрузить новый тайл. Так же проверяем все ли тайлы находятся в области видимости, если нет, то запускается «сборщик мусора»:
unVisibleTilesCollector: function() {
        for(var cnt = 0; cnt < this.__TILES__.length; cnt++) {
            if( (this.__TILES__[cnt].canvX + this.tileSize) < 0
                || this.__TILES__[cnt].canvX > this.canvas.width
                || this.__TILES__[cnt].canvY > this.canvas.height
                || (this.__TILES__[cnt].canvY + this.tileSize) < 0
                ) {
                this.__TILES__.splice(cnt, 1);
                cnt--;
            }
        }
    }


Масштабируем (zoomIn, zoomOut)

При срабатывании события “mousewheel” последовательно происходят следующие основные действия:
  • копируется текущее положение всех тайлов
$.extend(this.__ANIM_TILES__, this.mapper.__TILES__)

  • средствами canvas’a и с помощью математики происходит уменьшение или увеличение тайлов (в зависимости от того как мы крутим колесико мыши) из копии, сделанной в пп1
for(cnt; cnt < this.animSteps; cnt++){
	            setTimeout(function(){
	                _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height);
	               _this.ctxMarker.clearRect(0,0,_this.canvas.width,_this.canvas.height);
	                animScale += scale*stepScale;
	                _this.drawAllAnimTiles(evt, {
	                    animScale: animScale,
	                    stepCurrNum: Math.round(Math.abs(animScaleStart-animScale)/stepScale),
	                    stepScale: stepScale
	                });
	             }, delay*cnt);
	        }

  • сверху, по мере подгрузки, накладываются новые тайлы, в соответствии с новым масштабом
MapsWrapper.v2DMapComponent.update()


Работа в браузерах


Работа проверялась в FireFox, Chrome, Safari, Opera и IE последних версий.
Для тех кто все еще не в курсе лишний раз подчеркну следующее. Так как используется canvas, автоматически отпадают все браузеры, не поддерживающие данную технологию, а это — IE версии 8 и ниже и совсем уж старые версии вышеперечисленных браузеров.

TODO List по картам


1. Уменьшение размеров тайлов карты (должно дать ощутимый прирост скорости работы);
2. Слайдер изменения масштаба;
3. Инструмент получения информации по точке на карте (адрес здания, координаты и тп);
4. ???

Демо: share.arkada-sw.ru/canvasmap

ps все права на программный код и карты принадлежат компании, в которой я работаю
pps если данная статья возымеет интерес, то следующим моим постом будет описание примера реального использования данных карт