Cómo crear mapas con React y Leaflet

Publicado: 2022-03-10
Resumen rápido ↬ Leaflet es una herramienta muy poderosa y podemos crear muchos tipos diferentes de mapas. Este tutorial lo ayudará a comprender cómo crear un mapa avanzado con la ayuda de React y Vanilla JS.

Obtener información de un archivo CSV o JSON no solo es complicado, sino también tedioso. Representar los mismos datos en forma de ayuda visual es más sencillo. En este artículo, representaremos en un mapa las ubicaciones de los incidentes de incendios no médicos a los que respondió el Departamento de Bomberos de SF.

Para este tutorial, utilizaremos las siguientes herramientas:

  • Folleto
    Una biblioteca de JavaScript para mapas interactivos
  • Reaccionar
    Una biblioteca de JavaScript para crear interfaces de usuario
  • React-Folleto
    Reaccionar componentes para mapas de folletos

¿Qué es el folleto?

Con alrededor de 27 000 estrellas, Leaflet.js es una de las principales bibliotecas JavaScript de código abierto para mapas interactivos aptos para dispositivos móviles. Aprovecha HTML5 y CSS3 en los navegadores modernos y también es accesible en los más antiguos. En general, es compatible con todas las plataformas principales de escritorio y móviles.

El folleto pesa alrededor de 38 KB y funciona perfectamente para cosas básicas. Para extensiones adicionales, se puede ampliar con una gran cantidad de complementos.

Muchos periódicos, incluidos NPR, Washington Post, Boston Globe, entre otros, y otras organizaciones utilizan Leaflet para sus proyectos de datos en profundidad.

El San Francisco Chronicle, por ejemplo, realizó un proyecto llamado California Fire tracker, un mapa interactivo que brinda información sobre los incendios forestales que arden en California, utilizando Leaflet. No solo señalaron el origen del fuego, sino que también nos mostraron la trayectoria del mismo.

Dado que este es un tutorial introductorio, solo marcaremos las ubicaciones de los incidentes de incendio y mostraremos algunos detalles al respecto.

Antes de saltar a React, comprendamos los conceptos básicos de Leaflet. Para esto, crearemos un ejemplo simple en el que configuraremos un mapa de folleto, trabajaremos con marcadores y ventanas emergentes.

¡Más después del salto! Continúe leyendo a continuación ↓

Primero, creemos archivos index.html y app.js en nuestra carpeta /project y vinculemos este último a nuestro archivo index.html .

Para comenzar a usar Leaflet, debemos vincular Leaflet CSS y Leaflet JS en nuestras etiquetas de encabezado. Una cosa a tener en cuenta es que Leaflet CSS viene antes que Leaflet JS. Eso es todo para Folleto.

Hay una cosa más que debemos agregar a nuestro archivo index.html : un contenedor que contendrá nuestro mapa.

 <div></div>

Antes de que nos olvidemos, demos altura a nuestro div.

 #mapid { height: 1000px; }

Ahora viene la parte divertida. Ya sea que decida crear un nuevo archivo JavaScript o continuar con las etiquetas de script, asegúrese de <div id="mapid"> al dom antes de llamar a L.map('mapid') .

Probablemente te estés preguntando "Pero, ¿por qué?" Bueno, es porque le dará un error si vincula el mapa a un contenedor que aún no existe.

 Uncaught Error: Map container not found

Crear un mapa

Ahora, a la parte divertida. Para inicializar el mapa, pasamos nuestro div a L.map() con algunas opciones.

 const myMap = L.map('mapid', { center: [37.7749, -122.4194], zoom: 13 })

Vayamos paso a paso para entender lo que acaba de pasar. Usamos la clase Map de Leaflet API para crear un mapa en la página. Pasamos dos parámetros a esta clase:

  1. Pasamos una variable de cadena que representa el DOM ID
  2. Un literal de objeto opcional con opciones de mapa

Hay muchas opciones que podríamos pasar a nuestra clase, pero las dos opciones principales son el centro y el zoom. El centro define un centro geográfico inicial del mapa, mientras que el zoom especifica un nivel de zoom del mapa inicial. Ambos están indefinidos por defecto.

Para el centro, pasamos en las coordenadas de San Francisco. Hay muchos lugares donde podemos realizar la codificación geográfica directa e inversa, pero para una búsqueda básica como esta, podemos buscarla en Google.

Por lo general, el valor del zoom dependerá de lo que desee mostrar. ¿Quieres mostrar una ciudad o un estado? ¿País o un continente? Continúe y juegue con el valor del zoom para tener una mejor idea. Para este ejemplo, elegimos 13 porque muestra toda la ciudad.

Otra forma de inicializar el mapa es usando setView(). Toma el en una matriz de coordenadas y un número entero para el nivel de zoom.

 const myMap = L.map('map').setView([37.7749, -122.4194], 13);

De forma predeterminada, todas las interacciones táctiles y del mouse en el mapa están habilitadas y tiene controles de zoom y atribución.

Crear una capa

A continuación, agregaremos una capa de teselas a nuestro mapa; en nuestro caso, es una capa de teselas Mapbox Streets. Podemos agregar varios tipos de capas de teselas instanciando la clase TileLayer.

Para crear una capa de mosaico, debemos configurar la plantilla de URL para la imagen del mosaico, el texto de atribución y el nivel de zoom máximo de la capa. La plantilla de URL es lo que nos da acceso a la capa de teselas deseada del proveedor de servicios. Dado que estamos utilizando la API de mosaicos estáticos de Mapbox, necesitaremos solicitar un token de acceso.

 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);

En este punto, si abrimos nuestro index.html en un navegador, deberíamos poder ver un mapa de San Francisco. Pongamos una chincheta en el mapa.

Marcadores y círculos

Tenemos el mapa y la capa, pero no nos señala nada específico. Para señalar una ubicación particular en el mapa, Leaflet nos proporciona marcadores.

Para fijar una ubicación, instanciamos el marcador usando la clase Marker, pasamos las coordenadas y lo agregamos al mapa. Aquí estamos usando las coordenadas de Twin Peaks en la ciudad.

 const marker = L.marker([37.7544, -122.4477]).addTo(mymap);

De manera similar, podemos vincular un círculo al mapa usando una clase Circle . Pasamos algunas opciones opcionales, como radio, color, etc. Para el marcador circle , estamos pasando en las coordenadas del faro de Point Bonita.

 const circle = L.circle([37.8157, -122.5295], { color: 'gold', fillColor: '#f03', fillOpacity: 0.5, radius: 200 }).addTo(mymap);

ventanas emergentes

Todo esto es genial, pero ¿qué pasa si queremos pasar más información sobre la ubicación? Hacemos esto usando popup.

 circle.bindPopup("I am pointing to Point Bonita Lighthouse"); marker.bindPopup("I am pointing to Twin Peaks");

El método bindPopup toma un contenido HTML específico y lo agrega al marcador, de modo que la ventana emergente aparece cuando hace clic en el marcador.

React-Folleto

Ahora sabemos cómo crear un mapa y agregar marcadores usando Leaflet y Vanilla JavaScript. Veamos cómo podemos lograr los mismos resultados con React. No vamos a hacer la misma aplicación, sino que vamos a hacer una aplicación avanzada.

La primera tarea para nosotros es obtener un token de acceso del portal de datos abiertos de San Francisco. Es un portal en línea donde podemos encontrar cientos de conjuntos de datos de la ciudad y el condado de San Francisco. Decidí usar este recurso, pero hay muchos otros recursos que podemos usar en su lugar.

Acceder a la clave API

  1. Cree una cuenta e inicie sesión en el portal.
  2. Haga clic en el enlace administrar hacia la parte inferior derecha.
  3. Haga clic en Crear nueva clave API y asígnele un nombre.
  4. Copie su Key ID y Key Secret. Necesitaría esto para acceder a los datos.

Para esto, usaremos React-Leaflet: componentes de reacción para mapas de folletos. Vamos a crear una aplicación de reacción.

 npx create-react-app react-fire-incidents cd react-fire-incidents

Luego, instalemos react-leaflet y Leaflet ejecutando el siguiente comando en nuestra terminal:

 npm install react-leaflet leaflet

Aplicación.js

Vamos a crear una carpeta /components dentro de src . Dentro de los componentes, creemos un archivo llamado Map.js. Aquí es donde vivirá nuestro Mapa. Ahora editemos App.js eliminando código innecesario e importando módulos de react-leaflet axios recién creado.

 import React, { Component, Fragment } from 'react'; import axios from 'axios'; import Map from './components/Map'

En nuestra clase de aplicación, vamos a definir una matriz en nuestro estado llamada incidentes: cuando se cargue la página, insertaremos nuestros datos en esta matriz.

 class App extends Component { state = { incidents: [], } render() { return ( <div> </div> ); } } export default App;

A continuación, haremos una solicitud GET cuando se monte el componente. Tenemos el token de la aplicación, pero aún necesitamos un punto final. ¿Dónde encontramos el punto final?

Vayamos al portal y hagamos clic en Examinar datos. En la barra de búsqueda, busquemos incidentes de incendio. El primer resultado que aparece es lo que estamos buscando. Una vez que hacemos clic en el enlace, podemos obtener la URL haciendo clic en el botón API en la parte superior derecha.

Pasaremos el punto final a nuestra solicitud GET y pasaremos un límite y nuestro token de aplicación como parámetros. Los datos originales tienen miles de registros, pero para simplificar las cosas, los hemos limitado a 500. Actualizamos nuestra matriz de incidentes con nuestros resultados.

Una vez que obtenemos los datos, actualizamos nuestro estado.

 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 }); };

Así es como debería verse nuestro 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;

Mapa.js

Como ya sabemos cómo crear un mapa de folleto, esta parte será relativamente fácil. Importaremos los componentes Map , TileLayer , Marker , Popup desde react-leaflet .

 import React, { Component } from 'react' import { Map, TileLayer, Marker, Popup } from 'react-leaflet'

Si recordamos el ejemplo anterior, necesitamos coordenadas y un nivel de zoom para inicializar el mapa. En nuestra clase Map , los definimos en nuestro estado usando las variables lat , lng y zoom .

 export default class Map extends Component { state = { lat: 37.7749, lng: -122.4194, zoom: 13, } render() { return ( <div></div> ) } }

Luego comprobaremos si nuestra matriz de incidentes está vacía. Si está vacío, devolveremos un mensaje que dice "Los datos se están cargando"; de lo contrario, devolveremos un mapa.

En el componente Map de nuestro react-leaflet , pasaremos las coordenadas del centro y un nivel de zoom junto con algo de estilo. En nuestro componente TileLayer , pasaremos la atribución y la URL de manera similar a nuestro ejemplo anterior.

 render() { return ( this.props.incidents ? <Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}} > <TileLayer attribution='&copy <a href="https://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> </Map> : 'Data is loading...' ) } }

A continuación, recorremos nuestro props.incident y pasamos las coordenadas de cada incidente al componente Marker. Dado que React nos advierte que pasemos una clave a cada elemento de una matriz, también pasaremos una clave a Marker.

Dentro del componente Marker , pasamos un componente Popup . He agregado información sobre el incidente dentro de la ventana emergente.

 <Map center={[this.state.lat, this.state.lng]} zoom={this.state.zoom} style={{ width: '100%', height: '900px'}}> <TileLayer attribution='&copy <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>

Y esto es todo. Si ejecutamos nuestra aplicación y todo salió bien, deberíamos poder ver un mapa de San Francisco con 500 marcadores que nos indiquen las ubicaciones de los incidentes de incendio. Si hacemos clic en uno de esos marcadores, aparecerá una ventana emergente con más información sobre el incidente.

Terminando

Aunque cubrimos mucho, esto fue solo lo básico. Leaflet es una herramienta muy poderosa, y podemos crear muchos tipos diferentes de mapas. Si quiere jugar, intente agregar otra capa o un icono personalizado. O tal vez le gustaría crear un mapa de coropletas interactivo.