Оставаясь на сайте, вы принимаете условия Политики конфиденциальности и использования файлов-cookies

объекты на карте

Фильтрация объектов на карте с произвольным выделением

516
7

Часто на сайтах, в особенности посвященных всевозможным объявлениям, можно увидеть карту, на которой отображаются объекты. Обычно на таких сайтах присутствует поиск по объектам, с помощью которого можно найти интересующую информацию. В таких случаях перед разработчиком может возникнуть задача поиска объектов по выделенной области.


Безусловно, существует масса готовых решений для этой задачи, но возникает вопрос, как выделить именно произвольную область на карте? В распоряжении разработчика имеются стандартные методы предусмотренные API Google или Yandex картами, но их минус в том, что они ограничиваются выделением на карте с помощью стандартных геометрических фигур, таких как окружность или полигон.

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

google maps, maps, geo
Выделение области простым нажатием мышки.

Далее представлена простая реализация для начинающих подобного функционала:

  1. Дополнительные библиотеки:
    • Jquery;
    • simplify.js (http://mourner.github.io/simplify-js/) (git: https://github.com/mourner/simplify-js).
  2. HTML код:
<div id="wrapper-map" style="position: relative; width: 100%; height: 400px;"> 
	<div id="select_button" class="mdl-button mdl-js-button" style="z-index: 2000; top: 10px; left: 10px; position: absolute;" data-flag="fl">Выделить</div> 
	<canvas id="canv" style="z-index: 1000; top: 0; left: 0; position: absolute; display:none;"></canvas> 
	<div id="map" style="width: 100%; height: 100%; overflow: hidden; padding: 0; margin: 0; position: absolute;"></div> 
</div>

На канве рисуется произвольная линия, которая превращается в полигон средствами simplify.js. Для рисования необходимо поверх карты отобразить канву, но при этом не будет работать навигация карты, поэтому после того, как выделение области завершено и полигон добавлен на карту, необходимо скрывать блок с канвой.

Основные этапы JS кода:

$(document).ready(function(){ 
   //инициализация карты 
   var Map = (function() { 
       ymaps.load(init); 
       function init () { 
           // ...
           //добавляем точки на карту (addItemPoints некая функция, возвращающая массив точек) 
           points = addItemPoints(); 
           //добавляем нулевой полигон   
           addPolygon() 
       } 

       // ...

       //добавление выделенной области на карту 
       function addPolygon(coord) { 
           var myGeoObject = new ymaps.GeoObject({ 
               geometry: { 
                   type: "Polygon", 
                   coordinates: [coord], 
               } 
           }); 
           myMap.geoObjects.add(myGeoObject); 
           return myGeoObject; 
       } 

       return { 
           addPolygon: addPolygon, 
           convert: convert            
       }; 
   })(); 

   //инициализируем необходимые переменные (канва, карта, линия)
  // …

   // обработка начала выделения области (по нажатию мыши) 
   function mouseDown(e) { 
      // …
       line = []; 
       line.push({ 
          // …
       }); 
   } 

   // обработка выделения (по движению мыши) 
   function mouseMove(e) { 
          // …
       line.push({ 
          // …
       }); 
   } 

   // обработка завершения выделения (по отпусканию кнопки мыши) 
   function mouseUp() { 
       // убираем нажатие кнопки выделения, тем самым позволяя управлять картой 
      // …
       // убираем прослушку событий по действиям мыши 
      // …
       // проводим аппроксимацию нарисованной линии, а также добавляем получившийся полигон на карту 
       aproximate(); 

       // рассчитываем точки, входящие в выделенный полигон 
       var myCollection = new ymaps.GeoObjectCollection(); 
       myMap.geoObjects.remove(myCollection); 
       var placemarks = []; 
       myCollection = new ymaps.GeoObjectCollection();        

       // пробегаем по всем точкам points, и если они попадают в polygon.geometry.contains, то создаем 
       // myPlacemark и кладем ее в myCollection. Также весь список найденных точек кладем в points_select 
       for (i in points) { 
           if (polygon.geometry.contains([points[i].CoordX, points[i].CoordY])){ 
               points_select.push(points[i]); 
               var myPlacemark = new ymaps.GeoObject({ 
                         // …
                }); 
               placemarks.push(myPlacemark); 
               myCollection.add(myPlacemark); 
           } 
       } 
       myMap.geoObjects.add(myCollection); 

       /*updateItemList некая функция, которая позволяет передать выбранные точки на сервер или обработать каким-либо образом (допустим можно ajax отправить на сервер выделенные точки, там отфильтровать по каким-либо дополнительным параметрам и вернуть отфильтрованный результат.) */
       updateItemList(points_select); 
   } 

   // функция, аппроксимирующая нарисованную линию в полигон и добавляющая ее на карту 
   function aproximate() { 
      // … 
   } 

   canv.addEventListener('mousedown', mouseDown); 
});

Основные шаги:

  1. Инициализация карты с добавлением нулевого полигона.
  2. Добавление в массив всех существующих точек (addItemPoints).
  3. Определение событий мыши для рисования на канве (mouseDown, mouseMove, mouseUp).
  4. По завершении выделения области аппроксимация нарисованной линии в полигон и добавление его на карту (с помощью simplify.js).
  5. Определение точек, которые попадают в созданный полигон.
  6. Обработка выбранных точек (updateItemList).
google maps, maps, geo
Выделенная область на карте.

Приведенный код требует доработки в виде определения функций добавляющей точки и обрабатывающей возвращенный список точек. Также возможно необходимо будет подкорректировать под поставленную задачу условие, проверяющее вхождение точек в область (polygon.geometry.contains) (это будет нужно, если формат передаваемых точек будет отличаться от тех, что использованы в примере).

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

P.S. Скачать исходники можно тут.