OpenStreetMap es un servicio de mapas online que publica información contribuida en forma libre por mÔs de un millón de voluntarios, que benefician a los 5,5 millones de usuarios de la plataforma.

Los contribuidores mĆ”s entusiastas mapean barrios completos utilizando herramientas GPS para enviar información local completa, actualizada y precisa a OpenStreetMap. Varias empresas y entidades pĆŗblicas que producen información geogrĆ”fica tambiĆ©n contribuyen al permitir que sus datos sean incluidos. Existen equipos profesionales de contribuidores que que se coordinan para agregar y mantener actualizada información georeferenciada de lĆ­mites polĆ­ticos, calles, edificios, negocios y otros puntos de interĆ©s; en ocasiones empleados por compaƱƭas que dependen de OpenStreetMap para el ā€œmapa baseā€ de sus productos, como mapbox.com y carto.com.

Toda la información disponible en OpenStreetMap puede ser descargada y reutilizada por cualquier persona, ya sea accediendo al mapa online, obteniendo una copia completa de la base de datos, o accediendo a los datos vía API.

OpenStreetMap desde R

Utilizaremos osmdata, un paquete de R que permite acceder a los datos de OpenStreetMap (OSM de aquí en mÔs) con sus atributos, geometría y posición.

Como siempre, si no tenemos aĆŗn el paquete lo instalamos:

library(osmdata)

Y lo activamos junto a otros paquetes que vamos a utilizar:

library(osmdata)
library(tidyverse) # nuestra navaja suiza para manipulación y visualización de datos
library(sf) # para procesar info espacial
library(leaflet) # Para generar mapas interactivos

Definiendo el lugar

Antes de descargar información, definimos el lugar que queremos consultar. Ɖste puede ser un barrio, un municipio, un paĆ­s, un continente… en Ć©ste caso, lo intentaremos con la ciudad de Rosario.

Las funciones de osmdata nos permiten realizar consultas a Overpass (http://overpass-api.de/), una interfaz que permite extraer información de la base de datos global de OpenStreetMap. Overpass requiere que se especifique una ā€œbounding boxā€, es decir las coordenadas de un rectĆ”ngulo que abarque la zona de interĆ©s.

Podemos obtener la bounding box de cualquier lugar con la función getbb():

bbox <- getbb("Rosario, Santa Fe")
bbox
##         min       max
## x -60.78326 -60.61167
## y -33.03487 -32.86965

Con getbb() también podemos obtener un polígono con los límites políticos, las fronteras exactas, de un lugar. Esto es muy útil para realizar mapas, o para filtrar la información que obtendremos luego para quedarnos sólo con la que corresponda a nuestra ciudad de interés, descartando la de Ôreas aledañas:

bbox_poly <- getbb("Municipio de Rosario, Santa Fe", format_out = "sf_polygon")

Para asegurarnos de que tenemos el lugar que queremos, y no otro de nombre similar en alguna otra parte del mundo, lo verificamos en un mapa rƔpido provisto vƭa leaflet:

leaflet(bbox_poly) %>%
    addTiles() %>% 
    addPolygons()

Luce bien, asĆ­ que continuamos.

Extrayendo información de OSM

El siguiente paso es utilizar la función add_osm_feature() para especificar los datos que queremos descargar. Esto requiere conocer las palabras clave con las que se identifican los registras en la base de OSM, que permiten indicar con gran detalle el tipo de datos georeferenciados que queremos: ya sean Ć”reas de parques pĆŗblicos, posición de oficinas de correo o cajeros automĆ”ticos, vĆ­as de ferrocarril… u otro, en un larguĆ­simo etcĆ©tera que se puede consultar en https://wiki.openstreetmap.org/wiki/Map_Features
En este caso vamos a solicitar todas las vĆ­as de circulación (calles, avenidas, autopistas, etc) de la ciudad. En la base de datos de OSM todas aparecen con la clave ā€œhighwayā€.

rosario <- opq(bbox) %>% 
    add_osm_feature(key = "highway")

Observemos que lo único que hemos obtenido hasta ahora es la definición de una consulta (qué y en dónde), pero aun no descargamos ningún dato:

rosario
## $bbox
## [1] "-33.0348662,-60.7832623,-32.8696532,-60.6116695"
## 
## $prefix
## [1] "[out:xml][timeout:25];\n(\n"
## 
## $suffix
## [1] ");\n(._;>;);\nout body;"
## 
## $features
## [1] " [\"highway\"]"
## 
## attr(,"class")
## [1] "list"           "overpass_query"

Es sólo la definición de una consulta a la base de datos de OpenStreetMap: ā€œTodas las calles (objetos con claveā€highwayā€œ) dentro de Ć©ste rectĆ”ngulo (que sabemos, corresponde a Rosario)ā€. Para hacer efectiva la consulta y descargar los datos, la pasamos por la función osmdata_sf() que recolecta lo que buscamos y lo entrega en forma de dataset espacial:

rosario <- rosario %>% 
    osmdata_sf()

La descarga de información para una ciudad grande puede tomar varios minutos, y mÔs aún la de un Ôrea metropolitana (o país, o continente, etc) así que es normal esperar un poco en ésta parte.

En cuanto se completa, ya tenemos calles:

rosario

La consulta devolvió toda la información de puntos, lĆ­neas y polĆ­gonos disponibles en la base de OSM. A nos otros nos interesan las lĆ­neas, ā€œosm_linesā€, que demarcan la traza de las calles. Los registros con otras geometrĆ­as, como polĆ­gonos, pueden representar elementos asociados a las calles como bulevares o Ć”reas de vereda que no vamos a usar por el momento.

Del conjunto de datos disponibles, extraemos el dataframe con lĆ­neas, y chequeamos los atributos disponibles. Todos han sido recopilados por la comunidad de OpenStreetMap.

calles <- rosario$osm_lines

head(calles) 

Visualizando los resultados

Dado que las calles han sido descargadas en formato sf, podemos visualizarlas con ggplot: y geom_sf:

ggplot() +
    geom_sf(data = calles)

Las calles exceden los lĆ­mites de Rosario, ya que tenemos todos los datos encontrados dentro del rectĆ”ngulo de la bounding box. Para ā€œrecortarā€ los datos conservando solo las calles de la ciudad, podemos extraer su intersección con el polĆ­gono de lĆ­mites que obtuvimos antes.

calles <- st_intersection(calles, bbox_poly)

Ahora si!

ggplot() +
    geom_sf(data = calles)

Podemos visualizar atributos de las calles, por ejemplo el de la velocidad mĆ”xima permitida, que estĆ” presente para casi todas. Pero antes va a ser necesario limpiar un poco los datos… como es usual.

Los dataframes en formato sf que crea osmdata tienen todos los valores en formato texto, incluso aquellos que son números como maxspeed (la velocidad mÔxima), o lanes, la cantidad de carriles. Lo arreglamos:

calles <- calles %>% 
  mutate(maxspeed = as.numeric(maxspeed),
         lanes = ifelse(is.na(lanes), 1, as.numeric(lanes)))

Con eso tenemos limpias las variables de velocidad mƔxima y ancho en carriles. Listos para visualizar.

ggplot(calles) +
    geom_sf(aes(color = maxspeed), alpha = 0.5) +
    scale_color_viridis_c() +
      theme_void() +
    labs(title = "Rosario",
         subtitle = "Vías de circulación",
         caption = "fuente: OpenStreetMap",
         color = "velocidad mƔxima")

O podemos revisar la posición de las avenidas:

ggplot() +
    geom_sf(data = calles,
             color = "gray40", alpha = .5) +
    geom_sf(data = filter(calles, str_detect(name, "Avenida")), 
            color = "salmon") +
    theme_void() +
      labs(title = "Rosario",
         subtitle = "Avenidas",
         caption = "fuente: OpenStreetMap")

Un ejercicio mÔs: ”Bares en el barrio!

Imaginemos que estamos interesados en identificar y caracterizas los bares presentes en un barrio determinado, como San Telmo en la Ciudad de Buenos Aires. Como punto de partida, podemos consultar la base de OSM a ver que encontramos.

Comenzamos por definir nuestra Ɣrea de interƩs

bbox_st <- getbb('San Telmo, Ciudad Autonoma de Buenos Aires')

bbox_st_poly = getbb('San Telmo, Ciudad Autonoma de Buenos Aires', format_out = "sf_polygon")

leaflet(bbox_st_poly) %>% 
  addTiles() %>% 
  addPolygons()

Habiendo verificado que tenemos el Ɣrea correcta, armamos una consulta por la grilla de calles, y la ejecutamos.

SanTelmo_calles <- opq(bbox_st) %>% 
    add_osm_feature(key = "highway") %>% 
    osmdata_sf()

Y tambiĆ©n descargamos información sobre la posición de bares. Habiendo revisado https://wiki.openstreetmap.org/wiki/Map_Features, sabemos que para obtener bares necesitamos la categorĆ­a ā€œamenityā€, y el subtipo ā€œbarā€. En tĆ©rminos de OSM, key = "amenity", value = "bar":

SanTelmo_bares <- opq(bbox_st) %>% 
  add_osm_feature(key = "amenity", value = "bar") %>% 
  osmdata_sf() 

Extraemos la información dentro de los límites exactos del barrio.

A diferencia de las calles, que aparecen en la geometrĆ­a de lĆ­neas, para los bares nos interesan los puntos.

SanTelmo_calles <- st_intersection(SanTelmo_calles$osm_lines, bbox_st_poly)
SanTelmo_bares <- st_intersection(SanTelmo_bares$osm_points, bbox_st_poly)

Y listos para mapear! De paso, resaltamos aquellos donde se baila tango, al colorear segĆŗn el atributo ā€œdance.styleā€, incluido en los datos.

ggplot() +
  geom_sf(data = SanTelmo_calles, 
            color = "darkslateblue") +
  geom_sf(data = SanTelmo_bares, 
            aes(color = dance.style)) +
  geom_sf_label(data = SanTelmo_bares, 
                  aes(label = name), size = 2) +
  theme_void() +
  labs(title = "San Telmo",
       subtitle = "Bares",
       caption = "fuente: OpenStreetMap",
       color = "Ofrecen baile")

Casi listo. Antes de darnos por satisfechos, tenemos que mejorar la ubicación de las etiquetas, que se superponen por la proximidad de los lugares.

Por el momento geom_sf_label() -la geometría de ggplot que permite graficar etiquetas de datos sf- no incluye la útil opción de correr la posición de las etiquetas en forma automÔtica para que no se solapen. Por suerte, existe un pequeño paquete, ggsflabel, que provee la funcionalidad que necesitamos.

Podemos instalar el paquete directo desde el repositorio de su autor:

install.packages("devtools")
devtools::install_github("yutannihilation/ggsflabel")
library(ggsflabel)

Y ahora, usamos geom_sf_label_repel() para la versión final de nuestro mapa de bares en San Telmo:

ggplot() +
  geom_sf(data = SanTelmo_calles, 
            color = "darkslateblue") +
  geom_sf(data = SanTelmo_bares, 
            aes(color = dance.style)) +
  geom_sf_label_repel(data = SanTelmo_bares, 
                  aes(label = name), size = 2) +
  theme_void() +
  labs(title = "San Telmo",
       subtitle = "Bares",
       caption = "fuente: OpenStreetMap",
       color = "Ofrecen baile")