Datos de alojamientos en la Comunidad Valenciana

GEOLOCALIZACIÓN
COMUNIDAD VALENCIANA
ALOJAMIENTOS
Autor/a
Afiliación

Universitat de València

Fecha de publicación

10 de mayo de 2024

Input

Se ha obtenido un conjunto de datos del catálogo de datos de datos.gob.es, una web que ofrece información pública proveniente de distintas entidades también públicas de España.

Los datos obtenidos en concreto son de turismo sobre alojamientos en la Comunidad Valenciana, publicados por la Generalitat Valenciana. Se pueden descargar aquí.

Se descarga un fichero CSV.

Descripción

Se empieza cargando las librerías:

Se lee la base de datos:

hoteles <- readr::read_delim(file = "data/hoteles.csv",
                             delim = ";",
                                      locale = readr::locale(encoding = "ISO-8859-2"))
Rows: 1400 Columns: 30
── Column specification ────────────────────────────────────────────────────────
Delimiter: ";"
chr (25): Signatura, Cod. Estado, Estado, Cod. Categoria, Categoría, Nombre,...
dbl  (5): Cod. Provincia, Cod. Municipio, CP, Habitaciones, Plazas

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Se detecta que, pese a que la información es de gran interés, el dataset no dispone de ningún sistema de referencia, por lo que se hace imposible su conversión en objeto espacial.

El objetivo de esta práctica es seleccionar las zonas de la Comunidad más y menos lujosas según la categoría de sus hospedajes. Para ello, se hace necesaria la geolocalización de cada uno de ellos.

Primero de todo, se añade la variable ‘DIRECCIÓN’ al final de la base de datos. Esta se forma a partir de las variables existentes ‘Dirección’, que contiene el tipo de vía, la vía y el número, y ‘Municipio’, compuesta por el nombre del municipio al que pertenece.

Acto seguido, al haber 1400 observaciones, se toma una muestra aleatoria simple de 15 observaciones para que la geolocalización se haga más amena.

hoteles <- hoteles %>%
  mutate(DIRECCION = paste0(Dirección, ",", " ", Municipio))

set.seed(444)
obs <- sample(nrow(hoteles), 15)
muestra <- hoteles[obs,]

Tratamiento

Una vez tomada la muestra, se obtienen las coordenadas con tres métodos distintos: una con OSM I, la segunda con OSM II y la tercera con ArcGIS.

tidygeocoder::geo() con OSM I

crdsm_osm <- geo(address = muestra$DIRECCION)
Passing 15 addresses to the Nominatim single address geocoder
Query completed in: 15.2 seconds
sum(is.na(crdsm_osm$lat))
[1] 5

tidygeocoder::geo() con OSM II

crdsm_osm2 <- geo(street = paste(muestra$Via,muestra$Número, sep = ", "),
             city = paste(muestra$Provincia),
             country = rep("Spain",nrow(muestra)),
             postalcode = muestra$CP)
Passing 15 addresses to the Nominatim single address geocoder
Query completed in: 15.3 seconds
sum(is.na(crdsm_osm2$lat))
[1] 4

tidygeocoder::geo() con ArcGIS

crdsm_arcgis <- geo(address = muestra$DIRECCION, method = "arcgis")
Passing 15 addresses to the ArcGIS single address geocoder
Query completed in: 6.3 seconds
sum(is.na(crdsm_arcgis$lat))
[1] 0

De todas las geolocalizaciones realizadas, la única que no presenta ningún valor anómalo con los datos de la muestra, además de ser la que más rápido lo hace, es la realizada con el método ArcGIS.

crdsm_arcgis_sf <- sf::st_as_sf(crdsm_arcgis, coords = c("long", "lat"), crs = 4326)
crdsm_arcgis_sf <- cbind(crdsm_arcgis_sf, muestra$Nombre)
leaflet::leaflet() %>%
  leaflet::addMarkers(data = crdsm_arcgis_sf, popup = ~muestra.Nombre) %>%
  leaflet::addProviderTiles("Esri.WorldImagery")

Así pues, esta será la técnica que se utilizará para convertir la base de datos entera en un objeto espacial con el sistema de referencia que convenga.

crds_arcgis <- tidygeocoder::geo(address = hoteles$DIRECCION, method = "arcgis")
Passing 1,393 addresses to the ArcGIS single address geocoder
Query completed in: 711.2 seconds
sum(is.na(crds_arcgis$lat))
[1] 1
observacion_na <- crds_arcgis %>%
  filter(rowSums(is.na(.)) > 0)
observacion_na$address
[1] "PARTIDA EL BUSCARRO, 1, TŔRBENA"
crds_arcgis %>%
  leaflet::leaflet() %>%
  leaflet::addTiles() %>%
  leaflet::addCircles()
Assuming "long" and "lat" are longitude and latitude, respectively
Warning in validateCoords(lng, lat, funcName): Data contains 1 rows with either
missing or invalid lat/lon values and will be ignored

Cuando ya se tiene el objeto espacial, se observan dos nuevos problemas. El primero de ellos es que hay una observación que presenta valores anómalos tanto en la longitud como en la latitud. El segundo es que este objeto sitúa primero la latitud y, después la longitud, haciendo que el sistema de referencia sea YX en vez de XY (sistema por defecto para extraer, posteriormente, los puntos del objeto sf).

Para solucionar el primer inconveniente hay que modificar la base de datos original eliminando la observación con NAs.

crds_arcgis2 <- crds_arcgis %>% filter(complete.cases(.))
sum(is.na(crds_arcgis2$lat))
[1] 0

Respecto a la segunda incidencia, simplemente hay que cambiar el orden de las columnas y, posteriormente, volver a transformarla en un objeto espacial.

crds_arcgis2 <- crds_arcgis2[, c(1:(ncol(crds_arcgis2) - 2), ncol(crds_arcgis2), (ncol(crds_arcgis2) - 1))]
crds_arcgis_sf <- sf::st_as_sf(crds_arcgis2, coords = c("long", "lat"), crs = 4326)

# Eliminamos de "hoteles" la observación no válida y le añadimos a nuestro objeto espacial el nombre y la categoría de los 1399 alojamientos restantes.
hoteles2 <- hoteles %>% filter(hoteles$DIRECCION != "PARTIDA EL BUSCARRO, 1, TŔRBENA")

crds_arcgis_sf <- cbind(crds_arcgis_sf, hoteles2$Nombre, hoteles2$Categoría)
crds_arcgis_sf %>%
  leaflet::leaflet() %>%
  leaflet::addTiles() %>%
  leaflet::addCircles()

Como siguen saliendo puntos fuera de la Comunidad, lo que se hace ahora es identificar dichos puntos para eliminarlos. Para ello, se convierte la matriz de coordenadas en puntos sfg y, después, en sfc.

spain_ccaa <- mapSpain::esp_get_ccaa()

cv <- spain_ccaa %>% filter(ine.ccaa.name=="Comunitat Valenciana")
st_geometry(cv)
Geometry set for 1 feature 
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -1.528067 ymin: 37.84413 xmax: 0.689302 ymax: 40.78827
Geodetic CRS:  ETRS89
MULTIPOLYGON (((0.689104 39.90141, 0.686501 39....
cv <- st_transform(cv, 4326)

class(crds_arcgis2)
[1] "tbl_df"     "tbl"        "data.frame"
class(crds_arcgis_sf)
[1] "sf"         "data.frame"
puntos <- st_coordinates(crds_arcgis_sf)
class(puntos)
[1] "matrix" "array" 
puntos_sfg <- st_multipoint(puntos)
class(puntos_sfg)
[1] "XY"         "MULTIPOINT" "sfg"       
puntos_sfc <- st_sfc(puntos_sfg)
st_crs(puntos_sfc) <- 4326

puntos_cv <- st_intersection(puntos_sfc, cv)

ggplot() +
  geom_sf(data = cv) +
  geom_sf(data = puntos_cv, color = "red")

puntos_malos <- st_difference(puntos_sfc, cv)
puntos_malos
Geometry set for 1 feature 
Geometry type: MULTIPOINT
Dimension:     XY
Bounding box:  xmin: -111.0369 ymin: -33.0048 xmax: 20.03001 ymax: 50.32886
Geodetic CRS:  WGS 84
MULTIPOINT ((-6.288411 36.52553), (-3.555114 40...
puntos_individuales <- st_cast(puntos_sfc, "POINT")
class(puntos_individuales)
[1] "sfc_POINT" "sfc"      
length(puntos_individuales)
[1] 1399
puntos_buenos_sfc <- st_difference(puntos_individuales, puntos_malos)
puntos_buenos_sfc <- st_intersection(puntos_buenos_sfc, puntos_cv)

Ahora, se realiza la intersección de los puntos buenos con los polígonos de la Comunidad.

interseccion <- st_intersection(crds_arcgis_sf, puntos_buenos_sfc)
Warning: attribute variables are assumed to be spatially constant throughout
all geometries

Una vez hecha la intersección, hay que asegurarse de que no haya datos duplicados y, si los hay, de eliminarlos.

duplicados <- duplicated(interseccion)
sum(duplicados)
[1] 45
interseccion <- unique(interseccion)
duplicados2 <- duplicated(interseccion)
sum(duplicados2)
[1] 0

Finalmente, se representa en un mapa los hoteles según su categoría.

tipos_categoria <- interseccion$hoteles2.Categoría %>% unique()
tipos_categoria
[1] "PENSIÓN"     "3 ESTRELLAS" "2 ESTRELLAS" "1 ESTRELLA"  "4 ESTRELLAS"
[6] "5 ESTRELLAS" " "          
valores_vacios <- interseccion[interseccion$hoteles2.Categoría == " ", ]
nrow(valores_vacios)
[1] 99
hoteles2 %>%
  filter(hoteles2$Categoría == " ") %>%
  select(Nombre, Categoría, `Cod. Grupo`)# Miramos el tipo de alojamiento que es en "Grupo"
# A tibble: 185 × 3
   Nombre                 Categoría `Cod. Grupo`
   <chr>                  <chr>     <chr>       
 1 EL JARDIN DE LAS ERAS  " "       HS          
 2 A-CERO LIVING          " "       P           
 3 ALBEROLA ROMERO        " "       P           
 4 AMOR DE HELENE         " "       HS          
 5 ART HOUSE              " "       P           
 6 B&B CALDERON 41        " "       P           
 7 BARRIO BOUTIK          " "       HS          
 8 BERGEZ                 " "       P           
 9 CA DE MAR RAMBLA ROOMS " "       P           
10 CASA M&J               " "       P           
# ℹ 175 more rows
colores <- c('#fcbba1', '#c7e9b4','#7fcdbb', '#41b6c4','#1d91c0','#0c2c84', '#dd3497')
pal <- leaflet::colorFactor(colores, domain = tipos_categoria)
interseccion %>%
  leaflet() %>% 
  addProviderTiles("CartoDB.Positron") %>%
  addCircles(color = ~pal(hoteles2.Categoría)) %>%
  addLegend(data = interseccion,
            values = ~tipos_categoria,
            position = "bottomright",
            pal = pal,
            title = "Categoría",
            group = "Leyenda")

Output

Los datos generados en esta práctica han sido guardados en un objeto gpkg con el objeto de no repetir el procedimiento anterior cada vez. Se pueden obtener de aquí.



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