[ReactJS] Deck.gl, Mapbox и React: отображение точек, маршрутов и кластеризация на карте
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Mapbox является американским поставщиком пользовательских онлайн-карт для веб-сайтов и приложений. С 2010 года он быстро расширил нишу пользовательских карт, в ответ на ограниченный выбор, предложенный поставщиками карт, такими как Google Maps. На данный момент остается достойным продуктом на фоне большого количества конкурентов.
Deck.gl это платформа на базе WEB API WebGL для визуального исследовательского анализа больших наборов данных (не только для визуализации географических данных). Создана и поддерживается Uber. Изначально разработана с использованием Mapbox. Также поддерживаются Google Maps, но возможности будут ограничены.
Теперь мы можем переходить к практике, которую разделим на несколько пунктов. Напомню, что для реализации поставленных задач будем использовать React.
1. Отображение карты
Для начала нам нужно зарегистироваться на сайте Mapbox и в личном кабинете получить токен. Он понадобится для работы с картами в проекте.
Переходим к установке зависимостей: yarn add mapbox-gl @urbica/react-map-gl
Итоговый код для простого отображения карты на весь экран будет выглядеть следующим образом:
import * as React from "react";
import MapGL from "@urbica/react-map-gl";
import "mapbox-gl/dist/mapbox-gl.css";
const App = () => {
const viewport = {
latitude: 0,
longitude: 0,
zoom: 1,
};
return (
<MapGL
style={{ width: "100vw", height: "100vh" }}
accessToken={TOKEN}
{...viewport}
/>
);
};
2. Отображение точек
Будем использовать Deck.gl слой IconLayer.
Подробнее о нем можно почитать здесь.
Установим зависимости: yarn add @deck.gl/mapbox @deck.gl/layers
Атлас с иконками возьмем по ссылке.
В объекте ICON_MAPPING описаны все виды иконок и их координаты в атласе выше.
Массив для двух точек будет выглядеть следующим образом. Обращаю внимание, что нужно в ключе icon указать вид иконки из объекта ICON_MAPPING.
const iconsData = [
{
id: 1,
name: "First Point",
size: 5,
icon: "marker",
coordinates: [-100.12097640000002, 35.449965],
color: [0, 0, 128],
},
{
id: 2,
name: "Second Point",
size: 5,
icon: "marker",
coordinates: [-100.0893059, 40.39611790000001],
color: [255, 0, 0],
},
];
Итоговый код:
import * as React from "react";
import MapGL, { CustomLayer } from "@urbica/react-map-gl";
import { MapboxLayer } from "@deck.gl/mapbox";
import { IconLayer } from "@deck.gl/layers";
import "mapbox-gl/dist/mapbox-gl.css";
import Atlas from "../src/img/icon-atlas.png";
import iconsData from "./layersData/iconsData";
const ICON_MAPPING = {
marker: { x: 0, y: 0, width: 140, height: 148, mask: true },
circle: { x: 0, y: 130, width: 120, height: 120, mask: true },
};
const App = () => {
const [viewport, setViewport] = React.useState({
latitude: 0,
longitude: 0,
zoom: 1,
});
const iconsLayer = new MapboxLayer({
id: "icon-layer",
type: IconLayer,
data: iconsData,
iconAtlas: Atlas,
sizeScale: 10,
iconMapping: ICON_MAPPING,
getIcon: (d) => d.icon,
getPosition: (d) => d.coordinates,
getSize: (d) => d.size,
getColor: (d) => d.color,
});
return (
<MapGL
style={{ width: "100vw", height: "100vh" }}
accessToken={TOKEN}
onViewportChange={setViewport}
{...viewport}
>
<CustomLayer layer={iconsLayer} />
</MapGL>
);
};
3. Построение маршрутов
Для этой задачи используем TripsLayer слой.
В качестве данных я создаю объект с уже готовыми массивами, поэтому мне не нужно дополнительно использовать метод map при создании слоя и передачи координат с временем как в примере по ссылке. Хотелось бы отметить, что каждой координате соответствует свое время (unix timestamp).
const tripsData = [
{
coordinates: [
[-100.149639, 35.440481],
[-100.151832, 35.439649],
[-100.152752, 35.439323],
[-100.154222, 35.439699],
[-100.154293, 35.439301],
[-100.15539, 35.438506],
[-100.155745, 35.43833],
[-100.156138, 35.4382],
[-100.157342, 35.43783],
[-100.157729, 35.437787],
[-100.15931, 35.438335],
[-100.159402, 35.438002],
[-100.159333, 35.437877],
[-100.159702, 35.437776],
[-100.160215, 35.437766],
[-100.160195, 35.43783],
[-100.160439, 35.43806],
[-100.160269, 35.437808],
[-100.160124, 35.438099],
[-100.143509, 35.441997],
[-100.142375, 35.442401],
[-100.141645, 35.442632],
[-100.141291, 35.442684],
[-100.141841, 35.442592],
[-100.139913, 35.443157],
[-100.139481, 35.443198],
[-100.138995, 35.443288],
[-100.138686, 35.443415],
],
timestamps: [
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009520,
1556009880,
1556009880,
1556010060,
1556011440,
1556011440,
1556011500,
1556011500,
1556011500,
1556011680,
1556012100,
1556012160,
1556012160,
1556012160,
1556012160,
1556012160,
1556012160,
1556012160,
1556012160,
],
color: [18, 83, 2],
},
];
Итоговый код. Обращаю внимание, что в ключе currentTime данного слоя я указал для примера последнее время unix timestamp из массива выше.
import * as React from "react";
import MapGL, { CustomLayer } from "@urbica/react-map-gl";
import { MapboxLayer } from "@deck.gl/mapbox";
import { TripsLayer } from "@deck.gl/geo-layers";
import "mapbox-gl/dist/mapbox-gl.css";
import tripsData from "./layersData/tripsData";
const App = () => {
const [viewport, setViewport] = React.useState({
latitude: 0,
longitude: 0,
zoom: 1,
});
const tripsLayer = new MapboxLayer({
id: "trips-layer",
type: TripsLayer,
data: tripsData,
widthMinPixels: 3,
rounded: true,
trailLength: 200,
currentTime: 1556012340,
getPath: (d) => d.coordinates,
getTimestamps: (d) => d.timestamps,
getColor: (d) => d.color,
});
return (
<MapGL
style={{ width: "100vw", height: "100vh" }}
accessToken={TOKEN}
onViewportChange={setViewport}
{...viewport}
>
<CustomLayer layer={tripsLayer} />
</MapGL>
);
};
4. Кластеризация
Для ее реализации нам потребуется установить зависимости: yarn add supercluster @urbica/react-map-gl-cluster
Подробнее можно почитать здесь.
Данные для точек запишем следующим образом:
const clusterData = [
{
id: 1,
name: "First Point",
coordinates: [-110.12097640000002, 35.449965],
},
{
id: 2,
name: "Second Point",
coordinates: [-112.0893059, 35.39611790000001],
},
];
Нам понадобится написать компоненту, которая будет описывать точку, содержащую в себе несколько других. Например на скриншоте точка содержит в себе две обычные единичные и при приближении распадается.
import React from "react";
import { Marker } from "@urbica/react-map-gl";
import { pointStyle } from "./App";
const ClusterMarker = (props) => (
<Marker longitude={props.longitude} latitude={props.latitude}>
<div style={pointStyle}>{props.pointCount}</div>
</Marker>
);
Главная компонента выглядит так. Для надежности добавил дополнительную проверку координат:
import * as React from "react";
import MapGL, { Marker } from "@urbica/react-map-gl";
import Cluster from "@urbica/react-map-gl-cluster";
import "mapbox-gl/dist/mapbox-gl.css";
import ClusterMarker from "./ClusterMarker";
import clusterData from "./layersData/clusterData";
export const pointStyle = {
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "32px",
height: "32px",
borderRadius: "50%",
border: "1px solid black",
backgroundColor: "white",
};
const App = () => {
const [viewport, setViewport] = React.useState({
latitude: 0,
longitude: 0,
zoom: 1,
});
const clusterLayerData = React.useMemo(
() =>
clusterData.map((point) => {
const [fCoordinate, sCoordinate] = point.coordinates;
const lng =
sCoordinate > -90 && sCoordinate < 90 ? fCoordinate : sCoordinate;
const lat = lng === fCoordinate ? sCoordinate : fCoordinate;
return (
<Marker key={point.id} longitude={lng} latitude={lat}>
<div style={pointStyle}>1</div>
</Marker>
);
}),
[]
);
return (
<MapGL
style={{ width: "100vw", height: "100vh" }}
accessToken={TOKEN}
onViewportChange={setViewport}
{...viewport}
>
<Cluster
radius={40}
extent={512}
nodeSize={64}
component={ClusterMarker}
children={clusterLayerData}
/>
</MapGL>
);
};
Заключение
На сайте Deck.gl еще собрано множество разнообразных слоев. Но как вы могли заметить, с помощью Deck.gl и Mapbox можно с легкостью реализовывать достаточно сложный на первый взгляд функционал. Успехов!
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, ReactJS, Управление разработкой, TypeScript] Wrike переходит с Dart на новый стек. Какой?
- [JavaScript, Программирование, ReactJS, Учебный процесс в IT] React: наглядное пособие для начинающих. Создаем свой компонент без знаний JavaScript (перевод)
- [Разработка веб-сайтов, ReactJS] React Server-Side Rendering (SSR) — руководство новичка (перевод)
- [Data Mining, Алгоритмы, Big Data, Машинное обучение] Рекомендации Друзей ВКонтакте: ML на эго-графах
- [JavaScript, ReactJS] Исходники React.memo или что такое SimpleMemo
- [Программирование, ReactJS, TypeScript] Чего мне не хватало в функциональных компонентах React.js
- [Работа с 3D-графикой, Разработка игр, Дизайн игр, Игры и игровые приставки] От эскиза до релиза: пайплайн регулярного создания контента на примере идеи для оружия от игрока
- [PHP, JavaScript, Разработка мобильных приложений, ReactJS] История одного видео редактора
- [Python, MongoDB, Maps API] Аспекты учета и поиска геоинформационных объектов с задействованием MongoDB
- [JavaScript, Angular, ReactJS, VueJS] Экосистема JavaScript: тренды в 2021 году. Всё ли так однозначно?
Теги для поиска: #_reactjs, #_react.js, #_map, #_mapbox, #_marshruty (маршруты), #_klasterizatsija (кластеризация), #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:41
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Mapbox является американским поставщиком пользовательских онлайн-карт для веб-сайтов и приложений. С 2010 года он быстро расширил нишу пользовательских карт, в ответ на ограниченный выбор, предложенный поставщиками карт, такими как Google Maps. На данный момент остается достойным продуктом на фоне большого количества конкурентов. Deck.gl это платформа на базе WEB API WebGL для визуального исследовательского анализа больших наборов данных (не только для визуализации географических данных). Создана и поддерживается Uber. Изначально разработана с использованием Mapbox. Также поддерживаются Google Maps, но возможности будут ограничены. Теперь мы можем переходить к практике, которую разделим на несколько пунктов. Напомню, что для реализации поставленных задач будем использовать React. 1. Отображение карты Для начала нам нужно зарегистироваться на сайте Mapbox и в личном кабинете получить токен. Он понадобится для работы с картами в проекте. Переходим к установке зависимостей: yarn add mapbox-gl @urbica/react-map-gl Итоговый код для простого отображения карты на весь экран будет выглядеть следующим образом: import * as React from "react";
import MapGL from "@urbica/react-map-gl"; import "mapbox-gl/dist/mapbox-gl.css"; const App = () => { const viewport = { latitude: 0, longitude: 0, zoom: 1, }; return ( <MapGL style={{ width: "100vw", height: "100vh" }} accessToken={TOKEN} {...viewport} /> ); }; 2. Отображение точек Будем использовать Deck.gl слой IconLayer. Подробнее о нем можно почитать здесь. Установим зависимости: yarn add @deck.gl/mapbox @deck.gl/layers Атлас с иконками возьмем по ссылке. В объекте ICON_MAPPING описаны все виды иконок и их координаты в атласе выше. Массив для двух точек будет выглядеть следующим образом. Обращаю внимание, что нужно в ключе icon указать вид иконки из объекта ICON_MAPPING. const iconsData = [
{ id: 1, name: "First Point", size: 5, icon: "marker", coordinates: [-100.12097640000002, 35.449965], color: [0, 0, 128], }, { id: 2, name: "Second Point", size: 5, icon: "marker", coordinates: [-100.0893059, 40.39611790000001], color: [255, 0, 0], }, ]; Итоговый код: import * as React from "react";
import MapGL, { CustomLayer } from "@urbica/react-map-gl"; import { MapboxLayer } from "@deck.gl/mapbox"; import { IconLayer } from "@deck.gl/layers"; import "mapbox-gl/dist/mapbox-gl.css"; import Atlas from "../src/img/icon-atlas.png"; import iconsData from "./layersData/iconsData"; const ICON_MAPPING = { marker: { x: 0, y: 0, width: 140, height: 148, mask: true }, circle: { x: 0, y: 130, width: 120, height: 120, mask: true }, }; const App = () => { const [viewport, setViewport] = React.useState({ latitude: 0, longitude: 0, zoom: 1, }); const iconsLayer = new MapboxLayer({ id: "icon-layer", type: IconLayer, data: iconsData, iconAtlas: Atlas, sizeScale: 10, iconMapping: ICON_MAPPING, getIcon: (d) => d.icon, getPosition: (d) => d.coordinates, getSize: (d) => d.size, getColor: (d) => d.color, }); return ( <MapGL style={{ width: "100vw", height: "100vh" }} accessToken={TOKEN} onViewportChange={setViewport} {...viewport} > <CustomLayer layer={iconsLayer} /> </MapGL> ); }; 3. Построение маршрутов Для этой задачи используем TripsLayer слой. В качестве данных я создаю объект с уже готовыми массивами, поэтому мне не нужно дополнительно использовать метод map при создании слоя и передачи координат с временем как в примере по ссылке. Хотелось бы отметить, что каждой координате соответствует свое время (unix timestamp). const tripsData = [
{ coordinates: [ [-100.149639, 35.440481], [-100.151832, 35.439649], [-100.152752, 35.439323], [-100.154222, 35.439699], [-100.154293, 35.439301], [-100.15539, 35.438506], [-100.155745, 35.43833], [-100.156138, 35.4382], [-100.157342, 35.43783], [-100.157729, 35.437787], [-100.15931, 35.438335], [-100.159402, 35.438002], [-100.159333, 35.437877], [-100.159702, 35.437776], [-100.160215, 35.437766], [-100.160195, 35.43783], [-100.160439, 35.43806], [-100.160269, 35.437808], [-100.160124, 35.438099], [-100.143509, 35.441997], [-100.142375, 35.442401], [-100.141645, 35.442632], [-100.141291, 35.442684], [-100.141841, 35.442592], [-100.139913, 35.443157], [-100.139481, 35.443198], [-100.138995, 35.443288], [-100.138686, 35.443415], ], timestamps: [ 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009520, 1556009880, 1556009880, 1556010060, 1556011440, 1556011440, 1556011500, 1556011500, 1556011500, 1556011680, 1556012100, 1556012160, 1556012160, 1556012160, 1556012160, 1556012160, 1556012160, 1556012160, 1556012160, ], color: [18, 83, 2], }, ]; Итоговый код. Обращаю внимание, что в ключе currentTime данного слоя я указал для примера последнее время unix timestamp из массива выше. import * as React from "react";
import MapGL, { CustomLayer } from "@urbica/react-map-gl"; import { MapboxLayer } from "@deck.gl/mapbox"; import { TripsLayer } from "@deck.gl/geo-layers"; import "mapbox-gl/dist/mapbox-gl.css"; import tripsData from "./layersData/tripsData"; const App = () => { const [viewport, setViewport] = React.useState({ latitude: 0, longitude: 0, zoom: 1, }); const tripsLayer = new MapboxLayer({ id: "trips-layer", type: TripsLayer, data: tripsData, widthMinPixels: 3, rounded: true, trailLength: 200, currentTime: 1556012340, getPath: (d) => d.coordinates, getTimestamps: (d) => d.timestamps, getColor: (d) => d.color, }); return ( <MapGL style={{ width: "100vw", height: "100vh" }} accessToken={TOKEN} onViewportChange={setViewport} {...viewport} > <CustomLayer layer={tripsLayer} /> </MapGL> ); }; 4. Кластеризация Для ее реализации нам потребуется установить зависимости: yarn add supercluster @urbica/react-map-gl-cluster Подробнее можно почитать здесь. Данные для точек запишем следующим образом: const clusterData = [
{ id: 1, name: "First Point", coordinates: [-110.12097640000002, 35.449965], }, { id: 2, name: "Second Point", coordinates: [-112.0893059, 35.39611790000001], }, ]; Нам понадобится написать компоненту, которая будет описывать точку, содержащую в себе несколько других. Например на скриншоте точка содержит в себе две обычные единичные и при приближении распадается. import React from "react";
import { Marker } from "@urbica/react-map-gl"; import { pointStyle } from "./App"; const ClusterMarker = (props) => ( <Marker longitude={props.longitude} latitude={props.latitude}> <div style={pointStyle}>{props.pointCount}</div> </Marker> ); Главная компонента выглядит так. Для надежности добавил дополнительную проверку координат: import * as React from "react";
import MapGL, { Marker } from "@urbica/react-map-gl"; import Cluster from "@urbica/react-map-gl-cluster"; import "mapbox-gl/dist/mapbox-gl.css"; import ClusterMarker from "./ClusterMarker"; import clusterData from "./layersData/clusterData"; export const pointStyle = { display: "flex", justifyContent: "center", alignItems: "center", width: "32px", height: "32px", borderRadius: "50%", border: "1px solid black", backgroundColor: "white", }; const App = () => { const [viewport, setViewport] = React.useState({ latitude: 0, longitude: 0, zoom: 1, }); const clusterLayerData = React.useMemo( () => clusterData.map((point) => { const [fCoordinate, sCoordinate] = point.coordinates; const lng = sCoordinate > -90 && sCoordinate < 90 ? fCoordinate : sCoordinate; const lat = lng === fCoordinate ? sCoordinate : fCoordinate; return ( <Marker key={point.id} longitude={lng} latitude={lat}> <div style={pointStyle}>1</div> </Marker> ); }), [] ); return ( <MapGL style={{ width: "100vw", height: "100vh" }} accessToken={TOKEN} onViewportChange={setViewport} {...viewport} > <Cluster radius={40} extent={512} nodeSize={64} component={ClusterMarker} children={clusterLayerData} /> </MapGL> ); }; Заключение На сайте Deck.gl еще собрано множество разнообразных слоев. Но как вы могли заметить, с помощью Deck.gl и Mapbox можно с легкостью реализовывать достаточно сложный на первый взгляд функционал. Успехов! =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:41
Часовой пояс: UTC + 5