Кластеризация маркеров на карте Google Maps API

МЕНЮ


Новости ИИ
Поиск

ТЕМЫ


Внедрение ИИНовости ИИРобототехника, БПЛАТрансгуманизмЛингвистика, обработка текстаБиология, теория эволюцииВиртулаьная и дополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информации

АРХИВ


Сентябрь 2017
Август 2017
Июль 2017
Июнь 2017
Май 2017
Апрель 2017
Март 2017
Февраль 2017
Январь 2017
Декабрь 2016
Ноябрь 2016
Октябрь 2016
Сентябрь 2016
Август 2016
Июль 2016
Июнь 2016
Май 2016
Апрель 2016
Март 2016
Февраль 2016
Январь 2016
0000

RSS


RSS новости
Ураган харви в США

Новостная лента форума ailab.ru

2017-08-01 18:02

Кластеризация

Привет, Хабр! Хочу рассказать о моем опыте разработки карты с кластеризованными маркерами на google maps api и React.js. Кластеризация — это группировка близлежащих маркеров, меток, точек в один кластер. Это помогает улучшить UX и отобразить данные визуально понятнее, чем куча наехавших друг на друга точек. Компания, в которой я работаю, создает уникальный продукт для СМИ, это мобильное приложение, смысл которого заключается в съемке фото/видео/стрим материалов и возможности получить отличную компенсацию от СМИ в том случае, если редакция использует ваш материал в публикации. Я занимаюсь разработкой SPA приложения на стеке react/redux для модерации контента, присылаемого пользователями. Недавно передо мной встала задача сделать интерактивную карту на которой можно было бы увидеть местоположение пользователей и отправить им push уведомление, если поблизости происходит интересное событие.

Вот что мне предстояло сделать:

Первое что пришло мне на ум, поискать готовое решение для react.js. Я нашел 2 топовых библиотеки google-map-react и react-google-maps. Они представляют собой обертки над стандартным API Google maps, представленные в виде компонент для react.js. Мой выбор пал на google-map-react потому-что она позволяла использовать в качестве маркера любой JSX элемент, напомню что стандартные средства google maps api позволяют использовать в качестве маркера изображение и svg элемент, в сети есть решения, описывающие хитрую вставку html конструкций в качестве маркера, но google-map-react представляет это из коробки.

Едем дальше, на макете видно что если маркеры находятся близко к друг другу, они объединяются в групповой маркер — это и есть кластеризация. В readme google-map-react я нашел пример кластеризации, но он был реализован с помощью recompose — это утилита, которая создает обертку над function components и higher-order components. Создатели пишут чтобы мы думали что это некий lodash для реакта. Но тем, кто незнаком с recompose врятли сразу будет все понятно, поэтому я адаптировал этот пример и убрал лишнюю зависимость.

Для начала зададим свойства для google-map-react и state компоненты, отрендерим карту с заранее подготовленными маркерами:
(api key получаем здесь)

const MAP = {   defaultZoom: 8,   defaultCenter: { lat: 60.814305, lng: 47.051773 },   options: {     maxZoom: 19,   }, };  state = {   mapOptions: {     center: MAP.defaultCenter,     zoom: MAP.defaultZoom,   },   clusters: [], };  //JSX   <GoogleMapReact   defaultZoom={MAP.defaultZoom}   defaultCenter={MAP.defaultCenter}   options={MAP.options}   onChange={this.handleMapChange}   yesIWantToUseGoogleMapApiInternals   bootstrapURLKeys={{ key: 'yourkey' }}  >   {this.state.clusters.map(item => {     if (item.numPoints === 1) {       return (         <Marker           key={item.id}           lat={item.points[0].lat}           lng={item.points[0].lng}         />       );     }      return (       <ClusterMarker         key={item.id}         lat={item.lat}         lng={item.lng}         points={item.points}       />     );   })} </GoogleMapReact> 

Маркеров на карте не будет, так как массив this.state.clusters пустой. Для объединения маркеров в группу используем библиотеку supercluster

Для примера сгенерируем точки с координатами:

const TOTAL_COUNT = 200;  export const susolvkaCoords = { lat: 60.814305, lng: 47.051773 };  export const markersData = [...Array(TOTAL_COUNT)]   .fill(0) // fill(0) for loose mode   .map((__, index) => ({     id: index,     lat:       susolvkaCoords.lat +       0.01 *         index *         Math.sin(30 * Math.PI * index / 180) *         Math.cos(50 * Math.PI * index / 180) +       Math.sin(5 * index / 180),     lng:       susolvkaCoords.lng +       0.01 *         index *         Math.cos(70 + 23 * Math.PI * index / 180) *         Math.cos(50 * Math.PI * index / 180) +       Math.sin(5 * index / 180),   })); 

При каждом изменении масштаба/центра карты будем пересчитывать кластеры:

handleMapChange = ({ center, zoom, bounds }) => {   this.setState(     {       mapOptions: {         center,         zoom,         bounds,       },     },     () => {       this.createClusters(this.props);     }   ); };  createClusters = props => {   this.setState({     clusters: this.state.mapOptions.bounds       ? this.getClusters(props).map(({ wx, wy, numPoints, points }) => ({           lat: wy,           lng: wx,           numPoints,           id: `${numPoints}_${points[0].id}`,           points,         }))       : [],   }); };  getClusters = () => {   const clusters = supercluster(markersData, {     minZoom: 0,     maxZoom: 16,     radius: 60,   });    return clusters(this.state.mapOptions); }; 

В методе getClusters мы скармливаем сгенерированные точки в supercluster, и на выходе получаем кластеры. Таким образом supercluster просто объединил лежащие рядом координаты точек и выдал новую точку со своими координатами и массивом points, где лежат все вошедшие точки.

Демо можно посмотреть здесь
Исходный код примера здесь

Источник: habrahabr.ru