Cultura y Turismo en Barcelona: Dataset Integrado
Integración espacial de OSM y datos municipales

Input
El presente dataset pretende integrar información geoespacial sobre equipamientos culturales y turísticos en la ciudad de Barcelona, combinando datos procedentes de fuentes colaborativas y oficiales.
El objetivo principal es construir un conjunto de datos unificado, consistente y depurado que permita analizar la distribución espacial de infraestructuras culturales (teatros, museos, auditorios) y alojamientos turísticos (hoteles y hostels), así como evaluar la complementariedad entre fuentes abiertas y datos institucionales.
El dataset se ha obtenido a partir de dos fuentes complementarias:
OpenStreetMap (OSM): base de datos colaborativa global, consultada mediante la API Overpass para extraer puntos de interés etiquetados como teatros, museos, hoteles y hostels.
Datos abiertos del Ayuntamiento de Barcelona (CKAN): registros oficiales de teatros, auditorios, hoteles y hosteles, obtenidos mediante la API datastore del portal Open Data BCN.
-
OpenStreetMap: https://www.openstreetmap.org
- API Overpass: https://overpass-api.de
-
Open Data Barcelona: https://opendata-ajuntament.barcelona.cat
- Teatros/Auditorios:
resource_id=0f706441-b9d8-47c9-9e71-ced453810a72 - Hoteles:
resource_id=9bccce1b-0b9d-4cc6-94a7-459cb99450de
- Teatros/Auditorios:
El resultado es un dataset enriquecido que mejora la calidad, cobertura y fiabilidad de la información original mediante técnicas de integración y deduplicación espacial.
Descripción
En primer lugar, se cargan las librerías necesarias para la obtención, procesamiento y visualización de datos geoespaciales. El paquete sf gestiona las geometrías espaciales, osmdata permite consultar la API Overpass de OpenStreetMap, mapSpain proporciona los límites administrativos oficiales de España, y leaflet se emplea para la cartografía interactiva.
El script requiere conexión a internet para consultar las APIs de OSM y CKAN. Se recomienda ejecutar el documento completo para garantizar la reproducibilidad. Las librerías sf, osmdata y mapSpain gestionan la proyección espacial; leaflet genera la visualización interactiva en el navegador.
A continuación, se obtiene el límite municipal oficial de Barcelona a partir del paquete mapSpain, filtrando por código de provincia (08, Barcelona) y código municipal (019).
La geometría se transforma al sistema de referencia WGS84 (EPSG:4326) para su compatibilidad con las consultas a la API de OpenStreetMap. A partir del límite municipal se calcula el bounding box que acotará la consulta espacial.
# Límite municipal oficial de Barcelona
barcelona_mun <- esp_get_munic() %>%
filter(cpro == "08", cmun == "019")
bbox_bcn_mun <- st_bbox(barcelona_mun)Después, se consulta la API Overpass de OpenStreetMap mediante dos peticiones separadas: la primera extrae equipamientos culturales a través de la etiqueta amenity (teatros, museos, centros de arte, salas de música y recintos de eventos); la segunda recupera establecimientos turísticos mediante la etiqueta tourism (hoteles, hostels, museos y atracciones). Separar las consultas mejora la estabilidad y el control sobre los resultados obtenidos.
Los registros de ambas peticiones se combinan en un único objeto, eliminando posibles duplicados por osm_id antes de proceder a la clasificación. A cada punto se le asigna una categoría temática mediante la variable tipo y se normaliza el nombre a mayúsculas, priorizando el campo name cuando está disponible, para facilitar la posterior comparación con los datos municipales.
Finalmente, ambas capas se reproyectan a WGS84 (EPSG:4326) y los puntos se recortan al límite del término municipal de Barcelona mediante una intersección espacial, descartando cualquier registro que caiga fuera de los límites administrativos del municipio.
# Consulta OSM: equipamientos culturales y turísticos
q_mun_turismo <- bbox_bcn_mun %>%
opq(timeout = 1000) %>%
add_osm_feature(key = "amenity",
value = c("theatre", "arts_centre", "events_venue",
"music_venue", "museum"))
mun_turismo <- osmdata_sf(q_mun_turismo)
mun_turismo_ptos <- mun_turismo$osm_points
q_mun_hoteles <- bbox_bcn_mun %>%
opq(timeout = 1000) %>%
add_osm_feature(key = "tourism",
value = c("hotel", "hostel", "museum", "attraction"))
mun_hoteles <- osmdata_sf(q_mun_hoteles)
mun_hoteles_ptos <- mun_hoteles$osm_points
osm_puntos <- bind_rows(
mun_turismo_ptos %>% mutate(categoria = "turismo"),
mun_hoteles_ptos %>% mutate(categoria = "hoteles")
) %>%
# Eliminar duplicados (mismo ID OSM)
filter(!duplicated(osm_id)) %>%
# Clasificar tipo
mutate(
tipo = case_when(
amenity == "theatre" ~ "Teatro",
amenity == "arts_centre" ~ "Centro de arte",
amenity == "events_venue" ~ "Recinto de eventos",
amenity == "music_venue" ~ "Sala de música",
amenity == "museum" ~ "Museo",
tourism == "hotel" ~ "Hotel",
tourism == "hostel" ~ "Hostel",
tourism == "attraction" ~ "Atracción",
TRUE ~ NA_character_
),
# Nombre limpio (prioriza name, luego tipo)
nombre_clean = str_to_upper(str_trim(coalesce(name, tipo))),
fuente_original = "OSM"
) %>%
# Solo los que tienen clasificación válida
filter(!is.na(tipo)) %>%
select(nombre_clean, tipo, fuente_original, geometry)
# Reproyectamos ambas capas a WGS84 e intersectamos para quedarnos solo con los puntos que caen dentro del término municipal
barcelona_mun <- st_transform(barcelona_mun, 4326)
osm_puntos <- st_transform(osm_puntos, 4326)
osm_bcn <- osm_puntos %>%
st_intersection(barcelona_mun)Por otra parte, se accede a la API datastore del portal Open Data BCN mediante una función de recuperación iterativa que obtiene todos los registros disponibles en bloques de 1.000 entradas, garantizando la descarga completa independientemente del volumen de datos.
Se consultan dos recursos distintos: el primero contiene infraestructuras culturales, del que se extraen teatros y auditorios filtrando por la variable secondary_filters_name; el segundo agrupa los establecimientos de alojamiento turístico, del que se seleccionan únicamente los hoteles. Ambos subconjuntos se convierten a objetos espaciales (sf) utilizando las coordenadas geográficas en WGS84 incluidas en los registros originales, y se unifican en un único objeto para su posterior integración con OSM.
# Función para la página API CKAN
get_ckan_data <- function(resource_id) {
all_records <- list()
limit <- 1000
offset <- 0
repeat {
url <- paste0(
"https://opendata-ajuntament.barcelona.cat/data/api/action/datastore_search?",
"resource_id=", resource_id,
"&limit=", limit,
"&offset=", offset
)
response <- httr::GET(url)
data_list <- jsonlite::fromJSON(httr::content(response, "text", encoding = "UTF-8"))
records <- data_list$result$records
if(length(records) == 0) break
all_records <- append(all_records, records)
offset <- offset + limit
}
return(dplyr::as_tibble(all_records))
}
# Teatros y auditorios
ckan_teatres <- get_ckan_data("0f706441-b9d8-47c9-9e71-ced453810a72") %>%
filter(grepl("Teatres|Auditoris", secondary_filters_name, ignore.case = TRUE)) %>%
filter(!is.na(geo_epgs_4326_lon) & !is.na(geo_epgs_4326_lat)) %>%
mutate(
nombre_clean = str_to_upper(str_trim(name)),
tipo = ifelse(grepl("Teatre", secondary_filters_name, ignore.case = TRUE),
"Teatro", "Auditorio"),
fuente_original = "CKAN"
) %>%
st_as_sf(coords = c("geo_epgs_4326_lon", "geo_epgs_4326_lat"), crs = 4326) %>%
select(nombre_clean, tipo, fuente_original, geometry)
# Hoteles
ckan_hoteles <- get_ckan_data("9bccce1b-0b9d-4cc6-94a7-459cb99450de") %>%
filter(grepl("Hotel", secondary_filters_name, ignore.case = TRUE)) %>%
filter(!is.na(geo_epgs_4326_lon) & !is.na(geo_epgs_4326_lat)) %>%
mutate(
nombre_clean = str_to_upper(str_trim(name)),
tipo = "Hotel",
fuente_original = "CKAN"
) %>%
st_as_sf(coords = c("geo_epgs_4326_lon", "geo_epgs_4326_lat"), crs = 4326) %>%
select(nombre_clean, tipo, fuente_original, geometry)
# Unir datos CKAN
ckan_bcn <- bind_rows(ckan_teatres, ckan_hoteles)Antes de proceder a la fusión espacial, se eliminan de ambas fuentes los registros con nombre vacío o nulo, ya que la variable nombre_clean es el campo clave para la identificación y deduplicación de equipamientos. Este paso garantiza que el dataset integrado no contenga entradas sin identificación nominal.
Tratamiento
El tratamiento del dataset se estructura en dos etapas principales: la fusión espacial con deduplicación de registros procedentes de ambas fuentes, y la consolidación y validación del dataset final.
El objetivo es obtener un único conjunto de datos coherente, sin duplicidades y con trazabilidad del origen de cada registro.
Para identificar centros culturales presentes en ambas fuentes, se aplica una estrategia de coincidencia espacial basada en proximidad geográfica.
En primer lugar, ambos datasets se reproyectan al sistema de referencia ETRS89/UTM zona 31N (EPSG:25830), que permite trabajar con distancias reales en metros sobre el territorio peninsular español.
A continuación, se genera un buffer de 50 metros alrededor de cada punto CKAN y se comprueba si algún punto OSM cae dentro de dicho radio. Elegimos 50 metros como distancia porque permite detectar coincidencias reales entre las dos fuentes, sin confundir lugares distintos que simplemente están muy cerca.
Cada registro recibe un indicador de fusión (fuente_fusion) que puede tomar tres valores: Solo_OSM si el equipamiento solo aparece en OpenStreetMap, Solo_CKAN si solo consta en los datos municipales, o Ambas si existe coincidencia espacial entre fuentes.
En caso de coincidencia, se prioriza el registro CKAN por su mayor fiabilidad institucional, descartando el correspondiente de OSM para evitar duplicidades en el dataset final.
# Transformar a CRS métrico para distancias reales (ETRS89/UTM zone 31N)
osm_proj <- st_transform(osm_bcn, crs = 25830)
ckan_proj <- st_transform(ckan_bcn, crs = 25830)
# Buffer de 50 metros alrededor de puntos CKAN
ckan_buffers <- st_buffer(ckan_proj, dist = 50)
# Identificar coincidencias espaciales
coincidencias <- st_intersects(osm_proj, ckan_buffers, sparse = FALSE)
# Marcar coincidencias
osm_proj$match_ckan <- apply(coincidencias, 1, any)
ckan_proj$match_osm <- apply(t(coincidencias), 1, any)
# Añadir fusión en OSM
osm_final <- osm_proj %>%
mutate(
fuente_original = "OSM",
fuente_fusion = ifelse(match_ckan, "Ambas", "Solo_OSM")
)
# Añadir fusión en CKAN
ckan_final <- ckan_proj %>%
mutate(
fuente_original = "CKAN",
fuente_fusion = ifelse(match_osm, "Ambas", "Solo_CKAN")
)
# OSM solo NO coincidentes
osm_only <- osm_final %>%
filter(!match_ckan) %>%
select(nombre_clean, tipo, fuente_original, fuente_fusion, geometry)
# CKAN solo NO coincidentes
ckan_only <- ckan_final %>%
filter(!match_osm) %>%
select(nombre_clean, tipo, fuente_original, fuente_fusion, geometry)
# CKAN coincidentes
ckan_both <- ckan_final %>%
filter(match_osm) %>%
select(nombre_clean, tipo, fuente_original, fuente_fusion, geometry)
# Unir: OSM solo + CKAN solo + CKAN coincidentes (sin OSM coincidentes)
dataset_unificado <- bind_rows(osm_only, ckan_only, ckan_both)Una vez unificados los registros, se aplican los últimos pasos de consolidación. El dataset se mantiene en la proyección ETRS89/UTM zona 31N (EPSG:25830) como sistema de referencia estándar para España peninsular. Se validan las geometrías mediante st_make_valid() y se eliminan aquellos registros con geometría nula o vacía, garantizando la integridad espacial del dataset.
Cada registro recibe un identificador único con el prefijo BCN_ seguido de su número de fila, lo que permite su trazabilidad en análisis posteriores. Las columnas se reordenan de forma coherente: identificador, nombre, tipo de equipamiento, fuente original, indicador de fusión y geometría.
dataset_final <- dataset_unificado %>%
# CRS estándar para España peninsular
st_transform(crs = 25830) %>%
# Validar geometrías
mutate(geometry = st_make_valid(geometry)) %>%
filter(!is.na(geometry) & !st_is_empty(geometry)) %>%
# ID único y orden de columnas
mutate(id_unico = paste0("BCN_", row_number())) %>%
select(id_unico, nombre_clean, tipo, fuente_original, fuente_fusion, geometry)Las tablas siguientes resumen la composición del dataset, detallando el número total de registros y su distribución según el origen de los datos:
Ver código de la tabla
tabla_osm_detalle <- osm_puntos %>%
st_drop_geometry() %>%
count(tipo, sort = TRUE) %>%
mutate(porcentaje = round(n / sum(n) * 100, 1)) %>%
rename("Número" = n, "%" = porcentaje)
knitr::kable(tabla_osm_detalle,
caption = "Distribución por tipo en OSM",
col.names = c("Tipo", "Número", "%"), align = c("c", "c", "c"))| Tipo | Número | % |
|---|---|---|
| Hotel | 351 | 53.1 |
| Hostel | 140 | 21.2 |
| Teatro | 63 | 9.5 |
| Atracción | 49 | 7.4 |
| Centro de arte | 46 | 7.0 |
| Recinto de eventos | 12 | 1.8 |
La consulta a OSM recuperó un total de 672 registros distribuidos en seis categorías. Los hoteles constituyen la categoría más representada, con 351 registros (52,2% del total), seguidos de los hostels con 139 (20,7%), lo que refleja la buena cobertura que la comunidad OSM otorga a los alojamientos turísticos en Barcelona. Entre los equipamientos culturales destacan los teatros (63 registros, 9,4%) y los centros de arte (50 registros, 7,4%), mientras que las atracciones (56 registros, 8,3%) y los recintos de eventos (13 registros, 1,9%) tienen una presencia más limitada.
Ver código de la tabla
tabla_osm_detalle_ckan <- ckan_bcn %>%
st_drop_geometry() %>%
count(tipo, sort = TRUE) %>%
mutate(porcentaje = round(n / sum(n) * 100, 1)) %>%
rename("Número" = n, "%" = porcentaje)
knitr::kable(tabla_osm_detalle_ckan,
caption = "Distribución por tipo en CKAN",
col.names = c("Tipo", "Número", "%"), align = c("c", "c", "c"))| Tipo | Número | % |
|---|---|---|
| Hotel | 446 | 73.2 |
| Teatro | 137 | 22.5 |
| Auditorio | 26 | 4.3 |
Por su parte, el catálogo municipal del Ayuntamiento de Barcelona aportó 609 registros en tres categorías. Los hoteles dominan ampliamente con 446 registros (73,2%), lo que evidencia la exhaustividad del registro oficial de establecimientos hoteleros de la ciudad. Los teatros constituyen la segunda categoría con 137 registros (22,5%), y los auditorios suman 26 registros (4,3%), una tipología ausente en OSM y aportada exclusivamente por esta fuente.
Ver código de la tabla
tabla_resumen <- data.frame(
Indicador = c("Total registros",
"Registros de OSM (solo)",
"Registros de CKAN (solo)",
"Coincidentes en ambas fuentes"),
Valor = c(
nrow(dataset_final),
sum(dataset_final$fuente_original == "OSM" & dataset_final$fuente_fusion == "Solo_OSM"),
sum(dataset_final$fuente_original == "CKAN" & dataset_final$fuente_fusion == "Solo_CKAN"),
sum(dataset_final$fuente_fusion == "Ambas")
)
)
total <- tabla_resumen$Valor[1]
tabla_resumen$Porcentaje <- round(100 * tabla_resumen$Valor / total, 2)
knitr::kable(tabla_resumen,
caption = "Resumen de calidad del dataset integrado cultural-turístico de Barcelona",
col.names = c("Indicador", "Valor", "%"),
align = c("c", "c", "c"))| Indicador | Valor | % |
|---|---|---|
| Total registros | 855 | 100.00 |
| Registros de OSM (solo) | 246 | 28.77 |
| Registros de CKAN (solo) | 256 | 29.94 |
| Coincidentes en ambas fuentes | 353 | 41.29 |
Tras la fusión espacial y deduplicación, el dataset final contiene 867 registros únicos. De ellos, 258 proceden exclusivamente de OSM, aportando principalmente hosteles, centros de arte, atracciones y recintos de eventos no recogidos en el catálogo municipal; 255 son exclusivos de CKAN, e incluyen en su mayoría hoteles y teatros con presencia institucional pero escasa representación en OSM; y 354 registros coinciden en ambas fuentes (40,8% del total), confirmando un notable solapamiento en las categorías compartidas, especialmente hoteles y teatros, para los cuales se ha priorizado la versión CKAN por su mayor fiabilidad institucional.
Output
Una vez completada la integración, el dataset final se guarda en cuatro formatos distintos dentro de la carpeta data/. Exportarlo en varios formatos permite que pueda utilizarse en diferentes contextos y herramientas sin necesidad de repetir todo el proceso de limpieza e integración.
GeoPackage (
.gpkg): es el formato principal y el más recomendado para trabajar con datos espaciales en programas como QGIS.GeoJSON (
.geojson): formato muy extendido en aplicaciones web y visualizaciones interactivas, como las que se generan con Leaflet.RDS (
.rds): formato propio de R que permite cargar el dataset directamente en futuras sesiones sin tener que volver a ejecutar todo el código anterior.CSV (
.csv): tabla de atributos sin geometría, útil para consultar los datos de forma rápida o abrirlos en Excel.
# Crear carpeta de salida (paths RELATIVOS para reproducibilidad)
dir.create("data", showWarnings = FALSE, recursive = TRUE)
# GeoPackage (formato recomendado)
sf::st_write(
dataset_final,
dsn = "data/bcn_cultural_turistico_enhanced.gpkg",
driver = "GPKG",
delete_layer = TRUE,
quiet = TRUE
)
# GeoJSON para compatibilidad web
sf::st_write(
dataset_final,
dsn = "data/bcn_cultural_turistico_enhanced.geojson",
driver = "GeoJSON",
delete_layer = TRUE,
quiet = TRUE
)
# RData para análisis posterior en R
saveRDS(dataset_final, "data/bcn_cultural_turistico_enhanced.rds")
# CSV con atributos (sin geometría) para documentación
write.csv(
sf::st_drop_geometry(dataset_final),
file = "data/bcn_cultural_turistico_metadata.csv",
fileEncoding = "UTF-8",
row.names = FALSE
)El siguiente mapa muestra la distribución de los espacios culturales y de eventos del municipio de Barcelona recogidos en el dataset integrado: auditorios, centros de arte, atracciones turísticas y recintos de eventos. El color de cada punto indica el origen del registro, lo que permite evaluar visualmente la cobertura de cada fuente para esta categoría de equipamientos.
Como se puede observar, la mayoría de los puntos aparecen en azul, lo que indica que proceden exclusivamente de OSM. Esto tiene sentido dado que el catálogo municipal (CKAN) no incluye categorías como centros de arte, atracciones o recintos de eventos.
Los puntos en naranja (Solo CKAN) y verde (ambas fuentes) son minoritarios, correspondiendo a centros que sí tienen reflejo en los datos oficiales. Los equipamientos se concentran principalmente en el centro y la zona costera de la ciudad, áreas de mayor densidad cultural y turística.
Ver código del gráfico
# Preparar datos para leaflet (transformar a WGS84 para web)
dataset_web <- st_transform(dataset_final, crs = 4326)
bcn_muni <- mapSpain::esp_get_munic() %>%
filter(cpro == "08", cmun == "019") %>%
st_transform(crs = 4326) # Transformar a WGS84 para Leaflet
auditorios_map <- dataset_web %>%
filter(tipo == c("Auditorio", "Centro de arte", "Atracción", "Recinto de eventos"))
leaflet(auditorios_map) %>%
addControl(
html = '<div style="background-color:white; padding:10px; border-radius:5px;
box-shadow:0 2px 5px rgba(0,0,0,0.2);">
<h4 style="margin:0 0 5px 0; color:#2c3e50; font-weight:bold; font-size:14px;">
Espacios culturales y de eventos de Barcelona</h4>
<p style="margin:0; font-size:11px; color:#7f8c8d; line-height:1.3;">
Auditorios, centros de arte, atracciones y recintos de eventos</p>
</div>',
position = "topright"
) %>%
addPolylines(
data = bcn_muni,
color = "black",
weight = 2,
opacity = 0.8,
smoothFactor = 0.5,
label = "Límite municipal de Barcelona"
) %>%
addTiles() %>%
addCircleMarkers(
radius = 4,
stroke = FALSE,
fillOpacity = 0.7,
color = ~case_when(
fuente_fusion == "Ambas" ~ "darkgreen",
fuente_fusion == "Solo_OSM" ~ "steelblue",
fuente_fusion == "Solo_CKAN" ~ "orange",
TRUE ~ "gray"
),
popup = ~paste(
"<b>", nombre_clean, "</b><br>",
"Tipo: Auditorio<br>",
"Fuente: ", fuente_fusion, "<br>"
)
) %>%
addLegend(
position = "bottomright",
colors = c("darkgreen", "steelblue", "orange"),
labels = c("Ambas fuentes", "Solo OSM", "Solo CKAN"),
title = "Origen del dato",
opacity = 0.7
) %>%
setView(lng = 2.17, lat = 41.39, zoom = 13) %>%
addProviderTiles("CartoDB.Positron", group = "Claro") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Oscuro") %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "Claro", "Oscuro"),
options = layersControlOptions(collapsed = FALSE)
)A diferencia del mapa anterior, el de hoteles y hosteles muestra un patrón muy distinto: aquí los tres colores aparecen uniformemente repartidos por el mapa, lo que confirma que esta es la categoría donde ambas fuentes se solapan más y donde cada una aporta información valiosa.
Los puntos verdes (ambas fuentes) se concentran principalmente en el centro de la ciudad y en el eje del Eixample, que es precisamente la zona hotelera por excelencia de Barcelona. Los puntos naranja (solo CKAN) indican hoteles registrados oficialmente por el Ayuntamiento pero no presentes en OSM, y los azules (solo OSM) corresponden principalmente a hosteles, una tipología que OSM recoge con bastante detalle pero que no aparece en el catálogo municipal consultado.
En conjunto, el mapa refleja muy bien por qué tiene sentido integrar ambas fuentes: CKAN garantiza la cobertura de los establecimientos hoteleros oficiales, mientras que OSM complementa con los hosteles y algunos hoteles que aún no han sido incorporados al registro municipal.
Ver código del gráfico
hoteles_map <- dataset_web %>%
filter(tipo == c("Hotel", "Hostel"))
leaflet(hoteles_map) %>%
addControl(
html = '<div style="background-color:white; padding:10px; border-radius:5px;
box-shadow:0 2px 5px rgba(0,0,0,0.2);">
<h4 style="margin:0 0 5px 0; color:#2c3e50; font-weight:bold; font-size:14px;">
Hoteles y hosteles de Barcelona</h4>
<p style="margin:0; font-size:11px; color:#7f8c8d; line-height:1.3;">
Distribución geográfica y origen de los datos</p>
</div>',
position = "topright"
) %>%
addPolylines(
data = bcn_muni,
color = "black",
weight = 2,
opacity = 0.8,
smoothFactor = 0.5,
label = "Límite municipal de Barcelona"
) %>%
addTiles() %>%
addCircleMarkers(
radius = 3,
stroke = FALSE,
fillOpacity = 0.6,
color = ~case_when(
fuente_fusion == "Ambas" ~ "darkgreen",
fuente_fusion == "Solo_OSM" ~ "steelblue",
fuente_fusion == "Solo_CKAN" ~ "orange",
TRUE ~ "gray"
),
popup = ~paste(
"<b>", nombre_clean, "</b><br>",
"Tipo: Hotel<br>",
"Fuente: ", fuente_fusion, "<br>"
)
) %>%
addLegend(
position = "bottomright",
colors = c("darkgreen", "steelblue", "orange"),
labels = c("Ambas fuentes", "Solo OSM", "Solo CKAN"),
title = "Origen del dato",
opacity = 0.7
) %>%
setView(lng = 2.17, lat = 41.39, zoom = 13) %>%
addProviderTiles("CartoDB.Positron", group = "Claro") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Oscuro") %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "Claro", "Oscuro"),
options = layersControlOptions(collapsed = FALSE)
)El mapa de teatros es quizás el más interesante de los tres hasta ahora, ya que muestra claramente cómo las dos fuentes se complementan de formas distintas según la zona de la ciudad.
Los puntos verdes (presentes en ambas fuentes) se agrupan principalmente en el centro histórico y el Eixample, que concentra la mayor parte de la actividad teatral de Barcelona. Esto indica que los teatros más conocidos y consolidados están bien documentados tanto en OSM como en el catálogo municipal. Sin embargo, llama la atención la gran cantidad de puntos naranja (solo CKAN) repartidos por toda la ciudad, incluyendo barrios más periféricos: son teatros registrados oficialmente por el Ayuntamiento que la comunidad de OSM todavía no ha incorporado a su base de datos. Mientras que los escasos puntos azules (solo OSM) corresponden a algunos teatros que aparecen en OpenStreetMap pero que no constan en el registro municipal.
Ver código del gráfico
teatros_map <- dataset_web %>%
filter(tipo == "Teatro")
leaflet(teatros_map) %>%
addControl(
html = '<div style="background-color:white; padding:8px; border-radius:5px;
box-shadow:0 2px 5px rgba(0,0,0,0.2); font-weight:bold; font-size:14px;">
Teatros de Barcelona
<p style="margin:0; font-size:11px; color:#7f8c8d; line-height:1.3;">
Distribución geográfica y origen de los datos</p>
</div>',
position = "topright"
) %>%
addPolylines(
data = bcn_muni,
color = "black",
weight = 2,
opacity = 0.8,
smoothFactor = 0.5,
label = "Límite municipal de Barcelona"
) %>%
addTiles() %>%
addCircleMarkers(
radius = 4,
stroke = FALSE,
fillOpacity = 0.7,
color = ~case_when(
fuente_fusion == "Ambas" ~ "darkgreen",
fuente_fusion == "Solo_OSM" ~ "steelblue",
fuente_fusion == "Solo_CKAN" ~ "orange",
TRUE ~ "gray"
),
popup = ~paste(
"<b>", nombre_clean, "</b><br>",
"Tipo: Teatro<br>",
"Fuente: ", fuente_fusion, "<br>"
)
) %>%
addLegend(
position = "bottomright",
colors = c("darkgreen", "steelblue", "orange"),
labels = c("Ambas fuentes", "Solo OSM", "Solo CKAN"),
title = "Origen del dato",
opacity = 0.7
) %>%
setView(lng = 2.17, lat = 41.39, zoom = 13) %>%
addProviderTiles("CartoDB.Positron", group = "Claro") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Oscuro") %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "Claro", "Oscuro"),
options = layersControlOptions(collapsed = FALSE)
)Por otra parte, este mapa reúne todas las infraestructuras culturales del dataset (teatros, auditorios, atracciones, centros de arte y recintos de eventos) en una sola vista, lo que permite tener una imagen de conjunto de la oferta cultural del municipio y de cómo se distribuye según el origen de los datos.
A primera vista se aprecia que los tres colores están bastante repartidos por toda la ciudad, aunque con una clara concentración en el centro y en los barrios del Eixample, Gràcia y el entorno del Raval y el Gótico. Esto refleja la conocida densidad cultural de estas zonas frente a los barrios más periféricos, donde los puntos son más escasos y provienen principalmente de una sola fuente.
Lo más llamativo es el peso de los puntos azules (solo OSM) y naranja (solo CKAN), que en conjunto superan a los verdes. Esto indica que, para estudiar la ofera cutlural, ambas fuentes cubren territorios distintos con poca coincidencia: OSM documenta bien ciertas tipologías como centros de arte o atracciones que no aparecen en el catálogo municipal, mientras que CKAN recoge una gran cantidad de teatros distribuidos por toda la ciudad que aún no están en OSM. Los puntos verdes (ambas fuentes) corresponden principalmente a los teatros más consolidados del centro urbano, que son los que tienen mayor visibilidad en ambas plataformas y que hemos visto en el gráfico anterior.
Este mapa es, en definitiva, una buena síntesis de por qué la integración de fuentes tiene sentido: cada una aporta información que la otra no tiene, y solo combinándolas se obtiene una visión completa de la oferta cultural de Barcelona.
Ver código del gráfico
mapa_cultural <- dataset_web %>%
filter(!tipo %in% c("Hotel", "Hostel"))
leaflet(mapa_cultural) %>%
addControl(
html = '<div style="background-color:white; padding:10px; border-radius:5px;
box-shadow:0 2px 5px rgba(0,0,0,0.2);">
<h4 style="margin:0 0 5px 0; color:#2c3e50; font-weight:bold; font-size:14px;">
Puntos culturales de Barcelona</h4>
<p style="margin:0; font-size:11px; color:#7f8c8d; line-height:1.3;">
Teatros, auditorios, atracciones, centros de arte y recintos de eventos</p>
</div>',
position = "topright"
) %>%
addPolylines(
data = bcn_muni,
color = "black",
weight = 2,
opacity = 0.8
) %>%
addTiles() %>%
addCircleMarkers(
radius = 4,
stroke = FALSE,
fillOpacity = 0.7,
color = ~case_when(
fuente_fusion == "Ambas" ~ "darkgreen",
fuente_fusion == "Solo_OSM" ~ "steelblue",
fuente_fusion == "Solo_CKAN" ~ "orange"
),
popup = ~paste(
"<b>", nombre_clean, "</b><br>",
"Tipo: ", tipo, "<br>", # Muestra tipo real
"Fuente: ", fuente_fusion
)
) %>%
addLegend(
position = "bottomright",
colors = c("darkgreen", "steelblue", "orange"),
labels = c("Ambas fuentes", "Solo OSM", "Solo CKAN"),
title = "Origen del dato"
) %>%
setView(lng = 2.17, lat = 41.39, zoom = 13) %>%
addProviderTiles("CartoDB.Positron", group = "Claro") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Oscuro") %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "Claro", "Oscuro"),
options = layersControlOptions(collapsed = FALSE)
)Este último mapa muestra el dataset completo con todos los puntos integrados, diferenciando ahora por tipo de equipamiento en lugar de por origen del dato.
Es la visualización más completa de los cinco gráficos y permite ver de un vistazo cómo se distribuye la oferta cultural y turística de Barcelona en su conjunto.
Lo primero que llama la atención es la enorme presencia de puntos morados (hoteles) y pistacho (hosteles), que dominan claramente el mapa y se concentran en el centro de la ciudad, especialmente en el Eixample y la zona del Passeig de Gràcia hacia el mar. Esto es coherente con lo que ya veíamos en las tablas: los hoteles son con diferencia la categoría más numerosa del dataset.
Los puntos rosas (teatros) aparecen bien repartidos por toda la ciudad, con mayor densidad en el centro histórico, el Raval y el Eixample. Los rojos (atracciones) y los verdes (auditorios) son mucho más escasos y se sitúan de forma dispersa, mientras que los cyan (centros de arte) y los naranja (recintos de eventos) aparecen puntualmente en zonas concretas.
En conjunto, el mapa pone de manifiesto una realidad bien conocida de Barcelona: la actividad cultural y turística se concentra de forma muy marcada en el centro de la ciudad, con una densidad que va disminuyendo progresivamente hacia los barrios periféricos.
Ver código del gráfico
# Definir paleta de colores por tipo
pal_tipo <- colorFactor(
palette = c(
"Hotel" = "#FA1616",
"Hostel" = "#57D437",
"Teatro" = "#45B7D1",
"Auditorio" = "#1EEBD2",
"Museo" = "#80E012",
"Centro de arte" = "#811EEB",
"Atracción" = "#98D8C8",
"Recinto de eventos" = "#E07212",
"Sala de música" = "#E0128A"
),
domain = dataset_web$tipo
)
leaflet(dataset_web) %>%
addControl(
html = '<div style="background-color:white; padding:10px; border-radius:5px;
box-shadow:0 2px 5px rgba(0,0,0,0.2);">
<h4 style="margin:0 0 5px 0; color:#2c3e50; font-weight:bold; font-size:14px;">
Oferta cultural e infraestructuras hoteleras en Barcelona</h4>
<p style="margin:0; font-size:11px; color:#7f8c8d; line-height:1.3;">
Distribución por tipo de equipamiento</p>
</div>',
position = "topright"
) %>%
addPolylines(data = bcn_muni, color = "black", weight = 3, opacity = 0.8) %>%
addTiles() %>%
addCircleMarkers(
radius = 4,
stroke = FALSE,
fillOpacity = 0.8,
color = ~pal_tipo(tipo),
fillColor = ~pal_tipo(tipo),
popup = ~paste(
"<b>", nombre_clean, "</b><br>",
"Tipo: ", tipo, "<br>",
"Fuente: ", fuente_fusion
),
group = "Todos"
) %>%
addLegend(
position = "bottomright",
pal = pal_tipo,
values = ~tipo,
title = "Tipo de Equipamiento",
opacity = 0.8,
na.label = "Sin clasificar"
) %>%
setView(lng = 2.17, lat = 41.39, zoom = 13) %>%
addProviderTiles("CartoDB.Positron", group = "Claro") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Oscuro") %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "Claro", "Oscuro"),
options = layersControlOptions(collapsed = FALSE)
)Este trabajo ha permitido construir un dataset integrado de infraestructuras culturales y de alojamiento de Barcelona a partir de OpenStreetMap y el catálogo oficial del Ayuntamiento, obteniendo 867 registros únicos con una cobertura más completa que cualquiera de las dos fuentes por separado.
Los mapas generados confirman una pauta territorial clara: tanto la oferta cultural como los hoteles y hosteles se concentran en el centro de la ciudad, disminuyendo progresivamente hacia los barrios periféricos. Esto tiene todo el sentido, ya que el centro urbano es históricamente el espacio con mayor vida social y cultural.
No es casualidad tampoco que ambas cosas coincidan: donde hay más teatros, auditorios y atracciones, hay también más hoteles, ya que los alojamientos se instalan precisamente allí para apoyar y retroalimentar la oferta cultural que atrae a los visitantes.
En definitiva, combinar fuentes abiertas de distinta naturaleza permite obtener una visión más rica y completa del territorio.
Los ficheros generados con el procedimiento previamente descrito se puede descargar de aquí.

Proyecto de Innovación Educativa Emergente (PIEE-3898312)