Веб-приложения значительно выиграли от включения карт, предоставляющих пользователям ценную информацию о местоположении. Карты изменили наше взаимодействие с миром: от навигации по незнакомым местам до поиска близлежащих закусочных. В результате в последнее время интеграция карт в веб-сайты становится все более популярной. Тем не менее, разработка карт, одновременно функциональных и удобных для пользователя, может представлять собой проблему, особенно для тех, у кого нет опыта в этой области. В этой статье я поделюсь полезными советами о том, как создавать эффективные карты в браузере.
Давайте обсудим технологии. При работе с картами мы обычно используем три слоя:
Отрисовка пользовательского интерфейса, включающего кнопки и формы. В нашем стеке эту роль выполняет React;
Рендеринг карты и обеспечение взаимодействия с пользователем. Для этого мы используем Mapbox;
Получение данных с сервера, например информации о маркерах или полигонах. Для получения данных мы используем встроенную функцию выборки браузера.
Давайте рассмотрим каждый элемент, чтобы лучше понять наш стек технологий при работе с картами.
Поскольку изменение элементов на странице — самая затратная операция для браузера, важно делать ее максимально эффективно. Чтобы решить эту проблему, инженеры Facebook разработали библиотеку React, которая позволяет быстро и просто изменять элементы на странице. Помимо обеспечения быстрого изменения состояния на странице, React позволяет нам делать это декларативно, не работая напрямую с элементами DOM. Вместо этого мы используем абстракцию, обычно
// It's our state. Is the user our friend or not? // false by default const [isFriend, setIsFriend] = useState(false) // Depending on the state, we show the text on the button const buttonText = isFriend ? 'Your my Friend' : 'Add as Friend' // There is JSX, syntax for UI // In this case, we display a button, when clicked, we change the state return ( <button onClick={() => setIsFriend(true)}>{buttonText}</button> )
Компоненты можно вкладывать в обычные элементы DOM, такие как формы, кнопки и поля ввода, в нижней части иерархии. Собирая эти простые элементы, мы можем создавать более сложные, например, законченную форму:
const Form = () => ( <form> <input name="Email"/> <input name="Password"/> </form> ) const App = () => ( <main> <h1>My form!</h1> <Form /> </main> )
Как React помогает нам в контексте карт? Поскольку карта на странице интерактивна и похожа на кнопку или форму, мы стремимся оптимизировать ее отображение и взаимодействие с помощью таких событий, как клики по карте. React может помочь достичь этой оптимизации. Вот пример того, как это работает:
// Use React to render the map with different event handlers // and render markers return ( <BaseMap onInitMap={() => console.log('I am alive!')} onClickMap={() => console.log('Click!')} onDestroyMap={() => console.log('Oh no!')} > <ClustersMarkers /> <PostsMarkers /> <ListingsMarkers /> </BaseMap> )
При работе с React важно помнить, что он позволяет эффективно манипулировать элементами на странице, быстро вносить изменения и взаимодействовать с ними посредством событий. Это достигается за счет абстракции, напоминающей HTML, что позволяет легко создавать сложные компоненты из более простых.
Теперь давайте обсудим саму карту. Создание и использование карт может быть сложной задачей, и лишь немногие компании-производители могут создавать карты с нуля. Как правило, большинство людей полагаются на готовые библиотеки с удобным и проверенным API.
Доступны многочисленные поставщики динамических карт, включая Google Maps, Leaflet, Bing Maps, Mapbox и другие. Однако мы сосредоточимся на
Предложения Mapbox
Кроме того, Mapbox предоставляет
Mapbox предоставляет множество
Давайте вернемся к картам Mapbox. Что это
Он инициализирует карту в элементе HTML на странице;
Он загружает и отображает изображения, составляющие карту;
Он рисует дополнительные элементы, например маркеры, используя GeoJson в качестве входных данных;
Он генерирует события, такие как клики или изменения масштаба, которые можно обработать.
Давайте подробнее рассмотрим каждый из этих предметов.
Mapbox специализируется на рендеринге карт с использованием тайлов. Плитки — это маленькие квадратные изображения, составляющие большую карту. Размер плитки по умолчанию составляет 512x512 пикселей. Он может быть
К вашему сведению: Mapbox Studio позволяет нам выбирать конкретные данные, которые мы хотим включить в фрагменты карты. Эти плитки затем помещаются на
<canvas width="100" height="100" />
Mapbox обрабатывает загрузку, вставку и обновление плиток. Все, что нам нужно сделать, это указать, где мы хотим отображать карту, и начальные условия, такие как уровень масштабирования или координаты карты. Чтобы использовать Mapbox, вам понадобится
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; const map = new mapboxgl.Map({ container: 'map', // we can use an Id or an element style: 'mapbox://styles/mapbox/streets-v11', // URL for styles center: [-74.5, 40], // initial coordinates [lng, lat] zoom: 9, // initial zoom });
После этого мы получим карту на странице в элементе с идентификатором «map».
Чтобы предоставить пользователям больше информации на карте, мы часто отображаем расположение определенного заведения или границы определенной территории. Для этого мы используем особый формат данных, называемый
GeoJSON — это стандартный формат хранения географических структур на картах. Он может хранить различные примитивные типы, описывающие географические объекты, такие как адреса, местоположения, улицы, шоссе, границы, страны, штаты и их комбинации, известные как multipart. GeoJSON был представлен в 2008 году и представлен следующим образом:
{ "type": "Feature", // also can be FeatureCollection, it's collection of Feature "geometry": { "type": "Point", // also can be LineString, Polygon, MultiPolygon "coordinates": [125.6, 10.1] // for other types you can use Array with coordinates }, "properties": { // it's metadata, we can you that to show something on the map "name": "Dinagat Islands" } }
Давайте поговорим о системе координат, используемой в Mapbox. По умолчанию Mapbox используетmap.setProjection
.
Теперь мы обсудим, как отображать GeoJSON на карте. Mapbox предлагает две сущности, которые могут оказаться полезными:
Чтобы отобразить полигоны или маркеры на карте, мы должны получить данные в формате GeoJson с сервера. Затем создаем источник, вводим в него данные и подключаем к нужному слою.
const geoJsonFeature = { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [ [-67.13734, 45.13745], [-66.96466, 44.8097], [-68.03252, 44.3252], [-67.13734, 45.13745] ] } } // Create source with our data map.addSource('ourSource', { 'type': 'geojson', 'data': geoJsonFeature }); // Add layer for background map.addLayer({ 'id': 'background', 'type': 'fill', 'source': 'ourSource', // название нашего source 'layout': {}, 'paint': { 'fill-color': '#0080ff', 'fill-opacity': 0.5 } }); // Add layer for border map.addLayer({ 'id': 'border', 'type': 'line', 'source': 'ourSource', 'layout': {}, 'paint': { 'line-color': '#000', 'line-width': 3 } });
После запуска этого кода мы получаем результат:
Чтобы узнать больше по этой теме, вы можете обратиться кon
с нужным нам типом события, аналогично тому, как мы работаем с элементами DOM.
map.on('mousemove', (e) => { console.log(JSON.stringify(e.point)); }); // Result: {"x":330,"y":49}
Подводя итог, что нам нужно помнить? Mapbox позволяет нам отображать карту, рисовать поверх нее данные и обрабатывать события карты. При этом Mapbox заботится о загрузке и отображении изображений (тайлов).
Несколько слов о
Что помнить? Мы получаем данные с сервера, и для этого существует множество библиотек, но мы будем использовать fetch. Далее мы рассмотрим, как мы это делаем конкретно при работе с картами, так как есть нюансы.
Теперь посмотрим, как описанные выше технологии работают вместе. Сначала мы получим данные для отображения многоугольника с помощью выборки. Затем мы объявим инициализацию карты и после ее загрузки добавим полигон на карту.
Вы также можете найти рабочий пример на сайте
const useFetch = () => { /* Our data { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [-67.13734, 45.13745], [-68.03252, 44.3252], [-68.90478, 47.18479], [-67.13734, 45.13745], ] ] } } */ const [data, setData] = useState(null) useEffect(() => { fetch('https://our-api.com/polygon') .then(response => response.json()) .then(setData) .catch(e => { console.error(e) }) }, [setData]) return { data } } const BaseMap = () => { // Use the hook to fetch data const { data } = useFetch(GET_REGION); // Map instance const map = useRef(null); // DOM element const mapContainer = useRef(null); // Main logic - init the map and add the event useEffect(() => { if (map.current) { return; // initialize map only once } mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; map.current = new mapboxgl.Map({ container: mapContainer.current, style: 'mapbox://styles/mapbox/light-v10', // style URL (it's Mapbox's core style) center: [-68.137343, 45.137451], // starting position zoom: 5 // starting zoom }); // Handle event map.on('load', () => { const sourceId = 'source-region' // Add a data source containing GeoJSON data map.addSource(sourceId, { 'type': 'geojson', 'data': data.region // our data from Apollo }); // Add a new layer to visualize the polygon map.addLayer({ 'id': 'background', 'type': 'fill', 'source': sourceId, // reference the data source 'paint': { 'fill-color': '#0080ff', // blue color fill 'fill-opacity': 0.5 } }); // Add a black outline around the polygon map.addLayer({ 'id': 'outline', 'type': 'line', 'source': sourceId, 'paint': { 'line-color': '#000', 'line-width': 3 } }); }); }); return <div ref={mapContainer} />; }
Мы рассмотрели стек технологий, лежащий в основе нашей будущей архитектуры. В следующей статье мы обсудим принципы, которые помогают проектировать архитектуру карты, как добиться максимально низкой связанности и высокой связанности модулей, а также как поддерживать и развивать масштабируемую систему карт.
Большое спасибо за ваше внимание! Хорошего дня.