libs <- c("tidyverse", "sf", "mapSpain", "osmdata", "ggspatial", "leaflet", "gridExtra", "tidygeocoder", "prettymapr", "grid", "giscoR", "RColorBrewer", "readr", "ggmap", "leaflet.extras2", "htmltools")
installed_libs <- libs %in% rownames(installed.packages())
if (any(installed_libs == F)) {install.packages(libs[!installed_libs])}
invisible(lapply(libs, library, character.only = T))
rm(libs, installed_libs)
Datos Hospitales con Helipuertos España
Input
En el análisis de la localización de hospitales con y sin helipuerto cercano en España se han empleado dos conjuntos de datos provenientes de fuentes oficiales y colaborativas.
En el portal del Ministerio de Sanidad, dentro del apartado de ciudadanos, se encuentra disponible el Catálogo Nacional de Hospitales, el cual recoge información detallada y actualizada de todos los centros hospitalarios públicos y privados del territorio español. Este catálogo incluye datos como el nombre del hospital, su titularidad, el tipo de asistencia que presta, y su localización administrativa (comunidad autónoma, provincia, municipio y dirección postal).
Estos datos están disponibles para su descarga en formato Excel, pdf y csv. En este caso, hemos utilizado el fichero csv.
- Formato: Archivo de texto plano (.csv)
- Fecha: Año 2024 (actualizado a 31 de diciembre 2023).
- Licencia: Uso público, la información contenida en esta base de datos se facilita con fines puramente informativos
- Enlace de origen: https://www.sanidad.gob.es/ciudadanos/centros.do
Por otro lado, la información relativa a los helipuertos se obtiene a partir de los datos abiertos disponibles en OpenStreetMap (OSM), utilizando la etiqueta aeroway=helipad. Esta base de datos colaborativa y de código abierto ofrece información geográfica continuamente actualizada por voluntarios y comunidades locales. La descarga y filtrado de los helipuertos se realiza mediante el uso del paquete osmdata en R.
- Fecha: Información comunitaria, alimentada de manera continua por los usuarios de OSM.
- Licencia de uso: Bajo los términos de Creative Commons Atribución-CompartirIgual 2.0 (CC BY-SA 2.0).
- Formatos disponibles: Puede descargarse en formatos OSM, XML, SHP, GeoJSON, GPKG, entre otros, mediante herramientas y servicios de terceros (Geofabrik, Overpass API…).
- Servicios de visualización: Mapas en línea (el propio visor de OpenStreetMap) y múltiples aplicaciones GIS compatibles.
Mediante la combinación de ambas fuentes (hospitales en España y helipuertos de OSM) se procede a analizar la localización de los hospitales y su proximidad a una infraestructura de helipuerto. Esta información es de gran valor para la gestión de emergencias médicas y la planificación sanitaria, ya que permite evaluar la accesibilidad aérea de los centros hospitalarios y detectar posibles carencias en la red de transporte sanitario aéreo.
Descripción
Cargamos las librerías necesarias:
Leemos el primer conjunto de datos que es el listado oficial de hospitales de España (Ministerio de Sanidad):
hospitales_espanya <- read_csv("data/hospitales_espanya.csv", locale = locale(encoding = "UTF-8"))
hospitales_espanya <- as.data.frame(hospitales_espanya)
Se trata de un dataframe con 838 observaciones y las 15 siguientes variables:
Código.CCN: Código del Catálogo de Centros Nacionales. Es un identificador único para cada centro hospitalario.
Código.CNH: Código Nacional de Hospitales. También un identificador, utilizado en estadísticas sanitarias.
Nombre: Nombre oficial del hospital.
Comunidad.Autónoma, Provincia, Municipio: Localización administrativa del hospital.
Dirección: Calle o ubicación física del centro.
Código.Postal: Código postal del hospital.
Teléfono: Teléfono de contacto del centro.
Número.de.camas.instaladas: Capacidad de hospitalización medida en camas.
Tipo.de.Centro: Define si es un hospital general, de media estancia, de salud mental, etc.
Dependencia.Funcional: Indica el organismo responsable (por ejemplo, servicios autonómicos de salud, mutuas, privados…).
Concierto: Si el hospital tiene conciertos con el sistema público.
Acreditación.Docente: Si el hospital está acreditado para la formación (residencias MIR, EIR, etc.).
Apreciamos que no incluye coordenadas geográficas, por lo que, para poder visualizar y analizar estos datos en un contexto espacial, va a ser necesario obtenerlas mediante un proceso de geocodificación. Además, es importante revisar y seleccionar las variables más relevantes, ya que puede que no todas aporten valor al análisis.
Ahora, obtenemos los datos sobre helipuertos en España disponibles en OpenStreetMap (OSM):
bbox_espanya <- getbb("España")
helipuertos_espanya <- opq(bbox = bbox_espanya, timeout = 1000) %>%
add_osm_feature(key = "aeroway", value = "helipad")
puntos_helipuertos_esp <- osmdata_sf(helipuertos_espanya)$osm_points
La localización de helipuertos se obtiene filtrando aquellas entidades etiquetadas con aeroway=helipad. La descarga y filtrado se realiza utilizando el paquete osmdata en R, acotando el ámbito geográfico al territorio nacional español.
Seleccionamos únicamente los puntos, pues si tuviéramos polígonos (por ejemplo, zonas de aterrizaje), nuestro análisis debería considerar intersecciones y no solo la distancia euclidia. Aquí lo que nos importa es si un hospital específico tiene un helipuerto cercano, lo cual se mide con distancias punto a punto.
puntos_helipuertos_esp %>%
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::addCircles()
Al hacer un esbozo de los datos referentes a los helipuertos nos damos cuenta de que hay algunos representados por más de un punto por lo que posteriormente vamos a tener que tratar dicha problemática para evitar la duplicación de puntos y mejorar la visualización de la información. Este paso es crucial para evitar que múltiples registros representen el mismo helipuerto en la base de datos.
Además, la consulta realizada a OSM nos ha devuelto algún punto fuera del territorio español. Para garantizar que los datos son precisos y evitar incluir elementos fuera de España, vamos a tener que filtrar los helipuertos que realmente están dentro de los límites administrativos de España.
El objeto generado a partir de OpenStreetMap, incluye una gran cantidad de columnas provenientes de las etiquetas de OSM como name, access, aeroway, iata, operator, source, wikidata, geometry, entre muchas otras. Sin embargo, al observar el contenido del dataframe, se aprecia que la mayoría de las columnas están casi completamente vacías, con valores NA en la gran mayoría de registros. Esto indica que, aunque OpenStreetMap permite incluir mucha información adicional para cada elemento (como accesibilidad, operador, idioma, etiquetas en varios idiomas, etc.), en la práctica esta información no siempre está completa o cargada por los usuarios.
Por este motivo, va a ser necesario hacer una evaluación cuidadosa de las variables y quedarnos únicamente con aquellas que aporten valor al análisis.
Tratamiento
En primer lugar, convertimos el archivo hospitales_espanya.csv en un conjunto de datos espaciales (sf), a través de geocodificación basada en la dirección.
Se parte de un conjunto de datos en formato CSV que contiene direcciones de hospitales sin información geográfica. Para poder realizar un análisis espacial, es necesario transformar esas direcciones en coordenadas (latitud y longitud). Para ello, se unifica la dirección completa (calle, municipio, provincia y país) en una sola columna y, posteriormente, se utiliza la clave de la API de Google para geocodificar cada registro y así obtener su posición en un mapa.
Tras recibir las coordenadas, los resultados se integran de nuevo en el dataframe original y se transforman en un objeto espacial (sf), lo que permite manejarlo y visualizarlo en sistemas de información geográfica. Finalmente, se guarda el resultado en formato GeoPackage para tener un archivo con la información georreferenciada ya procesada y lista para futuros análisis sin necesidad de repetir la geocodificación.
## El primer paso realizado es concatenar la dirección completa para proceder
## a la geocodificación:
hospitales <- hospitales_espanya %>%
mutate(direccion_completa = paste(Nombre, Municipio, Provincia, "España", sep = ", "))
## Luego, utilizamos la función geocode() del paquete ggmap para buscar las
## coordenadas (latitud y longitud) de cada dirección en la columna direccion_completa:
# Registramos la clave API:
register_google(key = google_key)
# Geocodificamos la columna de direcciones:
resultado <- geocode(hospitales$direccion_completa, output = "latlona")
# El argumento output = "latlona" significa que:
# - lat: columna de latitud.
# - lon: columna de longitud.
# - a: dirección formateada devuelta por Google
## Combinamos el resultado con el dataframe original
hospitales_espanya <- hospitales %>%
select(-direccion_completa) %>% # Eliminamos la columna creada para geocodificar
bind_cols(select(resultado, -address)) # Eliminamos el'address' devuelto por Google
## Sólo queda convertirlo a un objeto espacial sf
hospitales_espanya <- st_as_sf(hospitales_espanya, coords = c("lon", "lat"), crs = 4326)
## Por último, guardamos el resultado en formato GeoPackage (para no tener que
## repetir el proceso cada vez que se ejecute el informe)
st_write(hospitales_espanya, "data/hospitales_geocodificados.gpkg")
Ahora, realizamos un estudio de los valores faltantes y de la relevancia de las variables para hacer una selección.
Código.CCN Código.CNH
0 0
Nombre Comunidad.Autónoma
0 0
Provincia Municipio
0 0
Dirección Código.Postal
0 0
Teléfono Número.de.camas.instaladas
0 0
Tipo.de.Centro Dependencia.Funcional
0 0
Observaciones Concierto
100 0
Acreditación.Docente geom
0 0
El resultado muestra que prácticamente todas las columnas presentan un 0% de valores perdidos, excepto la columna “Observaciones”. Esto significa que la calidad de los datos es muy buena en todas las variables menos en esa. Dado que “Observaciones” está completamente vacía, su utilidad para el análisis es nula, por lo que resulta razonable eliminarla para evitar almacenar información sin contenido y mantener solo las variables que realmente aportan valor al estudio.
hospitales_espanya <- hospitales_espanya %>%
select(-Observaciones)
Sólo nos queda comprobar que todos y cada uno de los datos (puntos) correspondan a hospitales dentro de España. Hacemos la comprobación a través de un plot:
hospitales_espanya %>%
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::addCircles()
En efecto, ahora sí que podemos decir que tenemos un objeto sf que almacena la información geográfica de los hospitales en España junto con sus atributos. Esto nos permite trabajar directamente con la capa espacial en entornos GIS y realizar análisis espaciales (como calcular distancias, generar mapas temáticos o consultar propiedades de cada punto) de manera más eficaz.
Ahora, debemos centrarnos en los puntos de helipuertos obtenidos a través de la consulta a OSM.
Previamente, al hacer un esbozo de los datos nos hemos dado cuenta de que hay helipuertos representados por más de un punto por lo que se aplica el siguiente tratamiento para evitar la duplicación de puntos y mejorar la visualización de la información. Para ello, primero se generan buffers alrededor de los helipuertos, con radio de 100 metros. Estos buffers permiten definir un área de influencia para cada punto y determinar si hay solapamientos entre helipuertos cercanos.
A continuación, se identifican los grupos de puntos que se solapan utilizando la función st_intersects(), lo que permite agrupar helipuertos que se encuentran dentro de sus respectivos buffers. Una vez identificados los grupos, se asigna un identificador único a cada grupo utilizando sapply(), tomando el primer elemento del grupo como referencia. Este paso es crucial para evitar que múltiples registros representen el mismo helipuerto en la base de datos.
Posteriormente, se selecciona un único punto representativo por grupo. En este caso, se utiliza slice(1) para tomar simplemente el primer punto de cada grupo.
Este procedimiento es fundamental para evitar la sobre-representación de helipuertos en el mapa, ya que en su formato original los datos contienen múltiples puntos asociados a una misma infraestructura. Al reducir la redundancia, se obtiene una visualización más clara y precisa, facilitando el análisis geoespacial de la distribución de hospitales con helipuerto en el territorio español.
## Creamos buffers alrededor de los puntos:
buffers_helipuertos <- st_buffer(puntos_helipuertos_esp, dist = 100)
## Identificamos grupos cuyos buffers se solapan:
grupos_helipuertos <- st_intersects(buffers_helipuertos)
## Creamos un identificador único de grupo:
puntos_helipuertos_esp$grupo <- sapply(grupos_helipuertos, function(g) if(length(g) > 0) g[1] else NA)
## Seleccionamos un punto representativo por grupo (el primero por ejemplo):
puntos_helipuertos_esp_unicos <- puntos_helipuertos_esp %>%
group_by(grupo) %>%
slice(1) %>%
ungroup()
Para garantizar que los datos sean precisos y evitar incluir elementos fuera del territorio español, es necesario filtrar los helipuertos que realmente estén dentro de los límites administrativos de España:
espanya <- esp_get_ccaa(moveCAN = FALSE) %>%
st_union() %>% # Une todas las CCAA para formar el límite del país
st_transform(4326)
puntos_helipuertos_esp <- st_intersection(puntos_helipuertos_esp_unicos, espanya)
## Seleccionamos solo las columnas que existían antes de la intersección:
columnas_comunes_hc <- intersect(names(puntos_helipuertos_esp),
names(puntos_helipuertos_esp_unicos))
## Aplicamos la selección solo con las columnas existentes:
puntos_helipuertos_esp <- puntos_helipuertos_esp %>% select(all_of(columnas_comunes_hc))
Primero, esp_get_ccaa() obtiene los límites de las Comunidades Autónomas de España y st_union() los combina en un único polígono que representa el país completo. Luego, st_transform(espanya, 4623) cambia el sistema de coordenadas a EPSG:4623 para asegurar que sea compatible con los datos obtenidos de OpenStreetMap (OSM).
A continuación, st_intersection() filtra los helipuertos, manteniendo solo aquellos que se encuentran dentro de los límites de España. Después, se identifican las columnas comunes entre los datos originales y los filtrados con intersect(), lo que permite seleccionar únicamente las variables que existían antes de la intersección. Finalmente, select(all_of(…)) aplica esta selección, asegurando que el conjunto de datos resultante conserve su estructura y sea compatibles con análisis posteriores.
Este proceso es esencial para garantizar la precisión de los datos, evitando la inclusión de registros erróneos que puedan pertenecer a países vecinos y asegurando que el estudio se basa exclusivamente en elementos ubicados dentro del territorio español.
En relación a las variables del conjunto de datos resultante, debemos estudiarlas para hacer una selección, haciendo que la base de datos sea más manejable. Muchas de las variables en el dataset contienen información irrelevante para nuestro estudio, como detalles sobre accesibilidad, métodos de pago, datos administrativos específicos o información con demasiados valores nulos.
Por ejemplo, columnas como bicycle, barrier, camera:type, currency:ETH, leaf_cycle, payment:bitcoin, entre muchas otras, no aportan valor en el contexto del análisis de hospitales con helipuertos.
En el conjunto tenemos 34 variables. Dado que la mayoría de las variables contienen valores vacíos, nos quedaremos solo con las siguientes:
osm_id: Es el identificador único del helipuerto en OpenStreetMap. Nos permite referenciar cada elemento de manera inequívoca.
name: Aunque la mayoría de los registros tienen valores vacíos en esta variable, si está presente, nos permite identificar el helipuerto por su nombre.
geometry: Contiene las coordenadas espaciales de los helipuertos, esenciales para la representación en mapas y el análisis espacial.
El resto de variables contienen valores nulos en la mayoría de los registros y no aportan información útil para el análisis.
puntos_helipuertos_esp <- puntos_helipuertos_esp %>%
select(osm_id, name, geometry)
Esto facilita la interpretación de los datos, mejora la eficiencia computacional y evita ruido innecesario en nuestro estudio.
Una vez hechas todas las transformaciones necesarias a nuestros inputs, vamos a hacer el tratamiento apropiado para crear dos bases de datos a partir de éstos.
## Encontramos el índice del helipuerto más cercano a cada hospital:
indice_helipuerto_mas_cercano <- st_nearest_feature(hospitales_espanya, helipuertos_espanya)
## Extraemos los helipuertos más cercanos:
helipuertos_cercanos <- helipuertos_espanya[indice_helipuerto_mas_cercano, ]
## Creamos un nuevo df uniendo la info de hosp. con la de los helipuertos más cercanos:
hospitales_helipuertos <- cbind(hospitales_espanya, helipuertos_cercanos)
## Calculamos la distancia real entre cada hospital y su helipuerto más cercano:
hospitales_helipuertos$Dist.entre.hosp.heli.m <-
st_distance(hospitales_espanya, helipuertos_cercanos, by_element = TRUE)
## Convertimos la distancia a número (st_distance devuelve unidades en metros)
hospitales_helipuertos$Dist.entre.hosp.heli.m <-
as.numeric(hospitales_helipuertos$Dist.entre.hosp.heli.m)
## Creamos la variable dicotómica "helipuerto_cercano":
hospitales_helipuertos <- hospitales_helipuertos %>%
mutate(Helipuerto.cercano = ifelse(Dist.entre.hosp.heli.m <= 500, 1, 0))
## Renombramos y reordenamos algunas variables para aportar una mayor claridad:
hospitales_helipuertos <- hospitales_helipuertos %>%
rename(
OSM.id.helipuerto = osm_id,
Nombre.helipuerto = name,
geom.hospital = geom,
geom.helipuerto = geom.1
) %>%
select(
everything(),
Helipuerto.cercano,
Dist.entre.hosp.heli.m,
OSM.id.helipuerto,
Nombre.helipuerto,
geom.hospital,
geom.helipuerto
)
Generamos la primera base de datos a partir del actual objeto hospitales_helipuertos. En esta base de datos incluímos todos y cada uno de los hospitales que hemos encontrado en el catálogo nacional añadiendo la variable dicotómica Helipuerto.cercano creada previamente (toma el valor de 1 si hay un helipuerto a menos de 500 metros y 0 en caso contrario):
hospitales_espanya <- hospitales_helipuertos %>%
select(-c(Dist.entre.hosp.heli.m, OSM.id.helipuerto, Nombre.helipuerto, geom.helipuerto))
A partir, también, del objeto hospitales_helipuertos generamos una segunda base de datos que almacena únicamente los hospitales con un helipuerto cercano (a menos de 500 metros) y la información y ubicación del helipuerto más cercano a cada uno de los hospitales:
hospitales_con_helipuerto_espanya <- hospitales_helipuertos %>%
filter(Helipuerto.cercano == 1) %>%
select(-c(Helipuerto.cercano, Nombre.helipuerto))
## Eliminamos la variable Helipuerto.cercano porque es redundante (siempre tiene
## valor 1) y eliminamos, también, la variable Nombre.helipuerto por la elevada
## cantidad de valores faltantes.
En el marco de nuestro análisis, acabamos de construir un objeto espacial de tipo sf que contiene dos columnas con geometría puntual (POINT). La primera de ellas representa la localización de cada hospital (geom_hospital), mientras que la segunda corresponde a la ubicación del helipuerto más cercano a dicho hospital (geom_helipuerto). Esta estructura nos permite conservar, dentro de un mismo conjunto de datos, tanto la referencia espacial de la entidad principal (el hospital) como la de la infraestructura asociada (el helipuerto).
Es crucial señalar que, aunque técnicamente es posible almacenar múltiples columnas con geometría dentro de un objeto sf, este solo puede tener una geometría activa al mismo tiempo. Esta geometría activa es la que se utilizará por defecto en operaciones espaciales como el cálculo de distancias, visualización en mapas o exportación a formatos geoespaciales como GeoPackage o Shapefile. En nuestro caso, la geometría activa por defecto es la de los hospitales, lo cual es coherente con el objetivo principal del análisis: identificar qué hospitales cuentan con un helipuerto en un radio máximo de 500 metros. La unidad espacial de referencia son, por tanto, los hospitales, ya que el análisis y la toma de decisiones se construyen a partir de su localización.
No obstante, al haber conservado también la geometría del helipuerto más cercano, mantenemos la flexibilidad analítica para cambiar el enfoque si fuese necesario. Si en algún momento se quisiera realizar un análisis centrado en la distribución o cobertura de los helipuertos, bastaría con activar la columna correspondiente (geom_helipuerto) como geometría principal mediante la función st_geometry().
Output
Por lo tanto, hemos obtenido dos outputs.
El primero tiene 838 observaciones y 16 variables:
Cada una de las observaciones corresponde a un hospital situado en territorio español y en el conjunto de datos podemos encontrar información valiosa cómo su dirección, dependencia funcional, número de camas instaladas y si tienen un helipuerto cercano o no.
Ver código del gráfico
## Creamos la paleta de colores:
pal <- colorFactor(
palette = c("darkred", "seagreen4"),
domain = hospitales_espanya$Helipuerto.cercano
)
## Obtenemos los distintos tipos de centro para crear las capas:
tipos_centro <- unique(hospitales_espanya$Tipo.de.Centro)
mapa <- leaflet(hospitales_espanya) %>%
addTiles()
## Añadimos los puntos de cada tipo de centro como grupo independiente:
for (tipo in tipos_centro) {
mapa <- mapa %>%
addCircleMarkers(
data = filter(hospitales_espanya, Tipo.de.Centro == tipo),
radius = 5,
color = ~pal(Helipuerto.cercano),
stroke = FALSE,
fillOpacity = 0.9,
label = ~lapply(
paste0("<strong>", Nombre, "</strong><br/>Camas: ", Número.de.camas.instaladas),
htmltools::HTML
),
group = tipo
)
}
## Añadimos la leyenda y el control de capas al mapa:
mapa <- mapa %>%
addLegend(
position = "bottomright",
pal = pal,
values = ~Helipuerto.cercano,
title = "Helipuerto cercano",
labFormat = function(type, cuts, p) c("No", "Sí")
) %>%
addLayersControl(
overlayGroups = tipos_centro,
options = layersControlOptions(collapsed = TRUE)
)
mapa
sf::st_write(hospitales_espanya, "hospitales_espanya.gpkg")
El segundo objeto creado tiene 193 observaciones y 18 variables.
class(hospitales_con_helipuerto_espanya)
[1] "sf" "data.frame"
dim(hospitales_con_helipuerto_espanya)
[1] 193 18
Y, cómo hemos explicado previamente, almacena la información de los hospitales en España que sí que tienen un helipuerto (en un radio máximo de 500 metros) y la información del helipuerto más cercano a cada hospital.
Ver código del gráfico
## Definimos un icono personalizado para los helipuertos:
helipuerto_icon <- icons(
iconUrl = "imgs/helipad_icon.png", # Icono de helipuerto
iconWidth = 10, iconHeight = 10
)
legend_html <- "
<div style='padding: 10px; background-color: white; border: 1px solid black;'>
<div style='display: flex; align-items: center;'>
<div style='width: 20px; height: 20px; background-color: blue; border: 1px solid black; margin-right: 5px;'></div>
<span>Hospital</span>
</div>
<div style='display: flex; align-items: center; margin-top: 5px;'>
<div style='width: 20px; height: 20px; background: linear-gradient(90deg, red 50%, white 50%); border: 1px solid black; margin-right: 5px;'></div>
<span>Helipuerto</span>
</div>
</div>
"
helipuertos_sf <- hospitales_con_helipuerto_espanya
sf::st_geometry(helipuertos_sf) <- "geom.helipuerto"
mapa_hospitales_helipuertos <- leaflet() %>%
addTiles() %>%
addCircleMarkers(data = hospitales_con_helipuerto_espanya,
color = "blue", radius = 7,
stroke = FALSE, fillOpacity = 1,
label = ~Nombre) %>%
addMarkers(data = helipuertos_sf,
icon = helipuerto_icon) %>%
addControl(html = legend_html, position = "bottomright")
htmltools::div(style = "fig-align: center;", mapa_hospitales_helipuertos)
Cuando intentamos guardar un objeto con múltiples geometrías en un solo archivo .gpkg sin ningún tipo de tratamiento previo, R a través de sf::st_write() conserva únicamente la geometría activa. Esto se traduce en la pérdida de información espacial relevante, como es el caso de la columna geom.helipuerto.
Para evitar esta pérdida y conservar ambas ubicaciones (la del hospital y la del helipuerto) es necesario separar los datos en diferentes capas dentro del mismo archivo .gpkg. Este enfoque permite guardar múltiples conjuntos de datos espaciales relacionados, cada uno con su propia geometría activa, en una única estructura de archivo.
Esta solución no solo conserva toda la información, sino que también es compatible con programas GIS como QGIS o ArcGIS, que pueden leer todas las capas dentro de un GeoPackage. Sin embargo, una implicación importante es que las dos capas están físicamente separadas dentro del archivo. Esto significa que cualquier análisis conjunto posterior (por ejemplo, unir de nuevo hospital con su helipuerto) requiere algún tipo de relación entre ambas capas, como un identificador común o una clave compartida.
Aunque manejar capas separadas añade un pequeño paso adicional, compensa ampliamente por la integridad de los datos y la flexibilidad que proporciona tanto para análisis como para visualización.
## Capa 1: Guardamos hospitales (geometría activa: hospital)
sf::st_write(
hospitales_con_helipuerto_espanya,
"hospitales_con_helipuerto_espanya.gpkg",
layer = "hospitales"
)
## Capa 2: Cambiamos la geometría activa a helipuerto
helipuertos <- hospitales_con_helipuerto_espanya
sf::st_geometry(helipuertos) <- "geom.helipuerto"
## Guardamos como segunda capa (añadiendo al mismo .gpkg)
sf::st_write(
helipuertos,
"hospitales_con_helipuerto_espanya.gpkg",
layer = "helipuertos",
append = TRUE
)
No obstante, es importante señalar que existe una alternativa válida cuando no se requiere operar espacialmente con ambas geometrías. Se trata de transformar la geometría secundaria (en este caso, la del helipuerto) a un formato no espacial, como texto en formato WKT (Well-Known Text) o como columnas separadas de coordenadas (X, Y o latitud, longitud). Esta opción implica convertir la geometría a una cadena de texto que describe su forma, pero ya no será tratada como un objeto espacial.
Esta solución alternativa tiene la ventaja de que permite conservar la información de ambas geometrías dentro de una sola capa del archivo .gpkg, sin necesidad de dividir los datos. Esto puede resultar útil en casos donde se prioriza la simplicidad del archivo o cuando la geometría secundaria se utiliza solo como referencia visual o informativa, y no se requiere realizar análisis espaciales directamente sobre ella.
Sin embargo, esta elección también tiene implicaciones. Al convertir una geometría a texto o coordenadas simples, se pierde la capacidad de utilizar directamente las funciones espaciales sobre esa información. No se podrían calcular distancias, realizar intersecciones ni visualizarla como un objeto geográfico sin antes volver a transformarla a su formato espacial original (sf). Por tanto, esta opción es recomendable únicamente cuando la geometría secundaria no forma parte activa del análisis espacial y se utiliza solo como atributo descriptivo.
En este trabajo, se ha optado por una solución más completa y analíticamente potente. Es decir, la creación de capas separadas para cada tipo de geometría, garantizando así la integridad y usabilidad total de los datos espaciales.
Los ficheros generados con el procedimiento descrito anteriormente se pueden descargar, en formato GeoPackage (gpkg), de aquí.
Proyectos de Innovación Educativa Emergente PIEE-2737007 y PIEE-3325394