Как создавать карты с помощью React и Leaflet
Опубликовано: 2022-03-10Извлекать информацию из файла CSV или JSON не только сложно, но и утомительно. Представление тех же данных в виде наглядного пособия проще. В этой статье мы собираемся представить на карте места немедицинских пожаров, на которые отреагировала пожарная служба Сан-Франциско.
Для этого урока мы будем использовать следующие инструменты:
- Листовка
Библиотека JavaScript для интерактивных карт. - Реагировать
Библиотека JavaScript для создания пользовательских интерфейсов. - React-буклет
Компоненты React для карт Leaflet
Что такое листовка?
Leaflet.js, набравшая около 27 тысяч звезд, является одной из ведущих библиотек JavaScript с открытым исходным кодом для интерактивных карт, удобных для мобильных устройств. Он использует преимущества HTML5 и CSS3 в современных браузерах, но при этом доступен и в старых. В целом, он поддерживает все основные настольные и мобильные платформы.
Листовка весит около 38 КБ и отлично подходит для базовых вещей. Для дополнительных расширений его можно расширить с помощью огромного количества плагинов.
Многие газеты, в том числе NPR, Washington Post, Boston Globe и другие организации, используют Leaflet для своих проектов по углубленному изучению данных.
The San Francisco Chronicle, например, разработала проект под названием California Fire tracker — интерактивную карту, которая с помощью Leaflet предоставляет информацию о лесных пожарах в Калифорнии. Они не только точно определили происхождение огня, но и показали нам его траекторию.
Поскольку это вводный урок, мы будем только отмечать места пожаров и отображать некоторые подробности о них.
Прежде чем перейти к React, давайте разберемся с основами Leaflet. Для этого мы создадим простой пример, где мы будем настраивать карту Leaflet, работать с маркерами и всплывающими окнами.
Во-первых, давайте создадим файлы index.html и app.js в нашей папке /project
и свяжем последний с нашим файлом index.html .
Чтобы начать использовать Leaflet, нам нужно связать Leaflet CSS и Leaflet JS в наших тегах head. Следует иметь в виду, что Leaflet CSS предшествует Leaflet JS. Это все для листовки.
Нам нужно добавить еще одну вещь в наш файл index.html — контейнер, в котором будет храниться наша карта.
<div></div>
Прежде чем мы забудем, давайте добавим высоту нашему div.
#mapid { height: 1000px; }
Теперь самое интересное. Независимо от того, решите ли вы создать новый файл JavaScript или продолжить в тегах script, убедитесь, что <div id="mapid">
добавлен в dom перед вызовом L.map('mapid')
.
Вы, наверное, спросите: «Но почему?» Ну, это потому, что это выдаст вам ошибку, если вы привяжете карту к контейнеру, который еще не существует.
Uncaught Error: Map container not found
Создание карты
Теперь самое интересное. Чтобы инициализировать карту, мы передаем наш div в L.map()
с некоторыми параметрами.
const myMap = L.map('mapid', { center: [37.7749, -122.4194], zoom: 13 })
Давайте шаг за шагом, чтобы понять, что только что произошло. Мы используем класс Map API-интерфейса Leaflet для создания карты на странице. Мы передаем в этот класс два параметра:
- Мы передали строковую переменную, представляющую идентификатор
DOM
. - Необязательный литерал объекта с параметрами карты
Есть много вариантов, которые мы могли бы передать нашему классу, но два основных параметра — это центр и масштабирование. Center определяет начальный географический центр карты, а zoom указывает начальный уровень масштабирования карты. Оба они не определены по умолчанию.
Для центра мы передали координаты Сан-Франциско. Есть много мест, где мы можем выполнять прямое и обратное геокодирование, но для базового поиска, такого как этот, мы можем воспользоваться Google.
Обычно значение масштабирования зависит от того, что вы хотите отобразить. Вы хотите показать город или штат? Страна или континент? Идите вперед и поэкспериментируйте со значением масштабирования, чтобы получить лучшее представление. Для этого примера мы выбрали 13, потому что он показывает весь город.
Другой способ инициализации карты — использование setView(). Он принимает массив координат и целое число для уровня масштабирования.
const myMap = L.map('map').setView([37.7749, -122.4194], 13);
По умолчанию все взаимодействия с мышью и касания на карте включены, и на ней есть элементы управления масштабированием и атрибуцией.
Создание слоя
Далее мы добавим слой тайлов на нашу карту; в нашем случае это мозаичный слой Mapbox Streets. Мы можем добавлять различные типы слоев тайлов, создавая экземпляр класса TileLayer.
Чтобы создать слой плитки, нам нужно установить шаблон URL для изображения плитки, текст атрибуции и максимальный уровень масштабирования слоя. Шаблон URL-адреса — это то, что дает нам доступ к нужному слою плитки от поставщика услуг. Поскольку мы используем API статических плиток Mapbox, нам потребуется запросить токен доступа.
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery (c) <a href="https://www.mapbox.com/">Mapbox</a>', maxZoom: 18, id: 'mapbox/streets-v11', accessToken: 'your.mapbox.access.token' }).addTo(mymap);
На этом этапе, если мы откроем наш index.html в браузере, мы сможем увидеть карту Сан-Франциско. Ставим булавку на карту.
Маркеры и круги
У нас есть карта и слой, но он не указывает нам ни на что конкретное. Чтобы указать на определенное место на карте, Leaflet предоставляет нам маркеры.
Чтобы закрепить местоположение, мы создаем экземпляр маркера с помощью класса Marker, передаем координаты и добавляем его на карту. Здесь мы используем координаты Твин Пикс в городе.
const marker = L.marker([37.7544, -122.4477]).addTo(mymap);
Точно так же мы можем привязать круг к карте, используя класс Circle
. Мы передаем несколько дополнительных параметров, таких как радиус, цвет и так далее. Для circle
маркера мы передаем координаты маяка Пойнт Бонита.

const circle = L.circle([37.8157, -122.5295], { color: 'gold', fillColor: '#f03', fillOpacity: 0.5, radius: 200 }).addTo(mymap);
Всплывающие окна
Это все здорово, но что, если мы хотим передать дополнительную информацию о местоположении. Мы делаем это с помощью всплывающего окна.
circle.bindPopup("I am pointing to Point Bonita Lighthouse"); marker.bindPopup("I am pointing to Twin Peaks");
Метод bindPopup принимает указанное содержимое HTML и добавляет его к маркеру, поэтому всплывающее окно появляется при нажатии на маркер.
React-буклет
Теперь мы знаем, как создать карту и добавить маркеры с помощью Leaflet и ванильного JavaScript. Давайте посмотрим, как мы можем добиться таких же результатов с помощью React. Мы не собираемся делать такое же приложение, а вместо этого сделаем расширенное приложение.
Первая задача для нас — получить токен доступа с портала открытых данных Сан-Франциско. Это онлайн-портал, где мы можем найти сотни наборов данных из города и округа Сан-Франциско. Я решил использовать этот ресурс, но есть много других ресурсов, которые мы можем использовать вместо этого.
API-ключ доступа
- Создайте учетную запись и войдите на портал.
- Нажмите на ссылку управления в правом нижнем углу.
- Нажмите «Создать новый ключ API» и дайте ему имя.
- Скопируйте идентификатор ключа и секретный ключ. Вам понадобится это для доступа к данным.
Для этого мы будем использовать React-Leaflet — компоненты реакции для карт Leaflet. Давайте создадим реагирующее приложение.
npx create-react-app react-fire-incidents cd react-fire-incidents
Затем давайте установим react-leaflet
и Leaflet, выполнив следующую команду в нашем терминале:
npm install react-leaflet leaflet
App.js
Давайте создадим папку /components
внутри src
. Внутри компонентов создадим файл с именем Map.js. Здесь будет жить наша Карта. Теперь давайте отредактируем App.js , удалив ненужный код и импортировав модули из axios react react-leaflet axios
и только что созданного Map.js.
import React, { Component, Fragment } from 'react'; import axios from 'axios'; import Map from './components/Map'
В нашем классе App мы собираемся определить массив в нашем состоянии, называемом инцидентами — когда страница загружается, мы будем помещать наши данные в этот массив.
class App extends Component { state = { incidents: [], } render() { return ( <div> </div> ); } } export default App;
Далее мы сделаем запрос GET при монтировании компонента. У нас есть токен приложения, но нам все еще нужна конечная точка. Где мы находим конечную точку?
Перейдем к порталу и нажмем «Обзор данных». В строке поиска давайте искать инциденты с пожарами. Первый результат, который появляется, это то, что мы ищем. После того, как мы нажмем на ссылку, мы сможем получить URL-адрес, нажав кнопку API в правом верхнем углу.
Мы передадим конечную точку в наш запрос GET, а в качестве параметров передадим лимит и наш токен приложения. Исходные данные содержат тысячи записей, но для простоты мы ограничили их до 500. Мы обновляем массив инцидентов нашими результатами.
Как только мы получим данные, мы обновим наше состояние.
async componentDidMount() { const res = await axios.get('https://data.sfgov.org/resource/wr8u-xric.json', { params: { "$limit": 500, "$$app_token": YOUR_APP_TOKEN } }) const incidents = res.data; this.setState({incidents: incidents }); };
Вот как должен выглядеть наш App.js.
class App extends Component { state = { incidents: [], } async componentDidMount() { const res = await axios.get('https://data.sfgov.org/resource/wr8u-xric.json', { params: { "$limit": 500, "$$app_token": YOUR_APP_TOKEN } }) const incidents = res.data; this.setState({incidents: incidents }); }; render() { return ( <Map incidents={this.state.incidents}/> ); } } export default App;
Map.js
Поскольку мы уже знаем, как создать карту Leaflet, эта часть будет относительно простой. Мы будем импортировать компоненты Map
, TileLayer
, Marker
, Popup
из react react-leaflet
.
import React, { Component } from 'react' import { Map, TileLayer, Marker, Popup } from 'react-leaflet'
Если мы помним из предыдущего примера, нам нужны координаты и уровень масштабирования для инициализации карты. В нашем классе Map
мы определяем их в нашем состоянии, используя переменные lat
, lng
и zoom
.
export default class Map extends Component { state = { lat: 37.7749, lng: -122.4194, zoom: 13, } render() { return ( <div></div> ) } }
Затем мы проверим, пуст ли наш массив инцидентов. Если он пуст, мы вернем сообщение о том, что «Данные загружаются»; в противном случае мы вернем карту.
В компонент Map
нашего react-leaflet
мы передадим центральные координаты и уровень масштабирования, а также некоторые стили. В нашем компоненте TileLayer
мы будем передавать атрибуцию и URL-адрес, как в нашем предыдущем примере.
render() { return ( this.props.incidents ? <Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}} > <TileLayer attribution='© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> </Map> : 'Data is loading...' ) } }
Затем мы перебираем наш props.incident
и передаем координаты каждого инцидента компоненту Marker. Поскольку React предупреждает нас о необходимости передачи ключа каждому элементу массива, мы также передадим ключ маркеру.
Внутри компонента Marker
мы передаем компонент Popup
. Я добавил некоторую информацию об инциденте во всплывающем окне.
<Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}}> <TileLayer attribution='© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> { this.props.incidents.map(incident => { const point = [incident['point']['coordinates'][1], incident['point']['coordinates'][0]] return ( <Marker position={point} key={incident['incident_number']} > <Popup> <span>ADDRESS: {incident['address']}, {incident['city']} - {incident['zip_code']}</span> <br/> <span>BATTALION: {incident['battalion']}</span><br/> </Popup> </Marker> ) }) } </Map>
И это все. Если мы запустим наше приложение, и если все пройдет нормально, мы сможем увидеть карту Сан-Франциско с 500 маркерами, указывающими нам на места пожаров. Если мы нажмем на один из этих маркеров, появится всплывающее окно с дополнительной информацией об инциденте.
Подведение итогов
Несмотря на то, что мы рассмотрели многое, это были только основы. Листовка — очень мощный инструмент, и мы можем создавать множество различных карт. Если вы хотите поиграть, попробуйте добавить еще один слой или собственный значок. Или, может быть, вы хотите создать интерактивную картографическую карту.