React con Leaflet y acceso a API Aemet y API OpenDataSoft
En este blog vamos a ver cómo implementar un mapa con la librería Leaflet, accediendo a la Api opendatasoft para poder localizar mediante coordenadas las diferentes provincias y municipios de España. Además, accederemos a la Api de Aemet para acceder al tiempo de cada zona interesada. La tecnología que se va a utilizar es React.
Instalación de dependencias Leaflet
Para comenzar vamos a crear un proyecto nuevo con el comando npx create-react-app mapaleaflet. Una vez tengamos el proyecto inyectaremos las dependencias de las librerías que vamos a utilizar:
- npm install -s react react-dom leaflet
- npm install -s react-leaflet
Como leaflet tiene su propio diseño, tenemos que añadir también el su css.
- npm i leaflet-css
Leaflet trabaja con types por lo que tenemos que instalar la libreria:
- npm install -D @types/leaflet
Uso de los componentes
Leaflet se encarga de representar las capas del mapa, mientras que React renderiza el componente <MapContainer></MapContainer> junto con todo el contenido que engloba la creación del mapa, el cual se sitúa dentro de dichas etiquetas.
Es importante saber que los mapas son un proyecto colaborativo y están bajo una licencia abierta, por lo tanto la marca de agua debe estar presente siempre. Las url donde podemos encontrar los diferentes diseños de los mapas es https://geopois.com/blog/leaflet/leaflet-tiles. Dicha url, para poder visualizar el nuestro, debemos incluirla como parámetro del componente <TileLayer>.
<TileLayer attribution=’©
<a href=»http://osm.org/copyright»>OpenStreetMap</a>
contributors’ url=»https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png» />
El componente MapContainer requiere de ciertos parámetros para poder ajustar el foco inicial (coordenadas) y el zoom, entre otros.
MapComponent.js
Primero importamos los componentes que vamos a utilizar y el css de leaflet:
import { MapContainer, Marker, Popup, TileLayer } from ‘react-leaflet’;
import ‘leaflet-css/dist/leaflet.css’;
//creamos una constante con las coordenadas por defecto
const coordenadas=[51.505, -0.09];
render(){
return(
<MapContainer
center={coordenadas}
zoom=’7′
scrollWheelZoom={true}>
// si queremos que al hacer scroll haga zoom
<TileLayer
attribution=’© <a href=»http://osm.org/copyright»>OpenStreetMap</a> contributors’
url=»https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png»
// a medida que cambian las coordenadas y el zoom, los parámetros {z}, {x} e {y} cambian para mostrarlos la parte del mapa que nos interesa
/>
</MapContainer>
)
Una vez que tenemos la instancia del mapa podremos visualizarlo. Lo que toca ahora es darle vida. Otros de los componentes que se pueden utilizar dentro del MapContainer son, por ejemplo, los <Marker> y el <Popup>.
¿Qué hace cada uno?
Marker es el icono que nos indica un punto concreto en el mapa.
<Marker
position={coordenadas} icon={Iconlocation}
>
El parámetro icon recibe un objeto, dicho objeto tendrá las propiedades de L.icon. De esta manera podemos personalizar el icono e incluso utilizar otros que no vengan por defecto con leaflet.
Si queremos utilizar varios iconos, creamos tantos objetos como deseemos. La creación del mismo quedaría asi:
IconLocation.js
Primero importamos el objeto L de leaflet para poder crear el icono y el .png o .jpg que vayamos a utilizar.
import L from ‘leaflet’;
import icon from ‘./assets/icono.png’;
const IconLocation=L.icon({
iconUrl: icon,
iconSize: new L.Point(60,75)
})
export default IconLocation;
Existen multitud de propiedades que podemos modificar a gusto.
Volviendo a nuestro componente MapComponent.js, seguimos analizando el resto de componentes que vamos a utilizar para trabajar con nuestro mapa.
MapComponent.js
El componente <Popup> nos permite mostrar una tarjeta informativa cada vez que hacemos clic en un marcador en el mapa. Podemos mostrar toda la información que deseemos, en este proyecto vamos a mostrar: la provincia, el municipio, las coordenadas y la temperatura máxima y mínima.
¿cómo se utiliza?
Simplemente vamos a introducir dentro de la etiqueta que abre y la que cierra, todo aquello que deseemos.
<Popup> .. Nuestro diseño de tarjeta .. </Popup>
Acceso a Api OpenDataSoft
Para buscar información en el mapa vamos a crear un formulario con un select. Este va a contener options con el nombre del municipio y como value la posición en el array.
Form.js
OpendataSoft nos va a devolver un array de objetos, cada objeto va a ser un municipio. Dentro de cada objeto vamos a tener información del tipo: coordenadas, provincia, comunidad autónoma etc.
Para acceder al servicio y poder disponer de esta información utilizamos Axios.
*Es buena práctica tener un documento Global.js para guardar en un objeto todas las url que vamos a utilizar.
Según cargamos el componente vamos a llamar al método que accede al servicio:
componentDidMount=()=>{
this.listarMunicipios();
}
listarMunicipios=()=>{
axios.get(urlOpendata).then(res=>{
//res.data nos devuelve la información de la imagen de arriba
this.setState({
municipios: res.data.records // si observamos en la imagen de arriba, records nos devuelve todos los objetos de municipio, por lo que estamos guardando un array de objetos
})
})
}
Lo que tenemos que hacer ahora es pintar cada uno de los municipios. Para ello, nos dirigimos al render() y recorremos this.state.municipios.
quedaría asi:
this.state.municipios.map((municipio, i)=>{
return(
<option value={i} >{municipio.info.fields.municipio}</option>
)
})
Una vez tenemos impreso el select en el html, podemos realizar la búsqueda y así poder mostrar en el mapa un Marker.
Como tenemos en el state.comunidades el array de objetos de la búsqueda, vamos a utilizarlo para no tener que realizar otra petición al servicio. Al disponer de la posición en el array (el value del option es la posición) vamos a acceder a la información concreta de dicho array con ayuda del índice.
index= React.createRef(); // referencia al select seleccionado del formulario de búsqueda
buscarMunicipio(e)=>{
e.preventDefault();
let objMunicipio= this.state.municipio[index]
// guardamos del objeto la información que vayamos a utilizar
this.setState({
coordenadas: {
lat: objMunicipio.geometry.coordinates[1],
lng: objMunicipio.geometry.coordinates[0]
},
provincia: objMunicipio.fields.provincia,
municipio: objMunicipio.fields.municipio,
cp: objMunicipio.fields.codigo_postal,
})
}
Una vez que tenemos en el state todo lo que necesitamos, importamos el componente MapComponent y le enviamos cada una de las propiedades para poder reflejarlas en el mapa.
esto quedaría así:
<Mapcomponent provincia={this.state.provincia} cp={this.state.cp} municipio={this.state.municipio} coordenadas={this.state.coordenadas} />
MapComponent.js
Desde MapComponent vamos a recibir mediante props los parámetros anteriores.
constructor(props) {
super(props);
this.state = {
coordenadas: props.coordenadas,
zoom: 7,
cp: props.cp
}
Vamos colocando cada parámetro en su lugar dentro de<MapContainer>
<MapContainer center={this.props.coordenadas} zoom={this.state.zoom}>
<TileLayer
attribution=’© <a href=»http://osm.org/copyright»>OpenStreetMap</a> contributors’
url=»https://dev.{s}.tile.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png»
/>
<Marker
position={this.props.coordenadas}
icon={Iconlocation}
>
<Popup>
<div className=»card-body»>
<h5 className=»card-title»>Provincia: {this.props.provincia} </h5>
<h5 className=»card-title»>Municipio: {this.props.municipio}</h5>
<p className=»»>Lat: {this.props.coordenadas.lat}</p>
<p>Long: {this.props.coordenadas.lng}</p>
<h6>El tiempo</h6>
<Aemet cp={this.props.cp} />
</div>
</Popup>
</Marker>
</MapContainer>
Acceso a Api Aemet
Para poder cargar la temperatura en nuestro <Popup> vamos a acceder a Aemet con Axios.
Lo que necesitamos es el cp del municipio seleccionado, este lo obtenemos mediante los props que hemos recibido.
Creamos un componente nuevo, lo llamaremos Aemet y recibirá el cp.
Vamos a añadirlo dentro de popup.
<MapContainer>
<Popup>
<Aemet cp={this.props.cp} />
Aemet.js
Según se crea el componente vamos a realizar la búsqueda:
componentDidMount=()=>{
this.tiempoMunicipio();
}
tiempoMunicipio=()=>{
axios.get(urlAemet).then(res=>{
// res.data = https://opendata.aemet.es/opendata/sh/cfaea297
//la primera petición devuelve una URL del servicio al que vamos a acceder
return res.data;
}).then(res =>{
axios.get(res).then(res=>{
// nos devuelve la información de la imagen de arriba, ahora queremos acceder a la temperatura
var resultado = res.data[0].prediccion.dia[0].temperatura;
this.setState({
status: true,
temperatura: { max: resultado .maxima, min: resultado .minima }
})
})
})
}
Como lo hemos guardado en el state, vamos a pintarlo en el render para que sea visible el resultado desde el componente MapComponet.
render(){
return(
<h6>
Máximas: {this.state.temperatura.max}º,
Mínimas: {this.state.temperatura.min}º
</h6>
)
}
Resultado
Autor: Carlota Lobo Sanz
Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma
Centro: Tajamar
Año académico: 2020-2021
GitHub: https://github.com/carlotaLobo/REACT-POST-LEAFLET.git
URL SERVICIOS:
- urlMap: https://public.opendatasoft.com/api/records/1.0/search/?dataset=espana-municipios&q=&rows=50&facet=provincia’, urlMinucipios: ‘https://public.opendatasoft.com/api/records/1.0/search/?dataset=espana-municipios&q=&rows=8&facet=municipio
- urlAemet: https://opendata.aemet.es/opendata/