Poster mapa con OSM

Poster mapa con OSM

Estaba navegando por la red, y encontré una de esas cosas curiosas, que me encantan, una web que te imprime en alta calidad un mapa de tu ciudad, o región en un póster.

Puedes pensar que eso es una tontería, hasta con imprimir un mapa directamente de google, o mejor de los de CARTO BD o una imagen de la tierra desde satélite, pero no, no es suficiente, hacer un mapa es algo más artístico y complejo que eso, y me llamó la atención la selección tan chula de colores y fondos que tiene y la sencillez desnuda de las líneas y fondos que usan, pues carecen de texto.

mapa-póster de Toronto

mapa-póster de Toronto

Esta web es modernmapart.com. La idea es buena, al menos yo me compararía unos cuantos de estos mapas para casa o el despacho… Bueno, como buen españolito, la falta de recursos agudiza el ingenio, así que pensé..¿por qué no hacerlo con R?, el diseño parece sencillo…. manos a la obra…

OSM

OpenStreetMap o (OSM) es un servicio de datos geográficos de acceso libre, con licencia Open Data Commons Open Database License (ODbL) de la Fundación OpenStreetMap (OSMF). Es el servicio de mapas de acceso libre más importante y gracias a esta fundación podemos hacer aplicaciones SIG que compiten en igualdad de condiciones con los grandes de la cartografía digital como google, bing maps, Carto, o Arcview.

Muchos de los datos los han importado en colaboración con instituciones publicas nacionales como el Instituto Geográfico Nacional en el caso de España, pero al principio fue la comunidad de gente voluntaria la que metía los trazados, los nombres y datos de carreteras, iglesias, caminos, bici rutas, etc.

La capa de mosaicos base de OSM la podemos ver por defecto en Leaflet, y como verás contiene multitud de categorías de datos, capas y etiquetas, pero además nos permite acceder directamente a sus datos, descargarlos y montar mapas personalizados a nuestro gusto a través de su API.

Estructura de capas de OSM

La información geográfica en OSM se almacena junto con metadatos, simples etiquetas que dan mucho juego a la hora de consultar o filtrar los datos. Actualmente hay unas 29 capas principales que contienen una inmensa variedad de datos geográficos (podemos ver el detalle en https://wiki.openstreetmap.org/wiki/Map_Features).

Para este ejemplo vamos a usar solo las siguientes etiquetas-capas principales:

  • Amenity que identifica zonas de ocio, comercio, educación, salud, entretenimiento…
  • Boundary que contiene los límites fronteras
  • Highway que contiene las carreteras y en general lineas de transporte
  • Natural identifica zonas naturales, vegetación, lagos, playas
  • Waterway para ríos, canales

Cada categoría principal tiene varias subcategorías y una tabla asociada con más datos asociados con las que podemos filtrar y seleccionar, veamos algún ejemplo.

Descargar datos OSM con osmdata

Para descargar directamente los datos geográficos y metadatos de OpenStreetMap usaremos la librería osmdata que comunica con su API.

La función opq() delimita la zona geográfica de la consulta, y add_osm_feature() realiza la consulta en esa zona de las etiquetas o capas que se especifiquen.

En los siguientes ejemplos vamos a seleccionar una zona geográfica y con la función getbb() que nos devuelve la caja de coordenadas que incluye una localización. En nuestro caso vamos a descargar datos de la zona del Mar Menor, nuestro querido y maltratado mar chico murciano.

  library(osmdata) 
  library(tidyverse)
  library(sf)
  
  #Objengo la caja de coordenadas de la zona de San Javier
  caja<-getbb("San Javier, Murcia")
  # bajamos la latitud de la caja hasta Cabo de Palos
  # para sacar todo el Mar Menor
  caja[2,1]<-37.60
  
  # Hacemos una consulta a OSM y descargamos datos como capa sf
  # por ejemplo de los ríos de la zona
  rios <- opq(caja) %>%
      add_osm_feature(key = 'waterway') %>%
      osmdata_sf()
  
  class(rios)
## [1] "list"       "osmdata"    "osmdata_sf"
  # para echar un vistazo  la lista podemos usar purrr::walk
  #walk(rios, print)
  # estructura de la capa de lineas
  str(rios$osm_lines)
## Classes 'sf' and 'data.frame':   245 obs. of  15 variables:
##  $ osm_id      : chr  "29341865" "33834293" "43312162" "44027445" ...
##  $ name        : chr  NA "Rambla del Beal" "Rambla de la Carrasquilla" NA ...
##  $ admin_level : chr  NA NA NA NA ...
##  $ boat        : chr  "no" "no" NA NA ...
##  $ boundary    : chr  NA NA NA NA ...
##  $ bridge      : chr  NA NA NA NA ...
##  $ intermittent: chr  NA "yes" "yes" NA ...
##  $ layer       : chr  NA NA NA NA ...
##  $ route       : chr  NA NA NA NA ...
##  $ source      : chr  NA NA NA NA ...
##  $ tourism     : chr  NA NA NA NA ...
##  $ tunnel      : chr  NA NA NA NA ...
##  $ type        : chr  NA NA NA NA ...
##  $ waterway    : chr  "river" "stream" "stream" "canal" ...
##  $ geometry    :sfc_LINESTRING of length 245; first list element:  'XY' num [1:12, 1:2] -0.854 -0.853 -0.853 -0.853 -0.853 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr  "322781962" "322782101" "322782100" "322782098" ...
##   .. ..$ : chr  "lon" "lat"
##  - attr(*, "sf_column")= chr "geometry"
##  - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
##   ..- attr(*, "names")= chr  "osm_id" "name" "admin_level" "boat" ...
  # Hacemos otra consulta para buscar mercadonas en el Mar Menor
  mercadonasMM<-opq(caja) %>%
         add_osm_feature("name","Mercadona")%>%
         add_osm_feature("shop","supermarket") %>%  osmdata_sf()  

En resumen, hemos delimitado una zona y después hecho una consulta de descarga de datos a la API de OSM con la función add_osm_feature() de la librería osmdata aplicando un filtro de etiqueta principal key = 'waterway.

Para manejar estos datos los convertimos a sf con la función osmdata_sf(). Así hemos obtenido el objeto rios que contiene una lista con datos geográficos y tablas, que incluye no solo ríos, también canales y ramblas.

Otro ejemplo es la capa mercadonaMM, aquí hemos consultado la etiqueta name=Mercadona y después filtrado las que tienen la etiqueta shop=supermarket. El resultado serán los supermercados mercadona que se encuentran en la zona y que almacenamos en formato sf también.

Para ver un listado de todas las etiquetas-capas disponibles hay una función available_features() que muestra una lista de todas, y para una descripción de cada una mejor ver [esta web](https://wiki.openstreetmap.org/wiki/Map_Features.

Vamos a pintar estas dos capas (sf) sobre un mapa dinámico en R con leaflet ( si quieres saber más de Leaflet mira mi post ). Como el objeto rios es una lista con: puntos,líneas, polígonos, multilineas…, vamos a pintar solo las líneas. Aquí usaremos un truco, ya que la función addPolylines de Leaflet falla cuando la tabla contiene el campo mane, por lo que hay que vaciarlo antes.

# Pintamos las capas descargadas en Leaflet 
library(leaflet)
# Arreglamos el error de sf con las polilineas 
# borrando los nombres de la capa de lineas
# si no haces esto no se pinta ..es un bug
names(st_geometry(rios$osm_lines)) = NULL

# Dibujamos el mapa con Leaflet y las 2 capas
leaflet(rios$osm_lines) %>% addTiles() %>% # carga el mosaico base de OSM 
  addPolylines(color ="blue",label = ~rios$osm_lines$name ) %>% # carga la capa de lineas de rios
  addCircleMarkers(data=mercadonasMM$osm_points,radius = 7, color = "red",
                   popup= ~mercadonasMM$osm_points$name) # carga la capa de puntos mercadonas

Bajar el resto de capas

Visto el proceso de descarga de OSM, procederemos a la descarga de algunas capas más que nos permitirán montar el mapa póster que buscamos.

También usaré una capa que tenía en formato shp del contorno de la Confederación Hidrográfica solo para montar un polígono base del terreno.

# descargamos capas de OSM y almacenamos como sf
# capa con poligonos de lagunas
agua <- opq(caja) %>%
    add_osm_feature(key = 'natural', value = 'water') %>%
    osmdata_sf()
# linea de costa
costa <- opq(caja) %>%
    add_osm_feature(key = 'natural', value = 'coastline') %>%
    osmdata_sf()
# calles de los pueblos
calles<- opq(caja) %>%
  add_osm_feature(key = 'highway',value = 'residential') %>% 
  osmdata_sf()  
#agricola <- opq(caja) %>%
#  add_osm_feature(key = 'landuse',value = 'farmland') %>% 
#  osmdata_sf()
# Carreteras
carreteras <- opq(caja) %>%
  add_osm_feature(key = 'highway') %>%  
  osmdata_sf()
# seleccionamos dentro de carreteras las principales y secundarias
carr_secun<-carreteras$osm_lines %>% # 
            select(highway,name)%>% # 
            filter(highway =='motorway' | highway =='trunk' | highway =='secondary')
# solo autovías
autovia <- carreteras$osm_lines %>% 
            select(highway)%>%
            filter(highway =='motorway' | highway =='trunk')
# De la capa de rios que ya tenemos
# seleccionamos los cursos de agua y canales
rios1<-rios$osm_lines %>% 
            select(waterway,name)%>% 
            filter(waterway =='river' | waterway =='canal' | waterway =='stream')

# voy a descargar una capa que tengo de shp de la confederación, para delimitar las costas
# cargo otras capas en shp que tengo
# pero que están en UTM por lo que transformo crs
#../../
chs <- st_read("static/capas/CHS_Cuenca_del_Segura.shp")
st_crs(chs)<-23030
chs<-st_transform(chs,crs=4326)
# cortamos la capa a la zona del Mar Menor
# para eso usamos la capa de lineas de rios
chs<-st_crop(chs,rios$osm_lines)

Con todas estas capas almacenadas en instancias sf, vamos a montar un póster bien chulo del Mar Menor usando la función plot(), la más simple.

Para que la salida tenga una calidad gráfica, lo pintamos en pdf y no en la salida gráfica.

# pintamos en una instancia pdf para que la calidad sea alta
pdf("plot_MarMenor.pdf", width=3.5, height=5)
    # Colores del poster
    cfondo<-"sandybrown" # rgb(255,245,245,max = 255)
    clineas<-rgb(15,30,55,max = 255)
    c1<-alpha("white",0.8)
    # colores del fondo
    par(bg=cfondo, oma=c(0,0,0,0), mar=c(0,0,2,0),adj=0)
    # pintamos
    plot(st_geometry(agua$osm_polygons),col=cfondo, 
         main="MAR MENOR (MURCIA) ",
         family="sans",font=1,
         col.main=clineas, cex.main=1.5)
    # pintamos pol tierra y lineas buffer en mar
    plot(st_geometry(chs),add=T, lwd=0.1,lty=1,col=clineas)
    plot(rios1, add=T, lwd=0.5,col=cfondo)
    buffer<-st_buffer(chs,0.003)
    plot(st_cast(st_union(buffer),"MULTILINESTRING"),add=T,lwd=0.1,col=c1)
    buffer<-st_buffer(chs,0.001)
    plot(st_cast(st_union(buffer),"MULTILINESTRING"),add=T,lwd=0.2,col=c1)
    # resto de capas
    plot(st_geometry(agua$osm_polygons),add=T,lty="blank",col=cfondo)#aguas
    plot(st_geometry(costa$osm_lines), add=T, lwd=0.1,col=clineas)# costa
    plot(st_geometry(carr_secun), add=T, lwd=0.5,col=c1)# carreteras secundarias
    plot(autovia, add=T, lwd=1,col=c1) # autovía
    plot(calles$osm_lines, add=T, lwd=0.2, col= c1) # calles
    #text(-0.86,37.85, cex=2.2,"Mar Menor",col="white")
dev.off()

Espero que os guste el póster y os animo a realizar otros de vuestros pueblos o ciudades siguiendo el proceso descrito.

Poster finalizado

Poster finalizado