Capítulo32 Leaflet: Mapas interactivos
## [1] "2026-10-08"
32.1 Temas
- Qué hace distinto a un mapa interactivo
- Las tres piezas:
leaflet(),addTiles()y una capa de marcadores - Latitud y longitud: cuál va primero y qué pasa si te equivocas
- Marcadores: círculos, iconos y agrupamiento
- Color por categoría y por valor, y la leyenda
- Ventanas emergentes y etiquetas
- Capas que se encienden y se apagan
32.2 Por qué un mapa interactivo
Una gráfica de puntos con la longitud en un eje y la latitud en el otro ya es un
mapa, y para muchas cosas basta. Lo que añade leaflet es lo que no cabe en una
imagen fija:
- el lector puede acercarse a una zona concreta sin que tú decidas por él cuál;
- cada punto puede llevar su propia ventana emergente con los datos de esa observación;
- el fondo es un mapa de verdad, con calles y nombres, así que los puntos se sitúan solos sin que haya que dibujar la costa;
- se pueden encender y apagar capas, y comparar dos conjuntos en el mismo mapa.
Un mapa de leaflet es HTML: solo funciona en la salida HTML. Si compilas el
documento a PDF o a Word, el mapa no aparece. Por eso varios bloques de este
capítulo llevan eval=knitr::is_html_output(). Para un artículo impreso hace falta
un mapa estático, con ggplot2 y sf, o una captura de pantalla.
Las tres piezas de un mapa: siempre las mismas, en este orden.
leaflet(datos): crea el mapa vacío y le dice de dónde salen los datos.addTiles(): pone el fondo. Por defecto usa OpenStreetMap; conaddProviderTiles()se puede escoger otro (satélite, relieve, blanco y negro).- Una capa de marcadores:
addMarkers()(el alfiler clásico),addCircleMarkers()(círculos, que se pueden colorear y dimensionar),addAwesomeMarkers()(iconos). - Se encadenan con
%>%, igual que las capas deggplotse suman con+.
El orden de latitud y longitud es la fuente número uno de mapas mal hechos. La
latitud va del sur al norte (Puerto Rico está alrededor de 18) y la longitud de
oeste a este (Puerto Rico está alrededor de menos 66). En leaflet los argumentos
se llaman lat y lng, así que escríbelos siempre con su nombre y nunca por
posición. Si tus puntos aparecen en el océano Índico o en la Antártida, casi
seguro los cambiaste de sitio.
32.3 Bajar los datos de la web del USGS
Website: https://earthquake.usgs.gov/earthquakes/feed/v1.0/csv.php
Este capítulo descarga datos en vivo desde internet. Si al ejecutar ves un error como Error in download.file(...): cannot open URL o un HTTP error, casi siempre significa que no hay conexión a internet o que el servidor no está respondiendo en ese momento. Verifica tu conexión y vuelve a intentar. En este libro el código usa una copia local guardada en Datos/ cuando la descarga falla, para que el capítulo siempre se pueda generar. Encontrarás más errores frecuentes y cómo resolverlos en el capítulo Errores comunes (14).
library(data.table)
# Intenta bajar los datos en vivo del USGS; si no hay conexión o el servidor
# no responde, usa la copia local guardada en Datos/.
earthquakes <- tryCatch(
fread("https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv"),
error = function(e) readr::read_csv("Datos/Earthquake_all_month.csv")
)
head(earthquakes, n=10)## time latitude longitude depth mag magType nst gap
## <POSc> <num> <num> <num> <num> <char> <int> <int>
## 1: 2026-10-08 15:14:10 63.09800 -150.8460 129.10 2.20 ml 49 49
## 2: 2026-10-08 15:07:42 19.24533 -155.5248 2.07 1.83 md 29 63
## 3: 2026-10-08 15:03:57 38.81533 -122.7950 2.55 1.38 md 32 38
## 4: 2026-10-08 15:03:00 19.21717 -155.3148 49.66 1.90 md 10 195
## 5: 2026-10-08 15:00:22 38.98817 -111.4163 -3.37 1.34 md 6 136
## 6: 2026-10-08 14:58:03 19.20633 -155.3215 47.57 1.85 md 31 170
## 7: 2026-10-08 14:57:42 38.76217 -122.9297 5.95 1.23 md 18 114
## 8: 2026-10-08 14:31:58 65.11000 -145.2470 0.30 1.90 ml 22 64
## 9: 2026-10-08 14:30:04 34.10100 -117.4827 6.48 1.06 ml 33 53
## 10: 2026-10-08 14:27:10 60.42000 -147.4430 17.80 1.70 ml 26 78
## dmin rms net id updated
## <num> <num> <char> <char> <POSc>
## 1: 0.600000 0.50 ak aka2026tyiptd 2026-10-08 15:17:19
## 2: 0.024600 0.34 hv hv75052242 2026-10-08 15:11:08
## 3: 0.007359 0.02 nc nc75446441 2026-10-08 15:37:21
## 4: 0.080690 0.12 hv hv75052232 2026-10-08 15:06:22
## 5: 0.045720 0.23 uu uu80158811 2026-10-08 15:07:32
## 6: 0.078220 0.12 hv hv75052227 2026-10-08 15:01:36
## 7: 0.065350 0.03 nc nc75446436 2026-10-08 14:59:19
## 8: 0.400000 0.90 ak aka2026tyhfes 2026-10-08 14:33:29
## 9: 0.052190 0.18 ci ci41345047 2026-10-08 14:33:26
## 10: 0.400000 0.70 ak aka2026tyhbak 2026-10-08 14:28:49
## place type horizontalError
## <char> <char> <num>
## 1: 66 km SE of Denali National Park, Alaska earthquake 3.10
## 2: 6 km NW of Pāhala, Hawaii earthquake 0.53
## 3: 5 km NW of The Geysers, CA earthquake 0.20
## 4: 17 km E of Pāhala, Hawaii earthquake 2.94
## 5: 16 km WNW of Emery, Utah earthquake 1.89
## 6: 16 km E of Pāhala, Hawaii earthquake 0.55
## 7: 9 km ESE of Cloverdale, CA earthquake 0.36
## 8: 55 km SSW of Central, Alaska earthquake 3.00
## 9: 2 km W of Fontana, CA earthquake 0.27
## 10: 50 km NE of Chenega, Alaska earthquake 3.00
## depthError magError magNst status locationSource magSource
## <num> <num> <int> <char> <char> <char>
## 1: 3.2352 0.2000000 27 automatic ak ak
## 2: 0.8000 0.1597319 10 automatic hv hv
## 3: 0.3500 0.1700000 31 automatic nc nc
## 4: 1.3800 0.1248827 13 automatic hv hv
## 5: 2.4800 0.3077691 6 reviewed uu uu
## 6: 0.6100 0.1246410 10 automatic hv hv
## 7: 1.3800 0.1700000 18 automatic nc nc
## 8: 2.6427 0.1000000 10 automatic ak ak
## 9: 0.5200 0.3378574 27 automatic ci ci
## 10: 1.9803 0.2000000 12 automatic ak ak
## [1] "earthquake" "explosion" "quarry blast" "ice quake"
## [5] "mining explosion" "landslide"
## Empty data.table (0 rows and 22 cols): time,latitude,longitude,depth,mag,magType...
32.4 Si bajo los datos a mi computadora
Use esta alternativa si bajo los datos a su computadora
#library(readr)
#Earthquake_all_month <- read_csv("Datos/Earthquake_all_month.csv")
#EQ=Earthquake_all_month
#head(EQ, n=10)## [1] "time" "latitude" "longitude" "depth"
## [5] "mag" "magType" "nst" "gap"
## [9] "dmin" "rms" "net" "id"
## [13] "updated" "place" "type" "horizontalError"
## [17] "depthError" "magError" "magNst" "status"
## [21] "locationSource" "magSource"
## Classes 'data.table' and 'data.frame': 10619 obs. of 22 variables:
## $ time : POSIXct, format: "2026-10-08 15:14:10" "2026-10-08 15:07:42" ...
## $ latitude : num 63.1 19.2 38.8 19.2 39 ...
## $ longitude : num -151 -156 -123 -155 -111 ...
## $ depth : num 129.1 2.07 2.55 49.66 -3.37 ...
## $ mag : num 2.2 1.83 1.38 1.9 1.34 1.85 1.23 1.9 1.06 1.7 ...
## $ magType : chr "ml" "md" "md" "md" ...
## $ nst : int 49 29 32 10 6 31 18 22 33 26 ...
## $ gap : int 49 63 38 195 136 170 114 64 53 78 ...
## $ dmin : num 0.6 0.0246 0.00736 0.08069 0.04572 ...
## $ rms : num 0.5 0.34 0.02 0.12 0.23 ...
## $ net : chr "ak" "hv" "nc" "hv" ...
## $ id : chr "aka2026tyiptd" "hv75052242" "nc75446441" "hv75052232" ...
## $ updated : POSIXct, format: "2026-10-08 15:17:19" "2026-10-08 15:11:08" ...
## $ place : chr "66 km SE of Denali National Park, Alaska" "6 km NW of Pāhala, Hawaii" "5 km NW of The Geysers, CA" "17 km E of Pāhala, Hawaii" ...
## $ type : chr "earthquake" "earthquake" "earthquake" "earthquake" ...
## $ horizontalError: num 3.1 0.53 0.2 2.94 1.89 0.55 0.36 3 0.27 3 ...
## $ depthError : num 3.24 0.8 0.35 1.38 2.48 ...
## $ magError : num 0.2 0.16 0.17 0.125 0.308 ...
## $ magNst : int 27 10 31 13 6 10 18 10 27 12 ...
## $ status : chr "automatic" "automatic" "automatic" "automatic" ...
## $ locationSource : chr "ak" "hv" "nc" "hv" ...
## $ magSource : chr "ak" "hv" "nc" "hv" ...
## - attr(*, ".internal.selfref")=<pointer: 0x105ee7da0>
32.5 Importación de Paquetes y Datos
Seleccionar un subgrupo de los datos, solamente los primeros 100 datos
## [1] 10619
## time latitude longitude depth mag magType nst gap
## <POSc> <num> <num> <num> <num> <char> <int> <int>
## 1: 2026-10-08 15:14:10 63.09800 -150.8460 129.10 2.20 ml 49 49
## 2: 2026-10-08 15:07:42 19.24533 -155.5248 2.07 1.83 md 29 63
## 3: 2026-10-08 15:03:57 38.81533 -122.7950 2.55 1.38 md 32 38
## 4: 2026-10-08 15:03:00 19.21717 -155.3148 49.66 1.90 md 10 195
## 5: 2026-10-08 15:00:22 38.98817 -111.4163 -3.37 1.34 md 6 136
## 6: 2026-10-08 14:58:03 19.20633 -155.3215 47.57 1.85 md 31 170
## dmin rms net id updated
## <num> <num> <char> <char> <POSc>
## 1: 0.600000 0.50 ak aka2026tyiptd 2026-10-08 15:17:19
## 2: 0.024600 0.34 hv hv75052242 2026-10-08 15:11:08
## 3: 0.007359 0.02 nc nc75446441 2026-10-08 15:37:21
## 4: 0.080690 0.12 hv hv75052232 2026-10-08 15:06:22
## 5: 0.045720 0.23 uu uu80158811 2026-10-08 15:07:32
## 6: 0.078220 0.12 hv hv75052227 2026-10-08 15:01:36
## place type horizontalError
## <char> <char> <num>
## 1: 66 km SE of Denali National Park, Alaska earthquake 3.10
## 2: 6 km NW of Pāhala, Hawaii earthquake 0.53
## 3: 5 km NW of The Geysers, CA earthquake 0.20
## 4: 17 km E of Pāhala, Hawaii earthquake 2.94
## 5: 16 km WNW of Emery, Utah earthquake 1.89
## 6: 16 km E of Pāhala, Hawaii earthquake 0.55
## depthError magError magNst status locationSource magSource
## <num> <num> <int> <char> <char> <char>
## 1: 3.2352 0.2000000 27 automatic ak ak
## 2: 0.8000 0.1597319 10 automatic hv hv
## 3: 0.3500 0.1700000 31 automatic nc nc
## 4: 1.3800 0.1248827 13 automatic hv hv
## 5: 2.4800 0.3077691 6 reviewed uu uu
## 6: 0.6100 0.1246410 10 automatic hv hv
32.6 Mapa básico con Leaflet
Leaflet es una biblioteca de JavaScript para mapas interactivos, creada por Volodymyr Agafonkin en 2011. Su ventaja es que es ligera y funciona en cualquier navegador. RStudio/Posit creó el paquete leaflet para R en 2015, permitiendo construir estos mapas sin escribir JavaScript. Los mapas base (“tiles”) provienen de OpenStreetMap, un proyecto colaborativo y abierto de cartografía mundial.
leaflet(): construye un mapa interactivo por capas, de forma parecida a ggplot(), pero uniendo las capas con el pipe (%>%).
leaflet(data): inicia el mapa con un conjunto de datos.addTiles(): añade el mapa base (por defecto, de OpenStreetMap).addMarkers()/addCircleMarkers(): colocan puntos;~longitudey~latitude(la tilde~) indican qué columnas usar para las coordenadas, ypopupel texto que aparece al hacer clic.
## [1] "time" "latitude" "longitude" "depth"
## [5] "mag" "magType" "nst" "gap"
## [9] "dmin" "rms" "net" "id"
## [13] "updated" "place" "type" "horizontalError"
## [17] "depthError" "magError" "magNst" "status"
## [21] "locationSource" "magSource"
leaflet(EQ_100) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitude,
lat = ~latitude,
popup = ~mag,
color= ~(type))#color= ~(type),
32.7 Usando la paletas de color de viridis
https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html
Evaluar las siguientes paletas de colores:
- viridis
- inferno
- plasma
- magma
- cividis
- rocket
- turbo
Pasos
- Seleccionar los datos en las áreas de PR, latitud y longitud
- Crear una paleta de colores/ o usar viridis
- Crear un mapa con los datos de los terremotos
## [1] "time" "latitude" "longitude" "depth"
## [5] "mag" "magType" "nst" "gap"
## [9] "dmin" "rms" "net" "id"
## [13] "updated" "place" "type" "horizontalError"
## [17] "depthError" "magError" "magNst" "status"
## [21] "locationSource" "magSource"
## [1] "earthquake"
pal = colorFactor(palette = "magma", domain = NULL)
leaflet(EQ_100) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitude,
lat = ~latitude,
color= ~pal(mag),
popup = ~paste("Magnitud", mag)
)32.8 Trabajando con datos de iNaturalist
32.8.1 Ahora crea un mapa de los terremotos alrededor de Puerto Rico
Obtener los datos de observaciones de una especie en iNaturalist desde R:
Los datos a utilizar se obtendrán desde iNaturalist, una aplicación con base en la ciencia ciudadana que mantiene un registro de ocurrencias de especies.
La función get_inat_obs() permite acceder a los datos disponibles en iNaturalist, especificando los siguientes parámetros:
get_inat_obs() consulta la API de iNaturalist en tiempo real: requiere conexión a internet y el número de observaciones puede variar entre ejecuciones a medida que la comunidad añade registros.
taxon_name: La especies de interés, siempre escrita entre comillas (“). Puede utilizar nombre común o científico.quality: Siendo una aplicación basada en ciencia ciudadana, la calidad de algunos datos podría ser baja, debido a que les falta información - como nombre científico o ubicación - o que contienen información errónea - especies mal identificadas-. Colocando'research'en este parámetro se especifica que se desea obtener sólo aquellas observaciones con alta calidad - que tengan información completa y cuya identificación haya sido confirmada por varias personas de la comunidad -.geo: Al igual que el parámetroquality, este parámetro cuyo valor es lógico (TRUEoFALSE), es para especificar que se quiere obtener sólo los datos que están georeferenciados (cuando se coloca comoTRUE).maxresults: A través del API (Interfaz de Programación de Aplicaciones) de iNaturalist se puede acceder a un máximo de 10,000 observaciones en una sola búsqueda. Con este parámetro, cuyo valor debe ser numérico, se especifica cuántas observaciones se desea obtener, y no se debe exceder de 10,000 por búsqueda.bounds: Un vector que contiene los límites geográficos de la búsqueda. Se puede insertar un cuadro delimitador ya especificado en un objeto, concatenar los valores del vector, o insertar un objeto de características simples (Simple Feature, sf). El argumentoboundses importante porque si obtenemos datos de iNaturalist, las únicas columnas que obtenemos con información acerca de la región o área donde se hicieron las observaciones son:
place_guess: Contiene el nombre de la región, pero su precisión y los nombres utilizados para cada región varía mucho.
latitude: Posición en el eje vertical del sistema de coordenadas.
longitude: Posición en el eje horizontal del sistema de coordenadas.
Esto crea un problema a la hora del filtrar los datos por el nombre de una región, pues es difícil encontrar un solo valor que encompase a todas las variantes del nombre utilizado para esa región. iNaturalist se encuentra a nivel mundial, y usted puede acceder a todos esos datos. Sin embargo, hoy accederemos solamente a datos de Puerto Rico. Entonces, lo primero antes de acceder a los datos de iNaturalist, es crear un objeto que contenga los límites geográficos de interés crea. Para esto se crea un objeto que contenga un cuadro delimitador (bounding box):
cajapr <- c(
17.75, #Latitud Sur
-67.4, #Longitud Oeste
18.75, #Latitud Norte
-65.15 #Longitud Este
)
cajapr## [1] 17.75 -67.40 18.75 -65.15
Ahora podemos acceder a datos de Puerto Rico utilizando el objeto cajapr. Para este ejercicio utilizaremos datos de orquídeas pertenecientes al género Eulophia. En Puerto Rico se han reportado 3 especies de este género Eulophia alta, Eulophia maculata y Eulophia graminea. De estos, sólo E. alta es considerada nativa de la isla. Entonces, utilizando la función get_inat_obs podemos acceder a los datos de Eulophia en Puerto Rico
# Intenta consultar iNaturalist en vivo y guarda una copia local; si la
# consulta falla (sin conexión o API no disponible), usa esa copia.
eulophia <- tryCatch(
{
d <- get_inat_obs(taxon_name = "Eulophia",
quality = 'research',
geo = TRUE,
maxresults = 1000)
readr::write_csv(d, "Datos/eulophia_inat.csv")
d
},
error = function(e) readr::read_csv("Datos/eulophia_inat.csv")
)
eulophia$scientific_name <- as.factor(eulophia$scientific_name)
flextable(head(eulophia)) # Para ver las primeras 6 lineas en una tabla bonitascientific_name | datetime | description | place_guess | latitude | longitude | tag_list | common_name | url | image_url | user_login | id | species_guess | iconic_taxon_name | taxon_id | num_identification_agreements | num_identification_disagreements | observed_on_string | observed_on | time_observed_at | time_zone | positional_accuracy | public_positional_accuracy | geoprivacy | taxon_geoprivacy | coordinates_obscured | positioning_method | positioning_device | user_id | user_name | created_at | updated_at | quality_grade | license | sound_url | oauth_application_id | captive_cultivated |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Eulophia graminea | 2026-10-07 14:53:10 -0400 | SE 23rd Ave, Summerfield, FL, US | 28.981387 | -82.10678 | Chinese Crown Orchid | https://www.inaturalist.org/observations/406615604 | https://static.inaturalist.org/photos/747084565/medium.jpg | amanda2139 | 406,615,604 | Chinese Crown Orchid | Plantae | 427,629 | 2 | 0 | 2026-10-07 14:53:10-04:00 | 2026-10-07 | 2026-10-07 18:53:10 UTC | Eastern Time (US & Canada) | 18 | 18 | false | 4,886,185 | Amanda Scacchi | 2026-10-07 18:54:40 UTC | 2026-10-08 04:53:43 UTC | research | 3 | false | ||||||||
Eulophia maculata | 2026-09-19 10:05:00 -0400 | Coconut Creek, FL, USA | 26.320844 | -80.18484 | Monk Orchid | https://www.inaturalist.org/observations/406594794 | https://inaturalist-open-data.s3.amazonaws.com/photos/747019236/medium.jpg | annievdne | 406,594,794 | Monk Orchid | Plantae | 1,453,469 | 2 | 0 | 2026/09/19 10:05 AM | 2026-09-19 | 2026-09-19 14:05:00 UTC | Eastern Time (US & Canada) | 1,414 | 1,414 | open | false | 8,906,166 | Ann Levarity | 2026-10-07 17:11:00 UTC | 2026-10-08 04:55:23 UTC | research | CC-BY-NC | false | |||||||
Eulophia hians hians | 2026-10-07 17:59:39 +0200 | Breyer Avenue, Pretoria, Gauteng, ZA | -25.694245 | 28.24744 | Blue Yawning Harlequin | https://www.inaturalist.org/observations/406591357 | https://static.inaturalist.org/photos/747034079/medium.jpg | carelf | 406,591,357 | Eulophia hians hians | Plantae | 599,258 | 2 | 0 | 2026-10-07 17:59:39+02:00 | 2026-10-07 | 2026-10-07 15:59:39 UTC | Pretoria | 6 | 6 | open | false | 2,479,971 | Carel Fourie | 2026-10-07 16:52:41 UTC | 2026-10-08 13:03:03 UTC | research | 3 | false | |||||||
Eulophia maculata | 2026-10-07 11:00:31 -0400 | Grand Oaks Dr, Davie, FL, US | 26.075044 | -80.32243 | Monk Orchid | https://www.inaturalist.org/observations/406570372 | https://inaturalist-open-data.s3.amazonaws.com/photos/746992422/medium.jpg | richackerman | 406,570,372 | Monk Orchid | Plantae | 1,453,469 | 1 | 0 | 2026-10-07 11:00:31-04:00 | 2026-10-07 | 2026-10-07 15:00:31 UTC | Eastern Time (US & Canada) | 32 | 32 | open | false | 1,542,538 | Rich Ackerman | 2026-10-07 15:00:43 UTC | 2026-10-07 15:37:13 UTC | research | CC-BY-NC | 3 | false | ||||||
Eulophia cucullata | 2026-09-29 11:39:00 +0200 | Manono, Democratic Republic of the Congo | -7.167834 | 27.45094 | Hooded Harlequin | https://www.inaturalist.org/observations/406565109 | https://inaturalist-open-data.s3.amazonaws.com/photos/746878939/medium.jpg | candyce_areington | 406,565,109 | Hooded Harlequin | Plantae | 338,672 | 1 | 0 | 2026/09/29 11:39 AM | 2026-09-29 | 2026-09-29 09:39:00 UTC | Africa/Lubumbashi | false | 7,485,834 | Candyce Areington | 2026-10-07 14:34:27 UTC | 2026-10-08 05:14:36 UTC | research | CC-BY-NC | false | ||||||||||
Eulophia hians | 2026-10-06 11:04:39 +0200 | Meyer St & 27th Ave, Rietfontein, Pretoria, 0084, South Africa | -25.696323 | 28.23042 | Yawning Harlequin | https://www.inaturalist.org/observations/406518270 | https://inaturalist-open-data.s3.amazonaws.com/photos/746891204/medium.jpg | hildegard3 | 406,518,270 | Yawning Harlequin | Plantae | 415,244 | 2 | 0 | 2026-10-06 11:04:39 | 2026-10-06 | 2026-10-06 09:04:39 UTC | Pretoria | open | false | gps | gps | 2,806,444 | Hildegard Klein | 2026-10-07 09:22:45 UTC | 2026-10-07 11:30:41 UTC | research | CC-BY | 2 | false |
bounds = cajapr Con el siguiente código podemos ver de forma rápida cuántas observaciones de cada especie hemos obtenido de iNaturalist:
## Eulophia aculeata Eulophia alta Eulophia andamanensis
## 1 23 1
## Eulophia angolensis Eulophia barbata Eulophia bolusii
## 3 1 2
## Eulophia callichroma Eulophia caricifolia Eulophia citrina
## 2 2 1
## Eulophia clitellifera Eulophia cochlearis Eulophia coeloglossa
## 19 18 4
## Eulophia cucullata Eulophia diffusiflora Eulophia ensata
## 8 4 3
## Eulophia euglossa Eulophia exaltata Eulophia flava
## 1 1 1
## Eulophia galeoloides Eulophia gracilis Eulophia graminea
## 1 1 170
## Eulophia guineensis Eulophia hians Eulophia hians hians
## 2 38 123
## Eulophia hians inaequalis Eulophia hians nutans Eulophia horsfallii
## 50 2 12
## Eulophia katangensis Eulophia lamellata Eulophia maculata
## 3 8 268
## Eulophia micrantha Eulophia nuda Eulophia odontoglossa
## 8 9 2
## Eulophia ovalis Eulophia ovalis bainesii Eulophia parviflora
## 9 1 21
## Eulophia parvilabris Eulophia parvula Eulophia pauciflora
## 1 4 2
## Eulophia petersii Eulophia petiolata Eulophia picta
## 12 1 101
## Eulophia platypetala Eulophia pratensis Eulophia ramosa
## 1 2 1
## Eulophia roseovariegata Eulophia saxicola Eulophia siamensis
## 1 1 3
## Eulophia speciosa Eulophia streptopetala Eulophia tuberculata
## 15 8 10
## Eulophia welwitschii Eulophia zeyheriana Eulophia zollingeri
## 4 2 8
Ahora que hemos obtenido los datos, podemos observar su distribución en el mapa de Puerto Rico utilizando leaflet.
32.9 Utilizando Leaflet
La función base del paquete leaflet es leaflet(), con esta se activa un artilugio (widget en inglés) sobre el cuál se construye el mapa. Esta función debe estar unida a otra función como addTiles() que añade el mapa.
Una vez tenemos el artilugio y el mapa, añadimos una capa que incluye los datos que se desean visualizar con marcadores sobre el mapa con la función addMarkers() dentro del cual se deben especificar los parámetros lng (longitud) y lat (latitud). Note que también se especifica el data.frame que contiene los datos bajo la función leaflet(). Cuando se trabaja en Leaflet, al especificar las variables a utilizar, es necesario colocar una virgulilla (~) antes de la variable. Esto le deja saber a R que lo que se especifica es una variable dentro del data.frame mencionado en la primera capa bajo la función leaflet().
## [1] "scientific_name" "datetime"
## [3] "description" "place_guess"
## [5] "latitude" "longitude"
## [7] "tag_list" "common_name"
## [9] "url" "image_url"
## [11] "user_login" "id"
## [13] "species_guess" "iconic_taxon_name"
## [15] "taxon_id" "num_identification_agreements"
## [17] "num_identification_disagreements" "observed_on_string"
## [19] "observed_on" "time_observed_at"
## [21] "time_zone" "positional_accuracy"
## [23] "public_positional_accuracy" "geoprivacy"
## [25] "taxon_geoprivacy" "coordinates_obscured"
## [27] "positioning_method" "positioning_device"
## [29] "user_id" "user_name"
## [31] "created_at" "updated_at"
## [33] "quality_grade" "license"
## [35] "sound_url" "oauth_application_id"
## [37] "captive_cultivated"
Una vez los marcadores están establecidos, es posible añadir etiquetas que contengan información acerca de las observaciones con el parámetro popup de la función addMarkers(). Por ejemplo, se pueden colocar etiquetas que contengan información del lugar de la observación especificando que la etiqueta contenga la información de la variable place_guess. Para ver la etiqueta debe hacer clic sobre el marcador de interés.
## [1] "scientific_name" "datetime"
## [3] "description" "place_guess"
## [5] "latitude" "longitude"
## [7] "tag_list" "common_name"
## [9] "url" "image_url"
## [11] "user_login" "id"
## [13] "species_guess" "iconic_taxon_name"
## [15] "taxon_id" "num_identification_agreements"
## [17] "num_identification_disagreements" "observed_on_string"
## [19] "observed_on" "time_observed_at"
## [21] "time_zone" "positional_accuracy"
## [23] "public_positional_accuracy" "geoprivacy"
## [25] "taxon_geoprivacy" "coordinates_obscured"
## [27] "positioning_method" "positioning_device"
## [29] "user_id" "user_name"
## [31] "created_at" "updated_at"
## [33] "quality_grade" "license"
## [35] "sound_url" "oauth_application_id"
## [37] "captive_cultivated"
leaflet(eulophia) %>%
addTiles() %>%
addMarkers(lng = ~ longitude,
lat = ~ latitude,
popup = ~ place_guess) # cambiar a la variable de interésLa función paste0 es una función base de R que concatena todos los elementos en una línea de caracteres. Los caracteres introducidos serán interpretados como UTF-8 o codificación de caracteres. Estos caracteres pueden ser mezclados con valores de una variable de caracteres en un data.frame. En el siguiente ejemplo se crea una etiqueta que contiene la ubicación y la especie observada. Se utiliza la función paste0() para combinar caracteres y las variables scientific_name y place_guess del data.frame.
leaflet(eulophia) %>%
addTiles() %>%
addMarkers(lng = ~ longitude,
lat = ~ latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado donde: </B>", eulophia$place_guess)
)32.9.1 addAwesomeMarkers: Cambiando el diseño de los marcadores
La función addAwesomeMarkers() puede ser utilizada en lugar de addMarkers() y permite utilizar marcadores con diferentes diseños que se pueden obtener de fontawesome. Es posible cambiar los colores de los marcadores utilizando la codificación de colores RGB en formato (#RRGGBB). La siguiente guía de colores en R creada por Melissa Clarkson (2010) explica como utilizar colores en R: A guide to using color in R. Adicional, esta guía creada por Derek H. Ogle (2022) es muy útil.
En schemecolor podemos encontrar las codificaciones de los colores y muchos funcionan para leaflet. Para hacer estos cambios utilizamos la función awesomeIcons() bajo el parámetro icon dentro de addAwesomeMarkers().
Dentro de la función awesomeIcons() podemos especificar varios parámetros como:
icon: El icono que desea utilizar para su marcador. Debe poner el nombre que aparece en la página web de iconos que esté utilizando (i.e., fontawesome).library: Cuando se especifica un icono, es necesario especificar la biblioteca de donde se saca el icono. Si es de fontawesome sería'fa'.markerColor: Para cambiar el color del marcador (parte que rodea el icono), utilizando la codificación de colores de schemecolor.iconColor: Para cambiar el color del icono del marcador, también con la codificación de schemecolor.
leaflet() %>%
addTiles() %>%
addAwesomeMarkers(lng = eulophia$longitude,
lat = eulophia$latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
icon = awesomeIcons(icon = "leaf", library = "fa",
markerColor = "blue",
iconColor = "#FFFFFF")
) Cuando se visualiza una gran cantidad de datos, esto puede resultar en el solapamiento de los marcadores, lo cual no es muy agradable a la vista. Es posible crear agregados que ayuden a visualizar mejor y faciliten el movimiento a través del mapa con el parámetro clusterOptions bajo la función addAwesomeMarkers(). El valor de este parámetro debe ser la función markerClusterOptions().
leaflet() %>%
addTiles() %>%
addAwesomeMarkers(lng = eulophia$longitude, lat = eulophia$latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
icon = awesomeIcons(icon = "leaf", library = "fa",
markerColor = "darkgreen",
iconColor = "#FFFFFF"),
clusterOptions = markerClusterOptions()
) Y para culminar, es posible cambiar el diseño del mapa utilizando la función addProviderTiles() en lugar de addTiles(). Puede echar un vistazo a los diferentes diseños disponibles para Leaflet la página de proveedores de Leaflet en GitHub. Actualmente Leaflet en R admite diseños de los siguientes proveedores: OpenStreetMap, MapQuestOpen, Stamen, Esri and OpenWeatherMap.
icon1 <- awesomeIcons(icon = "smile-o",
iconColor = "#FFFFFF",
library = "fa",
markerColor = "darkgreen")
leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addAwesomeMarkers(lng = eulophia$longitude, lat = eulophia$latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
icon = icon1
)32.10 Trabajando Variables Categóricas con Leaflet:
Es posible distinguir entre especies utilizando diferentes colores, pero debe utilizar la función addCircleMarkers() en lugar de addAwesomeMarkers(). Para colorear grupos en Leaflet es necesario generar una paleta de colores para las variables de interés de acuerdo al tipo de variable (i.e., numérica, factorial). Para esto se utilizan las funciones colorFactor(), colorNumeric(), colorBin(), o colorQuantile(). colorFactor() y colorNumeric() generan paletas de colores para variables factoriales y numéricas, respectivamente; mientras que colorBin() y colorQuantile() generan paletas de colores para valores numéricos resumidos en grupos.
En este caso se está trabajando con una variable factorial que divide las observaciones entre 3 especies de orquídeas. Primero se crea una paleta de colores para una variable factorial con la función colorFactor(). Bajo esta función se especifican los parámetros palette y levels. palettees para especificar la paleta de colores a utilizar y levelsespecifica los niveles de la variable factorial. La paleta de colores que se utiliza ("Dark2") proviene del paquete RColorBrewer. Esta paleta de colores para los niveles de la variable factorial scientific_name del data.frame eulophia se guardará en un objeto llamado pal.
mypal <- colorFactor(palette = 'Set3',
levels = levels(eulophia$scientific_name))
eulophia$scientific_name=as.factor(eulophia$scientific_name)
mypal <- colorFactor(palette = 'Set3',
levels = levels(eulophia$scientific_name))Ahora que se ha especificado los colores para los valores factoriales, es posible utilizar el objeto pal para colorear los marcadores circulares del mapa de Leaflet de acuerdo a la especie observada especificando el parámetro color
leaflet(eulophia) %>%
addTiles() %>%
addCircleMarkers(lng = ~ longitude, lat = ~ latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
color = ~ mypal(scientific_name)) Para ayudar con la visualización de datos pertenecientes a diferentes grupos, en ocasiones es bueno presentar una leyenda que indique los colores que representan cada grupo. Es posible añadir una leyenda al mapa de Leaflet utilizando la función addLegend() y especificando los siguientes parámetros:
position: Para determinar la posición de la leyenda, en este caso se colocará en la parte superior derecha,"topright", del mapa, pero podría colocarla en la parte inferior derecha ("bottomright"), la parte inferior izquierda ("bottomleft"), o la parte superior izquierda ("topleft").pal: La paleta de colores a utilizar generada a través de la función
colorFactor()o sus equivalentes (i.e.,colorNumeric())values: Los valores o la variable u objeto que contiene los valores utilizados.title: El título de la leyenda especificado como una línea de caracteres.
leaflet(eulophia) %>%
addTiles() %>%
addCircleMarkers(lng = ~ longitude, lat = ~ latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
color = ~ pal(scientific_name)) %>%
addLegend(position = "topright",
pal = pal,
values = ~ scientific_name,
title = "Species"
)iconcolor <- function(scienctific_name)
leaflet() %>%
addProviderTiles("Esri.WorldImagery") %>%
addAwesomeMarkers(lng = eulophia$longitude, lat = eulophia$latitude,
popup = paste0("<B>Especie: </B>", "<I>", eulophia$scientific_name, "</I>",
"<br/>",
"<B>Colectado por: </B>", eulophia$place_guess),
icon = icon1
)L.circleMarker([place.lon, place.lat], { color: getColor(place.constructdate), // you can call the getColor function fillColor: getColor(place.constructdate), fillOpacity: 0.5 })
32.11 Ejercicios del capítulo
Sobre los ejercicios de este capítulo. Cada ejercicio se entrega con tres
cosas: el script, el mapa (que se vea en el .html) y una explicación en
palabras. Como los mapas solo salen en HTML, entrega siempre el .html, no solo
el .Rmd. Los ejercicios usan Datos/crimen_pr.csv, que está en el repositorio
del curso y trae coordenadas de Puerto Rico, no los terremotos del USGS ni los
datos de iNaturalist del capítulo. La hoja completa para entregar está en
Ejercicios/Ejercicios_Capitulo_32_leaflet.Rmd.
32.11.1 Ejercicio 1. El mapa más sencillo
Haz un mapa vacío centrado en Puerto Rico, con leaflet(), addTiles() y
setView(). Añade un marcador en tu pueblo.
Verificación: al abrir el .html, la isla tiene que verse completa y el
marcador tiene que caer donde debe.
En palabras: anota las coordenadas que usaste. ¿Cuál de los dos números es la latitud y cuál la longitud? ¿Cómo lo sabes sin buscarlo?
32.11.2 Ejercicio 2. Los puntos que acabaron en el océano
Lee Datos/crimen_pr.csv. Fíjate en que las columnas se llaman POINT_X y
POINT_Y, y haz el mapa de la manera intuitiva: lng = ~POINT_X y
lat = ~POINT_Y.
Verificación: los puntos no caen en Puerto Rico. Mira dónde caen.
Después arréglalo y vuelve a hacer el mapa.
En palabras: ¿en qué parte del mundo aparecieron los puntos? Mira el rango de
las dos columnas con range(..., na.rm = TRUE) y explica cómo esos dos rangos te
dicen cuál columna es cuál, sin necesidad de abrir el mapa.
32.11.3 Ejercicio 3. Los puntos que no existen
El archivo tiene coordenadas faltantes, y además unas pocas guardadas como cero. Cuenta las dos cosas y quítalas antes de mapear.
Verificación: hay 733 filas con coordenada faltante y 2 con un cero exacto. Después de quitarlas quedan 8135 filas.
En palabras: dibuja en un mapa solo las filas con coordenada cero. ¿Dónde
caen? Busca en internet qué es “Null Island”. ¿Por qué un cero es más peligroso que
un NA?
32.11.4 Ejercicio 4. Color por categoría
Colorea los puntos según el tipo de delito, con colorFactor(), y añade la leyenda
con addLegend().
Verificación: la leyenda tiene que mostrar 9 categorías.
En palabras: con nueve colores el mapa se vuelve difícil de leer. Reduce las
categorías a cinco con fct_lump_n() del capítulo de factores y compara los dos
mapas. ¿Cuál usarías y qué perdiste al agrupar?
32.11.5 Ejercicio 5. Las ventanas emergentes
Añádele a cada punto un popup que diga el tipo de delito, la fecha, la hora y el
área policiaca. Prueba también label en vez de popup.
Verificación: al hacer clic (o al pasar el ratón, según cuál uses) tiene que salir la información de esa observación, no la de todas.
En palabras: ¿cuál es la diferencia entre popup y label? ¿Cuál usarías para
un mapa que va a mirar mucha gente sin instrucciones?
32.11.6 Ejercicio 6. Ocho mil puntos
Dibuja los 8135 puntos de golpe y mira cuánto tarda en abrirse el .html y cuánto
pesa el archivo. Después repítelo con
clusterOptions = markerClusterOptions().
Verificación: con agrupamiento, el mapa abre mucho más rápido y muestra círculos con números en vez de puntos sueltos.
En palabras: compara el tamaño de los dos .html con file.size(). ¿Qué
pierdes al agrupar? ¿En qué nivel de acercamiento vuelven a verse los puntos
individuales?
32.11.7 Ejercicio 7 (BONO). Capas que se encienden
Escoge dos tipos de delito y ponlos en dos capas distintas, con group =, y
añade addLayersControl() para poder encender y apagar cada una.
Verificación: en el mapa tiene que aparecer arriba a la derecha un control con las dos capas, y al desmarcar una, sus puntos tienen que desaparecer.
En palabras: ¿qué pregunta puedes contestar con las dos capas encendidas y apagadas que no podrías contestar con un mapa fijo? Este es el argumento de fondo del capítulo: escríbelo con tus palabras.