DevGang
Авторизоваться

React-Leaflet v3: Создание картографического приложения

Вот код созданного нами компонента Leaflet:

Это прекрасно работает, но наш код не очень похож на React, поскольку мы манипулируем DOM с помощью vanilla Leaflet и рендерим все в один div «map». Давайте рассмотрим, как создать правильное приложение сопоставления React с открытым исходным кодом с помощью React-Leaflet.

Некоторые вещи, которые следует помнить

Важно помнить, что React-Leaflet не заменяет Leaflet. Он просто предоставляет способ создания слоев Leaflet в качестве компонентов React, вместо использования настройки useRefuseEffect, как мы использовали в нашем приложении vanilla-Leaflet / React.  Это означает, что наши компоненты React-Leaflet могут вести себя иначе, чем стандартный компонент React. Из документации React-Leaflet:

Рендеринг DOM

React не отображает слои Leaflet в DOM, этот рендеринг выполняется самим Leaflet. React визуализирует элемент <div> только при визуализации компонента MapContainer, содержимого компонентов слоев пользовательского интерфейса.

Помните, как мы отрендерили все в один div на карте vanilla Leaflet? React-Leaflet делает то же самое за кулисами!

Свойства компонента

Свойства, переданные компонентам, используются для создания соответствующего экземпляра Leaflet при первом рендеринге компонента и по умолчанию должны рассматриваться как неизменяемые.

Во время первого рендеринга все эти свойства должны поддерживаться, как и Leaflet, однако они не будут обновляться в пользовательском интерфейсе при изменении, если они явно не задокументированы как изменяемые.

Изменения изменяемых свойств сравниваются по ссылке (если не указано иное) и применяются, вызывая соответствующий метод для экземпляра элемента Leaflet.

Опять же, это очень похоже на нашу карту vanilla Leaflet. Элементы-листовки создаются только при первом монтировании компонента, точно так же, как мы использовали хук useEffect, а затем модифицировали его с помощью refs.

Реагирование на контекст

React Leaflet использует контекстный API React, чтобы сделать некоторые экземпляры элементов Leaflet доступными для дочерних элементов, которые в этом нуждаются.

Каждый экземпляр карты Leaflet имеет свой собственный контекст React, созданный компонентом MapContainer. Другие компоненты и хуки, предоставляемые React Leaflet, могут использоваться только как потомки MapContainer.

React-Leaflet использует контекстный API, чтобы сделать свои экземпляры и состояние доступными для всех своих дочерних элементов. Вы увидите пример этого, как только мы перейдем к тому, как мы обрабатываем события карты.

Начинаем

Чтобы упростить настройку, у меня есть стартер, который вы можете использовать на Stack Blitz. Если вы предпочитаете следовать инструкциям на своем локальном компьютере, ознакомьтесь с разделом документации по установке. Не забудьте установить Leaflet, а также React-Leaflet! Также обратите внимание, что в этой демонстрации мы будем использовать некоторые стили и компоненты из Material-UI.

В public/index.html, мы можем вставить ссылку CDN для файлов CSS и Javascript Leaflet:

index.html

<!-- CSS -->
<link
  rel="stylesheet"
  href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
  integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
  crossorigin=""
/>
<!-- JS -->
<script
  src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
  integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
  crossorigin=""
></script>

Теперь мы можем настроить наш MapContainer и наш слой листов карты.  MapContainer - это то, что создает экземпляр карты Leaflet и совместно использует этот экземпляр со всеми его дочерними элементами через контекстный API. Для наших целей мы только передадим centerzoomstyle и zoomControl в MapContainer на данный момент, но ознакомьтесь с этим списком реквизитов, которые вы можете передать для последующего использования. Мы будем центрировать нашу карту по Соединенным Штатам ( [37.0902, -95.7129]), установив zoom для 3, и установив zoomControl для false,так как мы добавим его немного позже. У опоры style также должны быть установлены высота и ширина.

В src/map мы можем создать новый файл с именем Map.jsx, а затем импортировать его в наш файл App.js:

Map.jsx

import React from 'react'
import { MapContainer, TileLayer } from 'react-leaflet'


const Map = () => {

  return (
    <>
      <MapContainer 
        center={[37.0902, -95.7129]} 
        zoom={3} 
        zoomControl={false} 
        style={{ height: '100vh', width: '100%' }}
      >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
      </MapContainer>
    </>
  )
}

export default Map

App.jsx

import React from "react";
import "./assets/css/style.css";
import { Container } from "@material-ui/core";
import Map from "./map/Map";

export default function App() {
  return (
    <Container disableGutters>
      <Map />
    </Container>
  );
}

Теперь у вас на экране должна быть пустая карта, которая выглядит примерно так:

Добавление элементов управления

Теперь, когда у нас есть наша карта, давайте добавим ZoomControl и LayersControl.

ZoomControl добавить очень просто. Нам нужно импортировать компонент ZoomControl и разместить его прямо под компонентом TileLayer. Мы также хотим передать ему опору position со значением 'topright':

Map.jsx&nbsp;

import React from 'react'
import { MapContainer, TileLayer, ZoomControl } from 'react-leaflet'


const Map = () => {

  return (
    <>
      <MapContainer 
        center={[37.0902, -95.7129]} 
        zoom={3} 
        zoomControl={false} 
        style={{ height: '100vh', width: '100%' }}
      >
        <TileLayer
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <ZoomControl position='topright'/>
      </MapContainer>
    </>
  )
}

export default Map

LayersControl немного по-другому. Мы можем импортировать этот компонент и поместить его прямо над только что установленным ZoomControl. Затем мы используем LayersControl.BaseLayers и LayersControl.Overlay чтобы добавить наши слои к элементу управления. Мы будем использовать компонент LayersControl.Overlay для включения и выключения таких слоев, как маркеры и круги, на карте позже, а пока мы поместим компонент TileLayer внутрь компонента LayersControl.BaseLayers. Давайте также добавим вторую опцию плитки карты, чтобы мы действительно могли использовать наш элемент управления слоем. Перейдите к демонстрации Leaflet-Providers и выберите вторую плитку для использования, а затем настройте свой код следующим образом:

Map.jsx

import React from 'react'
import { MapContainer, TileLayer, ZoomControl, LayersControl } from 'react-leaflet'


const Map = () => {

  return (
    <>
      <MapContainer 
        center={[37.0902, -95.7129]} 
        zoom={3} 
        zoomControl={false} 
        style={{ height: '100vh', width: '100%' }}
      >
        <LayersControl position="topright">
          {/* 
            Give the layer a name that will be displayed inside of the layers control.
            We also want to pass the checked prop to whichever map tile we want
            displayed as the default:
          */}
          <LayersControl.BaseLayer checked name="Basic Map">
            <TileLayer
              attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
              url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            />
          </LayersControl.BaseLayer>
          {/* 
            Add the second maptile:
          */}
          <LayersControl.BaseLayer name="Topo Map">
            <TileLayer
              attribution='Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
              url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
            />
          </LayersControl.BaseLayer>
        </LayersControl>
        <ZoomControl position='topright'/>
      </MapContainer>
    </>
  )
}

export default Map

Доступ к экземпляру карты и обработчикам событий

Вспомните раздел "Что нужно помнить" в этой статье. Мы узнали, что MapContainer делает экземпляр карты Leaflet доступным для своих потомков и что хуки для доступа к этому состоянию доступны только его потомкам. Из-за этого нам выгодно отделить логику MapContainer от логики слоя. В src/map, давайте создадим новый файл с именем Layers.jsx и копипастнем наш компонент LayersControl здесь:

Layers.jsx

import React from 'react'
import { TileLayer, LayersControl } from 'react-leaflet'

const Layers = () => {

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          <TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          />
        </LayersControl.BaseLayer>
        <LayersControl.BaseLayer name="Topo Map">
          <TileLayer
            attribution='Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
            url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
          />
        </LayersControl.BaseLayer>
      </LayersControl>
    </>
  )
}

export default Layers

Map.jsx

import React from 'react'
import { MapContainer, ZoomControl } from 'react-leaflet'
import Layers from './Layers'

const Map = () => {

  return (
    <>
      <MapContainer 
        center={[37.0902, -95.7129]} 
        zoom={3} 
        zoomControl={false} 
        style={{ height: '100vh', width: '100%' }}
      >
        <Layers />
        <ZoomControl position='topright'/>
      </MapContainer>
    </>
  )
}

export default Map

Экземпляр карты Leaflet имеет множество методов, доступных для получения и установки его состояния. Чтобы использовать эти методы, React-Leaflet предоставляет нам хук useMap. Скажем, мы хотим получить начальные границы и уровень масштабирования нашей карты. Мы могли бы вызвать useMap в верхней части нашего компонента и присвоить его переменной, скажем map, а затем ссылаться на эту переменную всякий раз, когда мы хотим использовать метод карты Leaflet:

Layers.jsx

import React from 'react'
// Import the useMap hook:
import { useMap, TileLayer, LayersControl } from 'react-leaflet'

const Layers = () => {
  // Call useMap:
  const map = useMap()

  // Use the map methods:
  console.log("Map Bounds:", map.getBounds())
  console.log("Zoom Level:", map.getZoom())


  return (
    // ..LayersControl...
  )
}

export default Layers

Однако, если бы мы хотели получать границы и уровень масштабирования каждый раз, когда они меняются, мы бы использовали хук useMapEvents. Мы импортируем его так же, как и хук useMap, но мы прикрепим обработчики событий карты Leaflet к экземпляру карты следующим образом:

Layers.jsx

import React from 'react'
// Import useMapEvents:
import { useMapEvents, TileLayer, LayersControl } from 'react-leaflet'

const Layers = () => {
  // Call useMapEvents:
  const map = useMapEvents({
    // Use leaflet map event as the key and a call back with the 
    // map method as the value:
    zoomend: () => {
      // Get the zoom level once zoom ended:
      console.log(map.getZoom())
    },
    moveend: () => {
    // Get bounds once move has ended:
      console.log(map.getBounds())
    }
  })


  return (
    // ...LayersControl...
  )
}

export default Layers

Отображение данных с помощью GeoJSON

Очень высока вероятность, что в какой-то момент в процессе создания вашего картографического приложения вы будете получать и отображать данные в формате geoJSON. Наша карта в настоящее время сосредоточена на США, поэтому мы будем использовать геоданные для штатов Вайоминг, Монтана, а также Северная и Южная Дакота. Я уже настроил файлы, содержащие geoJSON для каждого состояния в src/data.

Чтобы отобразить geoJSON, мы собираемся использовать компонент слоя React-Leaflet GeoJSON. Мы собираемся импортировать данные Layers.jsx, поместить данные в массив в состоянии, затем использовать .map() для итерации этого массива и возврата слоев GeoJSON. При итерации данных нам необходимо передать свойство geometry, найденное в массиве features каждого набора geoJSON, которое дает слою GeoJSON тип и координаты создаваемого слоя. Нам также нужно будет предоставить уникальный ключ для каждого слоя GeoJSON, поэтому мы будем использовать display_name найденный в properties для каждого массива features. Если вы хотите изменить стиль слоя GeoJSON, вы можете передать объект опций в опору pathOptions. Вот как все это будет выглядеть:

Layers.jsx

import React, { useState } from 'react'
import { useMapEvents, TileLayer, LayersControl, GeoJSON } from 'react-leaflet'
import WY from '../data/Wyoming.json'
import MT from '../data/Montana.json'
import ND from '../data/NorthDakota.json'
import SD from '../data/SouthDakota.json'

const Layers = () => {
  // Set all the border data to state array:
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    // ... Map Events ...
  })

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          // ... LayersContol.BaseLayer ...
        </LayersControl.BaseLayer>
        {
          // Iterate the borderData with .map():
          borderData.map((data) => {
            // Get the layer data from geojson:
            const geojson = data.features[0].geometry
            // Get the name of the state from geojson:
            const state_name = data.features[0].properties.display_name
            return (
              // Pass data to layer via props:
              <>
                <GeoJSON key={state_name} data={geojson} pathOptions={{ color: 'blue' }} />
              </>
            )
          })
        }
      </LayersControl>
    </>
  )
}

export default Layers
Это очень красивые штаты
Это очень красивые штаты

Добавление наложений в LayersControl

Чтобы добавить базовые слои к нашему LayersControl, мы использовали LayersControl.BaseLayer для каждого базового слоя, который мы хотели добавить на карту. Это очень похожий процесс добавления желаемых наложений. Вместо LayersControl.BaseLayer мы обернем наши слои в LayersControl.Overlay указав name и установив checked равным true чтобы слои отображались на карте по умолчанию:

Layers.jsx

import { useMapEvents, TileLayer, LayersControl, GeoJSON } from 'react-leaflet'
// ... Data Imports ...

const Layers = () => {
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    // ... Map Events ...
  })

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          {// ... Layers Control: Base Layers ...}
        </LayersControl.BaseLayer>
        {borderData.map((data) => {
          const geojson = data.features[0].geometry
          const state_name = data.features[0].properties.display_name.split(',')[0]
          
          // Wrap the geojson in LayersControl.Overlay. We'll use state_name
          // as the name for the layer in the control and set checked to true:
          return (
            <>
              <LayersControl.Overlay checked name={state_name}>
                <GeoJSON key={state_name} data={geojson} pathOptions={{ color: 'blue' }} />
              </LayersControl.Overlay>
            </>
          )
        })}
      </LayersControl>
    </>
  )
}

export default Layers
Montana &amp; South Dakota
Montana & South Dakota

Добавление маркеров и всплывающих окон с LayerGroup

Допустим, мы хотим добавить больше в пользовательский интерфейс нашей карты, отображая некоторые маркеры, которые будут соответствовать geoJSON. Создать точки-маркеры невероятно просто. Мы просто импортируем компонент слоя React-Leaflet Marker, оборачиваем его компонентом слой Geojson и передаем объект latLng, который мы создадим с помощью метода Leaflet LatLng для его опоры position. Мы также импортируем компонент слоя Popup, добавим текст и обернем его компонентом Marker. Мы также можем использовать библиотеки, такие как Material UI, чтобы добавить дополнительный стиль к нашим всплывающим окнам. На данный момент мы просто будем использовать UI материала Typography и компоненты Divider:

Layers.jsx

import { 
  useMapEvents, 
  TileLayer, 
  LayersControl, 
  GeoJSON, 
  Marker, // Import the Marker and Popup layer components
  Popup 
} from 'react-leaflet'
// Import L from leaflet to create LatLng objects:
import L from 'leaflet'
// Import Material UI Components for styling:
import { Typography, Divider } from '@material-ui/core'
// ... Data Imports ...

const Layers = () => {
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    // ... Map Events ... 
  })

  // Function to provide each state's latLng:
  const getMarkerPosition = (state_name) => {
    switch (state_name) {
      case 'Montana':
        // Use L.latLng to create latLng object.
        // { "lat": LATITUDE, "lng": LONGITUDE }
        return L.latLng(46.8797, -110.3626)
      case 'Wyoming':
        return L.latLng(43.0760, -107.2903)
      case 'North Dakota':
        return L.latLng(47.5515, -101.0020)
      case 'South Dakota':
        return L.latLng(43.9695, -99.9018)
      default: return
    }
  }

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          { // ... Base Layers ... }
        </LayersControl.BaseLayer>
        {borderData.map((data) => {
          const geojson = data.features[0].geometry
          const state_name = data.features[0].properties.display_name.split(',')[0]
          
          // Wrap the Marker/Popup with the GeoJSON layer.
          // Pass the getMarkerPosition to the position prop with the state_name param.
          // Give the popup some Material UI styling with Typography and Divider:
          return (
            <>
              <LayersControl.Overlay checked name={state_name}>
                <GeoJSON key={state_name} data={geojson} pathOptions={{ color: 'blue' }}>
                  <Marker position={getMarkerPosition(state_name)}>
                    <Popup>
                      <Typography variant='subtitle2'>
                        {state_name}
                      </Typography>
                      <Divider />
                      <Typography variant='body2' style={{ margin: 3 }}>
                        Lat: {JSON.stringify(getMarkerPosition(state_name).lat)}
                      </Typography>
                      <Typography variant='body2' style={{ margin: 3 }}>
                        Lng: {JSON.stringify(getMarkerPosition(state_name).lng)}
                      </Typography>
                    </Popup>
                  </Marker>
                </GeoJSON>
              </LayersControl.Overlay>
            </>
          )
        })}
      </LayersControl>
    </>
  )
}

export default Layers
Клянусь, Монтана супер крутая.
Клянусь, Монтана супер крутая.

Обратите внимание на что-нибудь странное, особенно с LayersControl? Каждое имя отображается дважды. Это связано с тем, что LayersControl добавляет переключатель как для границы, так и для маркера точки с одним и тем же именем, чего мы не хотим. Чтобы исправить это, мы просто должны сказать, LayersControlчто компоненты GeoJson и Marker являются частью LayerGroup. Просто импортируйте компонент React-LeafletLayerGroup и используйте его как оболочку GeoJSON и Marker:

Layers.jsx

import { 
  useMapEvents, 
  TileLayer, 
  LayersControl,
  LayerGroup, // Import LayerGroup
  GeoJSON, 
  Marker, 
  Popup 
} from 'react-leaflet'
// ... Imports ... 

const Layers = () => {
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    // ... Map Events
  })

  const getMarkerPosition = (state_name) => {
    // ... Marker Position ... 
  }

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          { // ... Base Layers ... }
        </LayersControl.BaseLayer>
        {borderData.map((data) => {
          console.log(data)
          const geojson = data.features[0].geometry
          const state_name = data.features[0].properties.display_name.split(',')[0]
          
          // Wrap the GeoJSON component and its children with the LayerGroup component:
          return (
            <>
              <LayersControl.Overlay checked name={state_name}>
                <LayerGroup>
                <GeoJSON key={state_name} data={geojson} pathOptions={{ color: 'blue' }}>
                  <Marker position={getMarkerPosition(state_name)}>
                    <Popup>
                      { // ... Popup Content ... }
                    </Popup>
                  </Marker>
                </GeoJSON>
                </LayerGroup>
              </LayersControl.Overlay>
            </>
          )
        })}
      </LayersControl>
    </>
  )
}

export default Layers
Я слышал, что Канье где-то здесь живет ...
Я слышал, что Канье где-то здесь живет ...

Обработчики событий слоя

Бывают случаи, когда мы захотим изменить элементы на нашей карте, например, изменить fillOpacity для GeoJSONс событием onmouseover и снова onmouseout. Чтобы добавить эту функциональность, мы будем использовать опору eventHandlers для GeoJSON. Многие компоненты React-Leaflet имеют опору eventHandlers, но некоторые нет, поэтому обязательно обратитесь к документации. Для обработки событий наведения и вывода мыши мы настроим функцию с оператором switch, которая обрабатывает оба случая:

Layers.jsx

// ... Imports ...

const Layers = () => {
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    // ... Map Events ... 
  })

  const getMarkerPosition = (state_name) => {
    // ... Marker Position ... 
  }

  // Our mouse event handler:
  const onMouseEvent = (event, type) => {
    switch (type) {
      case 'over':
        // Set the style of the Leaflet DOM element:
        event.target.setStyle({ fillOpacity: 0.5 })
        break
      case 'out':
        event.target.setStyle({ fillOpacity: 0.0 })
        break
      default:
        break
    }
  }

  return (
    <>
      <LayersControl position="topright">
        <LayersControl.BaseLayer checked name="Basic Map">
          {// ... Base Layers ... }
        </LayersControl.BaseLayer>
        {borderData.map((data) => {
          const geojson = data.features[0].geometry
          const state_name = data.features[0].properties.display_name.split(',')[0]
          
          // We use the eventHandlers prop to tell the GeoJSON layer to listen for mouse events:
          return (
            <>
              <LayersControl.Overlay checked name={state_name}>
                <LayerGroup>
                <GeoJSON 
                  key={state_name} 
                  data={geojson} 
                  pathOptions={{ color: 'blue' }}
                  eventHandlers={{
                    mouseover: (event, type) => onMouseEvent(event, 'over'),
                    mouseout: (event, type) => onMouseEvent(event, 'out'),
                  }}
                >
                  <Marker position={getMarkerPosition(state_name)}>
                    <Popup>
                      {// ... Popup Content ... }
                    </Popup>
                  </Marker>
                </GeoJSON>
                </LayerGroup>
              </LayersControl.Overlay>
            </>
          )
        })}
      </LayersControl>
    </>
  )
}

export default Layers

В завершение

Хорошо, теперь у нас есть работающее картографическое приложение, созданное с помощью React-Leaflet версии 3! Вот финальная версия нашего кода:

Layers.jsx

import React, { useState } from 'react'
import {
  useMapEvents,
  TileLayer,
  LayersControl,
  LayerGroup,
  GeoJSON,
  Marker,
  Popup,
} from 'react-leaflet'
import L from 'leaflet'
import { Typography, Divider } from '@material-ui/core'
import WY from '../data/Wyoming.json'
import MT from '../data/Montana.json'
import ND from '../data/NorthDakota.json'
import SD from '../data/SouthDakota.json'

const Layers = () => {
  const [borderData, setBorderData] = useState([ND, MT, SD, WY])

  const map = useMapEvents({
    zoomend: () => {
      console.log(map.getZoom())
    },
    moveend: () => {
      console.log(map.getBounds())
    },
  })

  const getMarkerPosition = (state_name) => {
    switch (state_name) {
      case 'Montana':
        return L.latLng(46.8797, -110.3626)
      case 'Wyoming':
        return L.latLng(43.076, -107.2903)
      case 'North Dakota':
        return L.latLng(47.5515, -101.002)
      case 'South Dakota':
        return L.latLng(43.9695, -99.9018)
      default:
        return
    }
  }

  const onMouseEvent = (event, type) => {
    switch (type) {
      case 'over':
        event.target.setStyle({ fillOpacity: 0.5 })
        break
      case 'out':
        event.target.setStyle({ fillOpacity: 0.0 })
        break
      default:
        break
    }
  }

  return (
    <>
      <LayersControl position='topright'>
        <LayersControl.BaseLayer checked name='Basic Map'>
          <TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
          />
        </LayersControl.BaseLayer>
        <LayersControl.BaseLayer name='Topo Map'>
          <TileLayer
            attribution='Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
            url='https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png'
          />
        </LayersControl.BaseLayer>
        {borderData.map((data) => {
          const geojson = data.features[0].geometry
          const state_name = data.features[0].properties.display_name.split(
            ','
          )[0]

          return (
            <>
              <LayersControl.Overlay checked name={state_name}>
                <LayerGroup>
                  <GeoJSON
                    key={state_name}
                    data={geojson}
                    pathOptions={{ color: 'blue', fillOpacity: 0 }}
                    eventHandlers={{
                      mouseover: (event, type) => onMouseEvent(event, 'over'),
                      mouseout: (event, type) => onMouseEvent(event, 'out'),
                    }}
                  >
                    <Marker position={getMarkerPosition(state_name)}>
                      <Popup>
                        <Typography variant='subtitle2'>
                          {state_name}
                        </Typography>
                        <Divider />
                        <Typography variant='body2' style={{ margin: 3 }}>
                          Lat:{' '}
                          {JSON.stringify(getMarkerPosition(state_name).lat)}
                        </Typography>
                        <Typography variant='body2' style={{ margin: 3 }}>
                          Lng:{' '}
                          {JSON.stringify(getMarkerPosition(state_name).lng)}
                        </Typography>
                      </Popup>
                    </Marker>
                  </GeoJSON>
                </LayerGroup>
              </LayersControl.Overlay>
            </>
          )
        })}
      </LayersControl>
    </>
  )
}

export default Layers

Map.jsx

import React from 'react'
import { MapContainer, ZoomControl } from 'react-leaflet'
import Layers from './Layers'

const Map = () => {
  return (
    <>
      <MapContainer
        center={[37.0902, -95.7129]}
        zoom={3}
        zoomControl={false}
        style={{ height: '100vh', width: '100%' }}
      >
        <Layers />
        <ZoomControl position='topright' />
      </MapContainer>
    </>
  )
}

export default Map

Я рекомендую вам прочитать документацию, так как с этим пакетом вы можете сделать множество интересных вещей.

После того, как вы попрактикуетесь в использовании всех хуков и компонентов, которые может предложить общедоступный API React-Leaflet, перейдите в раздел документации Core API. Core API позволяет создавать настраиваемые компоненты Leaflet и расширять сторонние плагины Leaflet для работы с React-Leaflet.

Источник:

#JavaScript #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

Поделитесь своим опытом, расскажите о новом инструменте, библиотеке или фреймворке. Для этого не обязательно становится постоянным автором.

Попробовать

Оплатив хостинг 25$ в подарок вы получите 100$ на счет

Получить