Создаем редактор Яндекс.Карт Часть-1

С этого поста я начинаю цикл заметок, посвященных созданию редактора Яндекс.Карт.

Определимся для начала с основными функциями нашего редактора.

Весь код будет разделен на три логических группы:

— добавление объектов на карту (метки, линии, многоугольники);

— редактирование,  добавленных объектов (геометрия, цвет, параметры, удаление с карты);

— сохранение всех объектов в базе данных, с возможностью вывода на карту.

Теперь подробнее опишу каждую часть.

1. Добавление объектов на карту.

В данной части предусмотрим следующие возможности:

— добавление меток на карту, выбор стиля значка для обозначения из стандартного набора http://api.yandex.ru/maps/jsapi/doc/ref/reference/styles.xml, ввод названия и описания для добавляемой метки;
— добавление ломаной линии с возможностью задания толщины и цвета, редактировании геометрии при добавлении;
— добавление многоугольников с параметрами – толщина линии обводки, цвет обводки и цвет заливки многоугольника, редактирование геометрии при добавлении.

2. Редактирование

— для меток – перемещение в новое место на карте, замена значка, изменение названия и описания, удаление с карты;
— для ломаной линии – изменение геометрии, толщины и цвета, удаление;
— для многоугольника – изменение геометрии, толщины линии обводки, цвета обводки и заливки многоугольника, удаление. 

3. Сохранение в базу данных и вывод на карту.

Сохранять все параметры и геометрию добавленных объектов в базе данных MySQL, вывод сохраненных данных на карту или в файл формата YMapsML.

И так после определения основных функций редактора Яндекс.Карт начнем его реализацию.

Создадим первую часть – добавление объектов на карту.

В качестве исходного кода возьмем обычную карту с элементами управления:

тулбар (класс YMaps.ToolBar) – панель инструментов с кнопками, позволяющими перемещать карту, увеличивать ее, а также измерять расстояние на карте с помощью специальной линейки;

элемент масштабирования (класс YMaps.Zoom) – позволяет менять разрешение карты с определенным шагом;

переключатель типа карты (класс YMaps.TypeControl) – кнопки "Карта", "Спутник", "Гибрид";

поиск по карте (класс YMaps.SearchControl) – позволяет искать географические объекты по их названию.

Исходный код: ymap-editor-1.html

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

Вид кнопок – переключатель, кнопка остается нажатой до тех пор, пока не будет нажат другой переключатель из группы кнопок, в которую он входит.

Группа стандартных кнопок тулбара имеет ID YMaps.ToolBar.DEFAULT_GROUP.

Для создания переключателя используется стандартный конструктор класса YMaps.ToolBarRadioButton.

В результате мы получаем следующий код: ymap-editor-2.html

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

При активации кнопки справа от карты будет появляться форма для ввода необходимых параметров.

Для меток это будут события pointBootion.Events.Select и pointBootion.Events.Deselect.

Код для события pointBootion.Events.Select

// При активной кнопке включаем добавление меток
YMaps.Events.observe(pointBootion, pointBootion.Events.Select, function () {
YMaps.Events.observe(map, map.Events.Click, function (map, mEvent) {
var newGeoPoint = mEvent.getGeoPoint(); 
YMaps.jQuery("#image").change(function() {
YMaps.jQuery("#imagePreview").empty();
if ( YMaps.jQuery("#image").val()!="" ){
YMaps.jQuery("#imagePreview").append("<img src="" + YMaps.jQuery("#image").val() + "" />");
}
else{
YMaps.jQuery("#imagePreview").append("displays image here");
}
});
YMaps.jQuery("#formpoint").show();
document.getElementById('point_lat').value = newGeoPoint.getLng();
document.getElementById('point_lng').value = newGeoPoint.getLat(); 
})
})

При клике по кнопке pointBootion происходит событие ointBootion.Events.Select, необходимо щелкнуть левой кнопкой мыши по карте в месте, где будет располагаться метка.

При этом появляется html-форма для ввода параметров метки:

<div id="formpoint" style="display: none">
<p><strong>Форма ввода параметров метки</strong></p>
Выбирете значек для метки:<br /> 
 
<div style="width: 250px;">
 
<div id="imagePreview" style="float:left; width:30px;"></div>
 
<div style="margin-left: 40px; width:210px;">
<select name="image" id="image" class="inputbox" size="1">
<option value=""> - Select Image - </option><option value="http://api-maps.yandex.ru/i/0.4/micro/pmwts.png">default#whitePoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmgns.png">default#greenPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmrds.png">default#redPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmyws.png">default#yellowPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmdbs.png">default#darkbluePoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmnts.png">default#nightPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmgrs.png">default#greyPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmbls.png">default#bluePoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmors.png">default#orangePoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmdos.png">default#darkorangePoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmpns.png">default#pinkPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/micro/pmvvs.png">default#violetPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmwts.png">default#whiteSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmgns.png">default#greenSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmrds.png">default#redSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmyws.png">default#yellowSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmdbs.png">default#darkblueSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmnts.png">default#nightSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmgrs.png">default#greySmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmbls.png ">default#blueSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmors.png ">default#orangeSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmdos.png ">default#darkorangeSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmpns.png ">default#pinkSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/placemarks/pmvvs.png ">default#violetSmallPoint</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/airplane.png ">default#airplaneIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/anchor.png ">default#anchorIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/arrowDownLeft.png ">default#arrowDownLeftIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/arrowDownRight.png ">default#arrowDownRightIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/arrowLeft.png ">default#arrowLeftIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/arrowRight.png ">default#arrowRightIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/arrowUp.png ">default#arrowUpIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/attention.png ">default#attentionIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/badminton.png ">default#badmintonIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/bank.png ">default#bankIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/bar.png ">default#barIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/barberShop.png ">default#barberShopIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/bicycle.png ">default#bicycleIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/bowling.png ">default#bowlingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/buildings.png ">default#buildingsIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/bus.png ">default#busIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/cafe.png ">default#cafeIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/camping.png ">default#campingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/car.png ">default#carIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/cellular.png ">default#cellularIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/cinema.png ">default#cinemaIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/downhillSkiing.png ">default#downhillSkiingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/dps.png ">default#dpsIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/dryCleaner.png ">default#dryCleanerIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/electricTrain.png ">default#electricTrainIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/factory.png ">default#factoryIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/fishing.png ">default#fishingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/gasStation.png ">default#gasStationIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/gym.png ">default#gymIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/hospital.png ">default#hospitalIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/house.png ">default#houseIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/keyMaster.png ">default#keyMasterIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/mailPost.png ">default#mailPostIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/metroKiev.png ">default#metroKievIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/metroMoscow.png ">default#metroMoscowIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/metroStPetersburg.png ">default#metroStPetersburgIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/metroYekaterinburg.png ">default#metroYekaterinburgIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/motobike.png ">default#motobikeIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/mushroom.png ">default#mushroomIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/phone.png ">default#phoneIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/photographer.png ">default#photographerIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/pingPong.png ">default#pingPongIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/restauraunt.png ">default#restaurauntIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/ship.png ">default#shipIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/shop.png ">default#shopIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/skating.png ">default#skatingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/skiing.png ">default#skiingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/smartphone.png ">default#smartphoneIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/stadium.png ">default#stadiumIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/storehouse.png ">default#storehouseIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/swimming.png ">default#swimmingIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/tailorShop.png ">default#tailorShopIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/teather.png ">default#teatherIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/tennis.png ">default#tennisIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/tire.png ">default#tireIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/train.png ">default#trainIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/tramway.png ">default#tramwayIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/trolleybus.png ">default#trolleybusIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/truck.png ">default#truckIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/turnLeft.png ">default#turnLeftIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/turnRight.png ">default#turnRightIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/wifi.png ">default#wifiIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/wifiLogo.png ">default#wifiLogoIcon</option>
<option value="http://api-maps.yandex.ru/i/0.4/icons/workshop.png">default#workshopIcon</option>
</select>
</div>
</div>
<br />
Название: <input type="text" name="overlayName" id="overlayName" size="30" /><br />
Описание: <textarea name="overlayDescription" id="overlayDescription" rows="2" cols="30"></textarea><br />
Широта: <input id = "point_lat" type="text" size="25"/><br />
Долгота: <input id = "point_lng" type="text" size="25"/>'
<p><input name="addMarker" type="button" onClick="addMarker()" value="Добавить" /></p>
</div>

В ней с помощью оператора select мы выбираем нужный значок для метки, вводим название и описание, нажимаем кнопку Добавить и метка появляется на карте.

При щелчке на любой другой кнопки тулбара происходит событие pointBootion.Events.Deselect, мы убираем курсор с карты и скрываем форму.

Код события pointBootion.Events.Deselect:

// При неактивной - выключаем
YMaps.Events.observe(pointBootion, pointBootion.Events.Deselect, function () {
map.removeCursor(YMaps.Cursor.POINTER);
YMaps.jQuery("#formpoint").hide();
})

При нажатии кнопки Добавить данные из формы передаются в функцию addMarker для добавления метки на карту.

function addMarker() {
 
var overlayName = YMaps.jQuery('#overlayName').attr('value');
var overlayDescription = YMaps.jQuery('#overlayDescription').attr('value');
var ZnMetki = YMaps.jQuery('select[@name=image] option:selected').text();
var LatMetki = YMaps.jQuery('#point_lat').attr('value');
var LngMetki = YMaps.jQuery('#point_lng').attr('value'); 
 
var placemark = new YMaps.Placemark(new YMaps.GeoPoint(LatMetki, LngMetki), {hasHint: true, style: ZnMetki});
 
// Устанавливает содержимое балуна
placemark.name = overlayName;
placemark.description = overlayDescription;
 
// Добавляет метку на карту 
map.addOverlay(placemark); 
}

Полный код, который у нас получился на данном этапе: ymap-editor-3.html

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

Для реализации выбора цвета используется JavaScript-библиотеку работы с цветами – jscolor (http://jscolor.com/).

Использовать данную библиотеку очень просто, с начала подключить ее с помощью строки

<script type="text/javascript" src="jscolor/jscolor.js"></script>

А в форме прописать ячейку со следующим кодом:

<input class="color" value="66ff00">

Для ломаной линии используются события plineBootion.Events.Select и plineBootion.Events.Deselect.

Код формы:

<div id="formpline" style="display: none">
<strong>Форма ввода параметров ломаной</strong><br />
<br />Толщина линии: <input type="text" name="hline" id="hline" size="2" /><br />
<br />Цвет линии: <input id="colorpline" class="color" value="66ff00">
<p><input name="addLine" type="button" onClick="addLine()" value="Добавить" /></p>
</div>}

Функция обработки значений формы addLine:

function addLine() {
var hline = YMaps.jQuery('#hline').attr('value');
var pcolor = YMaps.jQuery('#colorpline').attr('value');
 
var sl = new YMaps.Style();
sl.lineStyle = new YMaps.LineStyle();
sl.lineStyle.strokeColor = pcolor;
sl.lineStyle.strokeWidth = hline;
YMaps.Styles.add("example#CustomLine", sl);
 
 
// Создание ломанной 
var polyline = new YMaps.Polyline();
 
polyline.setStyle("example#CustomLine");
 
// Установка параметров режима редактирования
polyline.setEditingOptions({
drawing: true,
menuManager: function (index, menuItems) {
menuItems.push({
id: "StopEditing",
title: '<span style="white-space:nowrap;">Завершить редактирование<span>',
onClick: function (polyline, pointIndex) {
polyline.stopEditing(); 
YMaps.jQuery("#coords").attr("value", polyline.getPoints().join('n')); 
}
});
return menuItems;
}
}); 
 
// Добавление ломаной на карту 
map.addOverlay(polyline); 
 
// Включение режима редактирования
polyline.startEditing(); 
}

Для добавления многоугольника события polygonBootion.Events.Select и polygonBootion.Events.Deselect.

Код формы:

<div id="formpolygon" style="display: none">
<strong>Форма ввода параметров многоугольника</strong><br />
<br />Толщина линии обводки: <input type="text" name="hpolyobv" id="polyobv" size="2" /><br />
<br />Цвет линии обводки: <input id="pcolorline" class="color" value="66ff00">
<br />Цвет заливки: <input id="colorzalivki" class="color" value="66ff00">
<p><input name="addLine" type="button" onClick="addPolygon()" value="Добавить" /></p>
</div>

Функция обработки добавленных в форму значений addPolygon:

function addPolygon(){
var hpolyobv = YMaps.jQuery('#polyobv').attr('value');
var pcolorline = YMaps.jQuery('#pcolorline').attr('value');
var colorzalivki = YMaps.jQuery('#colorzalivki').attr('value'); 
 
var pstyle = new YMaps.Style();
pstyle.polygonStyle = new YMaps.PolygonStyle();
pstyle.polygonStyle.fill = true;
pstyle.polygonStyle.outline = true;
pstyle.polygonStyle.strokeWidth = hpolyobv;
pstyle.polygonStyle.strokeColor = pcolorline;
pstyle.polygonStyle.fillColor = colorzalivki;
 
 
var polygon = new YMaps.Polygon();
 
polygon.setStyle(pstyle);
 
polygon.setEditingOptions({
drawing: true,
menuManager: function (index, menuItems) {
menuItems.push({
id: "StopEditing",
title: '<span style="white-space:nowrap;">Завершить редактирование<span>',
onClick: function (polygon, pointIndex) {
polygon.stopEditing(); 
YMaps.jQuery("#coords").attr("value", polygon.getPoints().join('n')); 
}
});
return menuItems;
}
}); 
 
map.addOverlay(polygon);
 
polygon.startEditing();
 
}

В результате у нас получается следующий код: ymap-editor-4.html

Посмотреть пример в действии

В следующей части мы будем работать над редактированием добавленных объектов на Яндекс.Карту.

  • Гость: Спасибо! Уникальный блог! Ждём часть 2!!
  • Гость: Сергей, не могли бы вы подсказать про такой момент, если есть время и желание :) Сейчас процедура добавление метки выглядит так: Нажать кнопку "Режим добавления метки" -&gt; Кликнуть на место на карте -&gt; появление формы -&gt; после заполнения полей нажать "Добавить" А нужно чтобы было так: Нажать кнопку "Режим добавления метки" -&gt; появление формы -&gt; Заполнения полей и клик на карте, затем нажать "Добавить" То есть нужно чтобы форма сразу появлялась после нажатия кнопки "Режим добавления метки", когда сейчас она выводится только после клика по карте. По-моему так удобнее!
  • Гость: А что значит bootion?
  • Гость: А как произвести экспорт получившегося кода, что бы вставить его на свой сайт?
  • Гость: Сейчас я работаю над новой версией редактора и скоро обо всем напишу
  • Гость: Вопрос. Если я использую свои тайлы, то ваш метод не работает. Такое ощущение что нет обработки событий координат. можете что-нибудь подсказать?
  • Гость: Здравствуйте, у меня к Вам вопрос. Ведется ли сейчас работа в данном направлении? если да, то хотелось бы увидеть продолжение, а если нет, то хотел бы с Вами немножко пообщаться по поводу части редактирования, может быть сможете подсказать что то.
  • Гость: Временно, разработка приостановлена. Думаю в марте доделать и написать несколько заметок на блоге
  • Гость: Добрый день, а будет что нить про новый редактор???
  • Гость: Очень хочется узнать как добавить в всплывающий балун с описанием метки, форму которая будет отправлять в отдельную таблицу запись о неточности маркера. К примеру у меня карта газовых заправок, пользователь хочет добавить информацию о времени работы. Он кликает по балуну нужной заправки, и пишет в форму свое пожелание. В новой таблице базы данных всего три столбца - id (заполняется автоматом), номер точки к которой комментарий (берется ID точки к которой пишется комментарий), и собственно сам комментарий. Понимаю, что такие вещи проще (и для меня лучше) делать через систему комментирования Joomla, но если не трудно опишите в следующей части редактора. Спасибо за блог!
  • Гость: Отлично! Спасибо! Многое стало понятно.Где же часть №2...
  • Гость: в самом начале кода строку YMaps.jQuery("#formpoint").show(); нужно поднять выше, чтобы она располагалась между строками YMaps.Events.observe(pointBootion, pointBootion.Events.Select, function () { и YMaps.Events.observe(map, map.Events.Click, function (map, mEvent) { иначе есть баг с "выключением" добавления меток метки продолжают добавляться, даже когда выбран иструмент полилиния или полигон.
  • Марат Касимов: Можно ли всё это перевести на API 2.1?? Очень нужно, пожалуйста!
  • Марат Касимов: а где продолжение?
  • Марат Касимов: ну не могло же всё вот так закончится.... ((