Как стать автором
Обновить

OpenLayers или делаем сервис мониторинга транспорта

Время на прочтение8 мин
Количество просмотров41K
Сейчас на рынке много предложений по продаже мобильных устройств, предназначенных для контроля движущихся объектов или трекеров. В большинстве из них есть функция передачи информации по GPRS на любой заданный веб-адрес через определенный интервал времени. Чаще всего формат передачи данных разный. Поэтому мы не будем рассматривать вопрос загрузки данных с трекера в базу. Предположим данные есть и мы хотим приступить к созданию сервиса мониторинга транспорта. Основу такой системы образуют возможности:
-выбор карты и ее отображение
-отображение точки или картинки и подписи к ней
-отображение полигона и его редактирование
-отображение линии и ее редактирование
-отображение информации связной с полигонами, линиями, точками (всплывающие подсказки)
-немного математики (подсчет пройденного пути, площадь полигона, принадлежность точки полигону)
Все эти функции легко реализовать с помощью OpenLayers, библиотеки на JavaScript.


Выбор карты и ее отображение
Для этого на вашей страничке нужно создать элемент div c нужными размерами. В нем и будет отображаться карта. Пример:
 div id="map" style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"

Подключить OpenLayers:
script type="text/javascript" src="Scripts/OpenLayers.js"

Создать глобальную переменную map, в которой будет храниться объект карты. Создать вызов функции на событие загрузки страницы, в которой будет код непосредственного создания карты. Пример:
body onload="LoadMap()"

Описать функцию LoadMap:
//Границы задаются для проекции EPSG:900913. 
var maxExtent = new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508);
var restrictedExtent = maxExtent.clone();
var maxResolution = 156543.0339;
//Отображаться карта будет в проекции EPSG:4326. 
var options = {
   projection: new OpenLayers.Projection("EPSG:900913"),
   displayProjection: new OpenLayers.Projection("EPSG:4326"),
   numZoomLevels: 18,
   maxResolution: maxResolution,
   maxExtent: maxExtent,
   restrictedExtent: restrictedExtent
};
//Создаем карту
map = new OpenLayers.Map('map', options);

// Создаем слои-карты VirtualEarth
var veroad = new OpenLayers.Layer.VirtualEarth("Virtual Earth Roads", { 'type': VEMapStyle.Road, sphericalMercator: true });
var veaer = new OpenLayers.Layer.VirtualEarth("Virtual Earth Aerial", { 'type': VEMapStyle.Aerial, hericalMercator: true });
var vehyb = new OpenLayers.Layer.VirtualEarth("Virtual Earth Hybrid", { 'type': VEMapStyle.Hybrid, sphericalMercator: true });
// Создаем слои-карты OpenStreet 
var mapnik = new OpenLayers.Layer.OSM();
//Добавляем подсказку масштаба карты и позици курсора
map.addControl(new OpenLayers.Control.ScaleLine());
map.addControl(new OpenLayers.Control.MousePosition());
//Складываем созданные слои в объект OpenLayers.Map
map.addLayers([ mapnik, veroad, veaer, vehyb]);
//Добавляем панель управления слоями 
map.addControl(new OpenLayers.Control.LayerSwitcher());
//Создаем точку на которую будет центрироваться карта при старте
var point0 = new OpenLayers.Geometry.Point(37.600328, 55.574624);
//Помним что карта отображается в одной проекции, а с данными работает в другой проекции. 
point0.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
//Выполняем центрирование карты на точку с масштабом 9. По умолчанию их 15.   
map.setCenter(new OpenLayers.LonLat(point0.x, point0.y), 9);
        }

На сайте проекта openlayers можно найти много примеров, около двухсот. Пример визуализации карты из заданного сервиса, можно увидеть здесь. Большинство решаемых задач в статье эти примеры покрывают. В представленном коде отсутствует присоединение карт google и yandex, для работы этих карт нужны ключи. Вы должны зарегистрироваться, получить ключ доступа к сервису и только после этого можно подсоединяться к этим сервисам. Результат кода выше:
1
Отображение точки или картинки и подписи к ней
Создаем слои, которые будет хранить наши объекты(точки и картинки).
//Создаем объект стиля отображения картинок. Указываем параметры ширены, высоты, сдвига по вертикале относительно точки, подпись(${} - это объявление параметра, можно выводить константу для всех точек), url к графику, размер шрифта. 
var styleImage = new OpenLayers.Style(
   {
      graphicWidth: 21,  
      graphicHeight: 25,  
      graphicYOffset: -28, 
      label: "${label}",   
      externalGraphic: "http://www.openlayers.org/dev/img/marker.png",   
      fontSize: 12 
   });
//labelYOffset - сдвиг текста по вертикале относительно точки
var stylePoint = new OpenLayers.Style(
   { 
      pointRadius: 5,
      strokeColor: "red",
      strokeWidth: 2,
      fillColor: "lime",
      labelYOffset: -16,
      label: "${label}",
      fontSize: 16 
   });
//Создаем слой для точек. В свойстве styleMap указываем как отображать в обычном случае. Свойство select будет применено после выбора элемента. 
var vectorPoint = new OpenLayers.Layer.Vector("Точки",
{
        styleMap: new OpenLayers.StyleMap(
        { "default": stylePoint,
          "select": { pointRadius: 20}
        })
    });
//В отличие от слоя с точками, где выделение объекта взывает увеличение радиуса точки, на слое с картинками будет их поворот на 45 градусов

var vectorImage = new OpenLayers.Layer.Vector("Картинки",
{
        styleMap: new OpenLayers.StyleMap(
        { "default": styleImage,
          "select": { rotation: 45}
        })
    });
//Складываем слои на карту
 map.addLayer(vectorImage );
 map.addLayer(vectorPoint );

Слои созданы, создаем функции которые будут работать со слоями.
//Функция добавляет изображение с подписью. Аргументы: долгота, широта, подпись, уникальный идентификатор, слой. В случае, если объект уже есть функция сдвигает его в новое положение. 
function addImg(lon,lat,title,ident, layr){
var ttt = new OpenLayers.LonLat(parseFloat(lon), parseFloat(lat));
ttt.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
// features-массив объектов на карте. Объект может быть например: точкой, полигоном, кривой, изображением
for (var k = 0; k < layr.features.length; k++) 
{
// У объектов features есть атрибуты, как предопределенные так и добавленные пользователем
if(layr.features[k].attributes.ImgId==ident) {
// move - функция перемещения объекта в данную точку
		layr.features[k].move(ttt);
		layr.features[k].attributes.label=title;
		return false;

	}
}
var point0 = new OpenLayers.Geometry.Point(parseFloat(lon), parseFloat(lat));
point0.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
//Для создание объекта Feature использовался класс OpenLayers.Feature.Vector
layr.addFeatures(new OpenLayers.Feature.Vector(point0, { label: title, name: title, ImgId: ident }));              
}
function addPoint(lon,lat,title,ident){
var ttt = new OpenLayers.LonLat(parseFloat(lon), parseFloat(lat));
ttt.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
for (var k = 0; k < map.layers[5].features.length; k++) 
{
if(map.layers[5].features[k].attributes.PointId==ident) {
		map.layers[5].features[k].move(ttt);
		map.layers[5].features[k].attributes.label=title;
		return false;

	}
}
var point0 = new OpenLayers.Geometry.Point(parseFloat(lon), parseFloat(lat));
point0.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
map.layers[5].addFeatures(new OpenLayers.Feature.Vector(point0, { label: title, name: title, PointId: ident }));
}
</code>
Теперь осталось выполнить добавление точек и изображений:
<code>
 addPoint(37,55.5,'Точка 1','1',map.layers[4]);
 addPoint(37,55.1,'Точка 2','2',map.layers[4]);
  addImg(37,55.6,'Изображение 1','2',map.layers[5]);
   addImg(37,55.9,'Изображение 2','2',map.layers[5]);

Результат:
1
Добавилось только одно изображение потому, что вызов функции был с одинаковыми идентификаторами изображений. Объект сместился, название было изменено на новое.
Отображение полигона и его редактирование
Функция создания полигона:
//Предполагаемый форма данных: координаты разделены точкой с запятой, долгота с широтой разделены пробелом
function addPoly(data,title,ident,layr){
var featuress = Array();
var coords = data.split(';');
for (var i = 0; i < coords.length; i++) {
                var d = coords[i].split(' ');
                var ttt = new OpenLayers.LonLat(parseFloat(d[0]), parseFloat(d[1]));
                var point0 = new OpenLayers.Geometry.Point(ttt.lon, ttt.lat);
                point0.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
                featuress.push(point0);
            }
//Массив точек порождает линию, из нее создается полигон
            var linearRing2 = new OpenLayers.Geometry.LinearRing(featuress);
            var polygonFeature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([linearRing2]), { label: title, PolyID: ident });
            layr.addFeatures(polygonFeature2);
	}

Пример вызова функции прорисовки полигона:
addPoly('37 55.9;37.5 55.4;37 55','Полигон','1',map.layers[5]); 

Редактирование в OpenLayrs осуществляется с помощью OpenLayers.Control.ModifyFeature. Нужно создать экземпляр этого класса указав слой объекты которого нужно модифицировать:
var modef = new OpenLayers.Control.ModifyFeature(map.layers[5])
modef.clickout = false;
modef.toggle = false;
modef.standalone = true;
map.addControls([ modef]);
modef.activate();
//Выбираем созданный полигон  
modef.selectFeature(map.layers[5].features[2]);
//Получаем центр полигона
var centr = map.layers[5].features[2].geometry.getCentroid()
//Карту центрируем по полученной точке
map.setCenter(new OpenLayers.LonLat(centr.x, centr.y));

Каждая вершина полигона в момент редактирования обозначается точкой и в силу того, что полигон мы разместили на слое для точек, к ним был применен стиль этого слоя. Результат: подписи «undefined» у вершин полигона. Это приме того, что для каждого типа объектов лучше создавать отдельные слои. Пример отображения режима редактирования:
3
Выход из режима редактирования:
modef.deactivate();

Отображение линии и ее редактирование
Как было уже сказано, полигон создается из линии, таким образом в функции AddPoly нужно изменить одну строку:
 layr.addFeatures(polygonFeature2);

на
layr.addFeatures(linearRing2 );

Процесс редактирование ничем не отличается.
Всплывающие подсказки
Для реализации всплывающей подсказки нужно добавить на карту обработчик событий, который представляет собой экземпляр класса: OpenLayers.Control.SelectFeature.
var selectControl = new OpenLayers.Control.SelectFeature([map.layers[5]]);
 map.addControls([selectControl],
                {
                    clickout: true, toggle: false,
                    multiple: false, hover: true,
                    toggleKey: "ctrlKey", 
                    multipleKey: "shiftKey" 
                });
selectControl.activate();

После этого будет происходить обработка нажатий на объекты слоев. Точки будут увеличиваться в размере, картинки поворачиваться на 45 градусов. Создаем обработчик события выделения точки:
//Событие on - это событие выделения   
map.layers[5].events.on( {
 "featureselected": function (e) {
       var HTMLcontent;
       var point
//Здесь можно генерировать любой контент
       HTMLcontent = 'table style="width: 100%;" tr td Информация об объекте td tr table ';
//getCentroid() - получить центр фигуры, в данном случае лишнее, но это унифицированный способ получения места всплытия подсказки
       point = new OpenLayers.LonLat(e.feature.geometry.getCentroid().x, e.feature.geometry.getCentroid().y);
//OpenLayers.Popup.AnchoredBubble - всплывающий прямоугольник, есть другие варианты в OpenLayers.Popup
       var popup = new OpenLayers.Popup.AnchoredBubble("SDVegetationInfo",
           point,
           new OpenLayers.Size(100, 100), 
           HTMLcontent,                   
           null,
           false);
       popup.opacity = 0.9;
       popup.autoSize = true;
       popup.setBackgroundColor("#bcd2bb");
//добавление на карту 
       map.addPopup(popup, true);
      }
// когда выделение убрано, через секунду окно погаснет
      , "featureunselected": function (e) {
           setTimeout('if(map.popups.length - 1>-1){map.removePopup(map.popups[map.popups.length - 1]);}', 1000);
        }
});

Подсчет пройденного пути, площадь полигона, принадлежность точки полигону
Подсчет пройденного пути
map.layers[4].features[2].feature.geometry.getLength()

площадь полигона
map.layers[4].features[2].feature.geometry.getArea()

принадлежность точки полигону
map.layers[4].features[2].containsPoint(map.layers[4].features[0])

Результат всех предыдущих действий можно увидеть на странице: ссылка

В заключение отмечу, что многое компании уже сделали свои трекеры на OpenLayers это дешево и просто. Документацию по OpenLayers можно найти тут.
Теги:
Хабы:
Всего голосов 25: ↑23 и ↓2+21
Комментарии16

Публикации