В этом примере проекта мы используемLeaflet, легкую картографическую библиотеку с открытым исходным кодом и базовый слой карты из OpenStreetMap для создания карта внутри приложения React. Данные geoJSON, которые мы используем для добавления дополнительного слоя, извлекаются из cartodb.

Демонстрационную карту листовки реакции можно посмотреть здесь.

Мы используем такие зависимости, как useRef, useEffect от реакции. И библиотека javascript листовки и css. Сначала нам нужно создать реагирующее приложение с помощью команды create-react-app.

Установка

npx create-react-app <app_name>
cd <app_name>
npm start

npm start запустит сервер разработки, и в браузере откроется приложение по умолчанию. Далее нам нужно установить зависимости, в данном случае только библиотеку листовок. В папке src приложения введите команду установки npm вместе с именем пакета в качестве аргумента в командной строке/терминале.

npm install leaflet

Создание карты внутри компонента приложения

В файле App.js, который находится в папке src, импортируйте все зависимости, необходимые для проекта.

import { useRef, useEffect } from 'react';
import '../../../node_modules/leaflet/dist/leaflet.css';
import L from 'leaflet';

Компонент приложения()

Внутри компонента приложения, который возвращает контейнер карты, <div id="map_container" ref={map_container_ref}></div> будет содержать карту после ее инициализации. Переменная map_container_refconst содержит ссылку на контейнер карты.

const App = () => {
    const map_container_ref = useRef(null);
    const display_map = () => {
        // ...
    }
    useEffect(() => {
         const map_instance = display_map();
        /* clean up
        return () => {
          if (map_instance._loaded) {
              map_instance.remove();
          }
        };
      }, []);
    return (
        <div id="map_container" ref={map_container_ref}></div>
     );
}

После того, как React обновил DOM, мы используем хук react useEffect для инициализации карты с помощью функции display_map(); который возвращает экземпляр созданной карты. Мы можем использовать эту ссылку, чтобы уничтожить карту и очистить все связанные прослушиватели событий в разделе очистки хука useEffect.

useEffect(() => {
    const map_instance = display_map();
    // clean up
    return () => {
        if (map_instance._loaded) {
            map_instance.remove();
        }
    };
}, []);

Показать карту

Внутри display_map() на контейнер карты можно ссылаться с помощью map_container_ref. С помощью этой ссылки мы можем создать базовый слой тайлов.

/*
* Creating map
*/
const display_map = () => {
    const id = map_container_ref.current.id;
    const latitude = 0;
    const longitude = 0;
    const zoom = 2;
    const tile = "https://tile.openstreetmap.be/osmbe/{z}/{x}/{y}.png";
    const attribution =
      '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' +
      ', Tiles courtesy of <a href="https://geo6.be/">GEO-6</a>';
    const map = L.map(id).setView([latitude, longitude], zoom);
    L.tileLayer(tile, {
        attribution: attribution,
        maxZoom: 18,
    }).addTo(map);
return map;
};

Добавьте немного стиля

/*Inside App.css*/
#map_container { height: 450px; }

Это отобразит карту с тайлами из OpenStreetMap. Также не забудьте добавить свойство height в контейнер карты в файле App.css.

Отображение базового слоя

Код до этого момента создает карту с базовым слоем

/*
* Code up to this point
*/
import { useRef, useEffect } from "react";
import "../../../node_modules/leaflet/dist/leaflet.css";
import L from "leaflet";
const App= () => {
    const map_container_ref = useRef(null);
    const display_map = () => {
        const id = map_container_ref.current.id;
        const latitude = 0;
        const longitude = 0;
        const zoom = 2;
        const tile =
"https://tile.openstreetmap.be/osmbe/{z}/{x}/{y}.png";
        const attribution =
      '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' +
      ', Tiles courtesy of <a href="https://geo6.be/">GEO-6</a>';
        const map = L.map(id).setView([latitude, longitude], zoom);
        L.tileLayer(tile, {
            attribution: attribution,
            maxZoom: 18,
        }).addTo(map);
        return map;
    };
    useEffect(() => {
        const map_instance = display_map();
        // clean up
        return () => {
            if (map_instance._loaded) {
                map_instance.remove();
            }
        };
    }, []);
    return (
        <div id="map_container" ref={map_container_ref}></div>
    );
};
export default App;

Показать геоJSON

Далее мы собираемся добавить слой geoJSON из cartodb поверх нашей карты, используя метод L.geoJSON()листовки. Создайте функцию, скажем, display_population_tile()и вызовите ее после добавления базового слоя.

/*
* Fetch the data and if it's not null display it on the map with pop.
*/
const display_population_tile = (map) => {
    const url =
      "https://cartovl.carto.com/api/v2/sql?q=SELECT%20*%20FROM%20populated_places_small&format=GeoJSON";
    fetch_geoJSON(url)
        .then((geojson_data) => {
            if (geojson_data) {
                L.geoJSON(geojson_data, {
                    pointToLayer: function (feature, latlng) {
                    return L.circleMarker(latlng, {
                        radius: 8,
                        fillColor: "#ef4b38",
                        color: "#000",
                        weight: 0.5,
                        opacity: 1,
                        fillOpacity: 1,
                   });
                   },
                   onEachFeature: onEachFeaturePoint,
                }).addTo(map);
            }
        }).catch((e) => console.error(e));
    };

Получить данные

const fetch_geoJSON = async (url) => {
    const response = await fetch(url);
    if (response.status >= 200 && response.status <= 299) {
      const data = await response.json();
      return data;
    }
    return null;
  };

Функция onEachFeaturePoint() связывает содержимое всплывающего окна

const onEachFeaturePoint = (feature, layer) => {
    let popupContent = "";
    if (feature.properties && feature.properties.adm0name) {
        popupContent += "<h4>Country: " + feature.properties.adm0name + "</h4>";
    }
    if (feature.properties && feature.properties.adm1name) {
        popupContent += "<h5>City: " + feature.properties.adm1name + "</h5>";
    }
    layer.bindPopup(popupContent);
};

Весь код этого проекта

/*
* Display a base map from OpenStreetMap with Leaflet
* Add a geoJSON layer
*/
import { useRef, useEffect } from "react";
import "../../../node_modules/leaflet/dist/leaflet.css";
import L from "leaflet";
const App= () => {
    const map_container_ref = useRef(null);
    // Fetch the geoJSON sample data from cartodb    
    const fetch_geoJSON = async (url) => {
        const response = await fetch(url);
        if (response.status >= 200 && response.status <= 299) {
            const data = await response.json();
            return data;
        }
        return null;
    };
    // Display popup when clicked on the geoJSON layer with info
    const onEachFeaturePoint = (feature, layer) => {
        let popupContent = "";
        if (feature.properties && feature.properties.adm0name) {
            popupContent += "<h4>Country: " + feature.properties.adm0name + "</h4>";
        }
        if (feature.properties && feature.properties.adm1name) {
            popupContent += "<h5>City: " + feature.properties.adm1name + "</h5>";
       }
       layer.bindPopup(popupContent);
    };
    // Display the geoJSON on map
    const display_population_tile = (map) => {
        const url =
      "https://cartovl.carto.com/api/v2/sql?q=SELECT%20*%20FROM%20populated_places_small&format=GeoJSON";
      fetch_geoJSON(url)
            .then((geojson_data) => {
                if (geojson_data) {
                    L.geoJSON(geojson_data, {
                        pointToLayer: function (feature, latlng) {
                        return L.circleMarker(latlng, {
                            radius: 8,
                            fillColor: "#ef4b38",
                            color: "#000",
                            weight: 0.5,
                            opacity: 1,
                            fillOpacity: 1,
                        });
                    },
                    onEachFeature: onEachFeaturePoint,
                    }).addTo(map);
                }
            }).catch((e) => console.error(e));
      };
    //Display the base layer
    const display_map = () => {
        const id = map_container_ref.current.id;
        const latitude = 0;
        const longitude = 0;
        const zoom = 2;
        const tile =
"https://tile.openstreetmap.be/osmbe/{z}/{x}/{y}.png";
        const attribution =
      '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' +
      ', Tiles courtesy of <a href="https://geo6.be/">GEO-6</a>';
        const map = L.map(id).setView([latitude, longitude], zoom);
        L.tileLayer(tile, {
            attribution: attribution,
            maxZoom: 18,
        }).addTo(map);
        display_population_tile(map);
        return map;
    };
    useEffect(() => {
        const map_instance = display_map();
        // clean up
        return () => {
            if (map_instance._loaded) {
                map_instance.remove();
            }
        };
    }, []);
    return (
        <div id="map_container" ref={map_container_ref}></div>
    );
};
export default App;

Подведение итогов

В этом посте мы рассмотрим только некоторые основы листовки и реакции. С помощью Leaflet и React мы можем создавать более сложные карты. Надеюсь, вам понравилась моя первая статья. Если вы нашли это полезным, то я благодарен. Если вы обнаружили некоторые проблемы, пожалуйста, прокомментируйте. Также приветствуются критические замечания и предложения.