Работа с Yandex-картой

879
15.07.2011

Есть перечень сотрудников, работающих с перечнем клиентов. Все очень просто: каждый сотрудник обслуживает свой участок города и все клиенты в этом участке - его. Т.е. отношение сотрудник-клиенты = один ко многим. 

Стоит конкретная задача: Есть перечень сотрудников, работающих с перечнем клиентов. Все очень просто: каждый сотрудник обслуживает свой участок города и все клиенты в этом участке - его. Т.е. отношение сотрудник-клиенты = один ко многим. 

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

Данный механизм отлично работал, пока было всего лишь 3 сотрудника и они были постоянными. Но ... все растет, все развивается. и добавление дополнительных сотрудников, замена сотрудников без программиста в данной реализации не возможны. Поэтому появилась необходимость сделать некую интеграцию инфоблоков битрикса с яндекс картами (аналогично можно сделать в принципе и на гугл картах, просто когда делал первую версию нашел хорошее описание именно по яндекс картам, поэтому на них и остановился....). 

Итак решение. 

Создаем инфоблок, в котором будем описывать наших клиентов со всеми необходимыми свойствами. Обязательно одним из свойств должна быть Привязка к Яндекс карте (у меня это "C_ADDR"). Каждый раздел данного ИБ - соответствующий сотрудник (Можно указывать код сотрудника в названии раздела, но я решил сделать более надежно - создал доп. свойство с привязкой к сотруднику для раздела "UF_USER ", т.к. раздел можно назвать как-то удобно для себя, например "ЦЕНТР", если сотрудник обслуживает клиентов, находящихся в центре, а в привязку к сотруднику ставим нужного пользователя сайта). 

Далее создаем механизм вывода всех элементов на карту. Понадобится 2 ключевых файла: index.php - файл для вывода карты и outpoint.php - для формирования массива точек. 

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

$person_list=array();
if(CModule::IncludeModule("iblock"))
{
   $arFilter = Array('IBLOCK_ID'=>intval(55), 'ACTIVE'=>'Y');
    $items = CIBlockSection::GetList(Array("sort"=>"ASC"), $arFilter, false,Array("UF_USER", "UF_BALOON"));
   while($arItem = $items->GetNext())
   {
     $person_list[]=array(
     "U_ID"=>intval($arItem["UF_USER"]),  /* ID ответственного сотрудника*/
     "SECT_NAME"=>$arItem["NAME"],        /* Название раздела - пригодится при формировании групп точек*/
     "SECT_ID"=>$arItem["ID"],             /* ID раздела.*/
     "BALOONLINK"=>"<img src='".$baloonlink."' /> ",   /*Картинка балуна*/
     "BALOON"=>CFile::GetPath($arItem["UF_BALOON"])  /*ссылка на балун*/
     );
    };
}
else
  ShowError("Модуль не установлен");
//echo "<pre>";print_r($person_list);echo "</pre>";

На странице index.php, которая занимается выводом карты должна быть подключена библиотека jquery и определен ключ яндекс карты: 

<script type="text/javascript" src="jquery-1.5.2.min.js"></script>
<script src="http://api-maps.yandex.ru/1.1/?key=<Ваш ключ>&modules=pmap&wizard=constructor" type="text/javascript"></script>

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

<td style="vertical-align:top;">
        <div class="YMaps YMaps-cursor-grab" id="YMapsID" style="width:600px;height:600px">    </div>
    </td>

и на основании построенного массива с разделами выводим перечень разделов: 

<td valign="top">
    <ol class="message_list">
        <?foreach($person_list as $group):
            echo "<li>";
            echo "<p class='message_head'><cite>".$group["SECT_NAME"]."</cite></p>";
            echo "<div class='message_body' style='display: none'>";
            echo "<ul>";
            $arSelect = Array("ID", "NAME");
            $arFilter = Array("IBLOCK_ID"=>IntVal(<Код ИБ>), "SECTION_ID"=>intval($group["SECT_ID"]), "ACTIVE"=>"Y");
            $res = CIBlockElement::GetList(Array("NAME" => "ASC"), $arFilter, false, Array(), $arSelect);
            while($ob = $res->GetNextElement())
            {
                $arFields = $ob->GetFields();
                //выводим элементы в списке, сразу добавляя ссылки на редактирование/удаление
                echo "<li>";?>
                <a title="Удалить" href="del.php?id=<?=$mar['id']?>" onclick="if(!confirm('Вы действительно хотите удалить данный элемент?')){return false;}">
                    <img src="deletered.png">
                </a>
                <a title="Пройдено" href="set.php?id=<?=$mar['id']?>" onclick="if(!confirm('Вы тут уже были?')){return false;}">
                    <img src="camera_test.png">
                </a>
                <a title="Редактировать" href="edt.php?id=<?=$mar['id']?>">
                    <img src="pencil.png">
                </a>
                <?echo "  ";?>
                <?echo $arFields["NAME"]."</li>";
            }
        echo "</ul>";
    echo "</div>";
    echo "</li>";
        endforeach;?>
    </ol>
</td>

На этом подготовительную часть можно считать завершенной. Перейдем к непосредственному выводу данных на карту.

Я вешаю вывод карты на событие загрузки страницы: 

<script type="text/javascript">
        // Создание обработчика для события window.onLoad
        var map, geoResult;

        YMaps.jQuery(function () {
            // Создание экземпляра карты и его привязка к созданному контейнеру
            map = new YMaps.Map(YMaps.jQuery("#YMapsID"[0]

            // Установка для карты ее центра и масштаба
            map.setCenter(new YMaps.GeoPoint(<Ваши координаты центра>), <Ваш масштаб>);</script>

Далее нам нужно создать шаблон балуна. Определяем шаблон содержимого балуна: 

t.text = "<div style=\"color:#ff0000; font-weight: normal;\">$[description]</div>";
YMaps.Templates.add("my#template", t);

Для каждой группы нужно создать свою иконку на карте. Для этого в списке разделов сайта было добавлено пользовательское свойство "UF_BALOON" типа "файл", в которое при создании раздела заливаем соответствующую иконку. 

Перебираем в цикле все иконки: 

<?reset($person_list);
foreach($person_list as $group):
    $gid=$group["U_ID"]?>
    var s<?php echo $gid?> = new YMaps.Style();
    s<?php echo $gid?>.iconStyle = new YMaps.IconStyle();
    s<?php echo $gid?>.iconStyle.offset = new YMaps.Point(-15, -15);
    s<?php echo $gid?>.iconStyle.href = "<?php echo $group["BALOON"]?>";
    s<?php echo $gid?>.iconStyle.size = new YMaps.Point(30, 30);
    s<?php echo $gid?>.balloonContentStyle = new YMaps.BalloonContentStyle("my#template");
<?php
endforeach;?>

в качестве имени стиля сделал такое: 

т.е. к букве "s" добавляем соответствующий ИД раздела. 

Далее добавляем на карту элементы управления: 

s<?php echo $gid?>

Далее формируем коллекции, которые будем выводить: 

map.addControl(new YMaps.TypeControl());
                map.addControl(new YMaps.ToolBar());
                map.addControl(new YMaps.Zoom());
                map.addControl(new YMaps.MiniMap());
                map.addControl(new YMaps.ScaleLine());
                map.enableScrollZoom();
          map.enableDragging();

                /*Создаем панель инструментов*/
                var toolbar = new YMaps.ToolBar();

                /*Определяем кнопки панели инструментов*/
                var pointBootion = new YMaps.ToolBarRadioButton(YMaps.ToolBar.DEFAULT_GROUP, {
                icon: "http://api.yandex.ru/i/maps/tools/draw/add_point.png",
                width: 20,
                hint: "Режим добавления меток"
                });

                toolbar.add(pointBootion);
          map.addControl(toolbar);
                // При активной кнопке включаем добавление меток

      YMaps.Events.observe(pointBootion, pointBootion.Events.Select, function () {
               map.addCursor(YMaps.Cursor.POINTER);
               YMaps.Events.observe(map, map.Events.Click, function (map, mEvent) {
                  /*var myHtml = "Значение: " + mEvent.getGeoPoint() + "<br />"+'<form id="formadd" name="formadd_point" method="post" action="outpoint.php"><p>Сотрудник: <select name="pointperson">'+$persons+'</select></p><p>Название: <input name="namepoint" type="text" size="20" maxlength="80" /></p><p>Описание: <textarea name="descriptpoint" cols="20" rows="5"></textarea></p><input name="pcoord" type="hidden" value="'+mEvent.getGeoPoint()+'" /><p><input name="subpoint" type="submit" value="Добавить" /></p></form>';
                  map.openBalloon(mEvent.getGeoPoint(), myHtml);*/
               });

      })


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

И наполняем эти коллекции точками: 

<?php
    reset($person_list);
    foreach($person_list as $group):?>
        <?php $gid=$group["U_ID"]?>
        collection<?php echo $gid?> = new YMaps.GeoObjectCollection(s<?php echo $gid?>);

        <?php
    endforeach;
    ?>

Далее эти коллекции группируем для скрытия/отображения на карте:

var groups = [
    <?php
    reset($person_list);
    $colgroups=count($person_list);
    foreach($person_list as $i => $item):
        ?>
        <?php echo ($i > 0 ? ', ' : '')?>createGroup("<?php echo $item["BALOONLINK"]?>"+"<?php echo $item["SECT_NAME"]?>", collection<?php echo $item["U_ID"]?>)

        <?php
    endforeach;
    ?>
    ];

                // Создание списка групп
                for (var i = 0; i < groups.length; i++) {
                    addMenuItem(groups[i], map, YMaps.jQuery("#menu"));
                }
            })

Функция построения карты закончилась. Далее нужно описать вспомогательные функции (Вставляю без расшифровок - комментов в коде хватает...) : 

// Добавление одного пункта в список
            function addMenuItem (group, map, menuContainer) {

                // Показать/скрыть группу на карте
                    YMaps.jQuery("<a class=\"title \" href=\"#\">" + group.title + "</a>")
                    .bind("click", function () {
                        var link = YMaps.jQuery(this);

                        // Если пункт меню "неактивный", то добавляем группу на карту,
                        // иначе - удаляем с карты
                        if (link.hasClass("active")) {
                            map.removeOverlay(group);
                        } else {
                            map.addOverlay(group);
                        }

                        // Меняем "активность" пункта меню
                        link.toggleClass("active");

                        return false;
                    })

                    // Добавление нового пункта меню в список
                    .appendTo(
                        YMaps.jQuery("<li></li>").appendTo(menuContainer)
                    )
            };

            // Создание группы
            function createGroup (title, objects, style) {
                var group = new YMaps.GeoObjectCollection(style);

                group.title = title;
                group.add(objects);

                return group;
            };

            // Функция для отображения результата геокодирования
            // Параметр value - адрес объекта для поиска
            function showAddress (value) {
                // Удаление предыдущего результата поиска
                map.removeOverlay(geoResult);

                // Запуск процесса геокодирования
                var geocoder = new YMaps.Geocoder(value, {results: 3, boundedBy: map.getBounds(), prefLang: "uk"});

                // Создание обработчика для успешного завершения геокодирования
                YMaps.Events.observe(geocoder, geocoder.Events.Load, function () {
                    // Если объект был найден, то добавляем его на карту
                    // и центрируем карту по области обзора найденного объекта
                    if (this.length()) {
                        geoResult = this.get(0);
                        map.addOverlay(geoResult);
                        map.setBounds(geoResult.getBounds());
                    }else {
                        alert("Ничего не найдено")
                    }
                });

                // Процесс геокодирования завершен неудачно
                YMaps.Events.observe(geocoder, geocoder.Events.Fault, function (geocoder, error) {
                    alert("Произошла ошибка: " + error);
                })
            }

Ну вот в принципе и все. Осталось настроить механизм добавления на карту новых точек ( закомментированная в коде функция "YMaps.Events.observe".

Итак, переходим к функции YMaps.Events.observe(pointBootion, pointBootion.Events.Select, function () {}). 

В нее пишем следующий код: 

YMaps.Events.observe(pointBootion, pointBootion.Events.Select, function () {
    map.addCursor(YMaps.Cursor.POINTER);
    YMaps.Events.observe(map, map.Events.Click, function (map, mEvent) {
                        var myHtml = "Координаты: " + mEvent.getGeoPoint() + "<br />"+'<form id="formadd" name="formadd_point" method="post" action="add.php"><input name="pcoord" type="hidden" value="'+mEvent.getGeoPoint()+'" /><p><input name="subpoint" type="submit" value="Добавить" /></p></form>';
     map.openBalloon(mEvent.getGeoPoint(), myHtml);
   });

})

Данная функция обрабатывает событие нажатия мышкой на карте (при включенном режиме добавления точек на карту). 

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

При нажатии на кнопку "Добавить" переходим на страницу добавления новой точки на карту (add.php). 

Рассмотрим более подробно суть данной страницы. 

На данной странице размещаем стандартный компонент добавления нового элемента ИБ ("bitrix:iblock.element.add.form" Необходимо провести небольшую правку шаблона данного компонента. 

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

global $USER;
$uid = $USER->GetID();

Дальше определим раздел для данного пользователя: 

if(CModule::IncludeModule("iblock"))
    {
       $arFilter = Array('IBLOCK_ID'=>intval(55), 'ACTIVE'=>'Y');
       $items = CIBlockSection::GetList(Array("sort"=>"ASC"), $arFilter, false,Array("UF_USER", "UF_BALOON"));
       while($arItem = $items->GetNext())
       {
         if($arItem["UF_USER"]==$uid){$sectid=$arItem["ID"];}
         $person_list[]=array(
            "U_ID"=>intval($arItem["UF_USER"]),  /* ID ответственного сотрудника*/
            "SECT_ID"=>$arItem["ID"],       /* ID раздела.*/
            "SECT_NAME"=>$arItem["NAME"]
            );
        };
    }
    else
      ShowError("Модуль не установлен");

(Скажу сразу, что в данной разработке не учитывался вариант, когда раздел не создан. Один раз в пол года можно и вручную создать раздел... Хотя со временем добавлю все-таки автоматическое создание раздела в случае его отсутствия... ) 

Теперь знаем раздел. 

Из пост-переменной получаем значение координат: 

//получаем координаты:
    if(isset($_POST["pcoord"])){
        $Coord = $_POST["pcoord"];
        $coords = explode(",",$Coord);
        $Coord = $coords['1'].','.$coords['0']; //у яндекса и у битрикса разной направленности координаты...
    } else {
        LocalRedirect("index.php");
    }

Делаем скрытые поля, в которых записываем значение координат, раздела, и сотрудника: 

<input type="hidden" name="PROPERTY[211][0]" value=<?=$Coord?> /> <?//Записываем координаты?>
    <input type="hidden" name="PROPERTY[217][0]" value=<?=$uid?> /> <?//Записываем сотрудника?>
    <input type="hidden" id="SECTION" name="PROPERTY[IBLOCK_SECTION][0]" value=<?=$sectid?> /> <?//Задаем раздел, в котором отображать?>

Ну вот в принципе и все ключевые моменты. 

Всем спасибо за внимание. 



Благодарю за внимание! Делитесь вашими замечаниями в комментариях ниже.


P.S. Обращайтесь ко мне за приобретением лицензий и продлений на 1C-Битрикс "Управление сайтом", лицензий на облачную и коробочную версии Битрикс 24 а также за приобретением и внедрением готовых решений на базе 1С-Битрикс от партнеров. За более подробной информацией свяжитесь со мной любым удобным для вас способом


Комментарии

Еще никто не комментировал данную публикацию. Будьте первыми!

Добавить комментарий

captcha

Возврат к списку