libs <- c("tidyverse", "sf", "mapSpain", "osmdata", "leaflet", "tidygeocoder", "xml2","dplyr","readxl","purrr","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)Mapa De Radares Fijos en España
Input
En el análisis de la localización de radares fijos en España se han empleado varios conjuntos de datos provenientes de fuentes oficiales y colaborativas.
Para el conjunto nacional, se han utilizado los datos de radares fijos proporcionados por la Dirección General de Tráfico (DGT). Esta fuente ofrece información detallada y actualizada sobre la ubicación de los radares en todo el territorio español (excepto Cataluña y el País Vasco), incluyendo datos como la carretera o vía en la que se encuentran, la provincia, el sentido,las coordenadas geográficas y el tipo de radar. Los datos se descargan en formato XML , y en este trabajo se han procesado mediante R para extraer la información necesaria.
Por otro lado, los radares de Cataluña se han obtenido de la Generalitat de Catalunya, a través del fichero de texto abierto publicado en su portal de seguridad vial . Este archivo ofrece información actualizada sobre la ubicación de radares en las distintas provincias catalanas y permite integrarla fácilmente con el resto de los datos nacionales.
Para complementar (País Vasco) y verificar la localización geográfica de los radares, también se han consultado los datos abiertos de OpenStreetMap (OSM) mediante la etiqueta highway y speed_camera. Esta base colaborativa y de código abierto permite acceder a información geográfica continuamente actualizada por usuarios y comunidades locales. La descarga y filtrado de estos datos se realiza con el paquete osmdata en R.
La combinación de estas fuentes permite analizar la localización de los radares fijos en España y su distribución por provincias y tipos de vía. Esta información es útil para estudios de seguridad vial, planificación de campañas de control de velocidad y análisis de accesibilidad en la red de carreteras.
Descripción
Comenzamos cargando las librerías necesarias para el tratamiento, análisis y visualización de datos en R. Estas incluyen paquetes para manipulación de datos (tidyverse), tratamiento espacial (sf), descarga de información geográfica (osmdata) y representación cartográfica (leaflet, ggplot2, entre otros).
A continuación, trabajamos con el conjunto de datos principal, que corresponde a los radares fijos en España, obtenidos a partir de un archivo XML proporcionado por la DGT.
Este tipo de formato no es habitual en comparación con CSV u otros formatos, por lo que ha sido necesario realizar un proceso previo de lectura y transformación para convertirlo en un dataframe manejable en R. Para ello, se ha utilizado el paquete (xml2), extrayendo los nodos relevantes y transformando su contenido en variables estructuradas, y el paquete (purrr) , exactamente la función (map_dfr), para combinar todos los datos en un único data frame llamado datos.
doc <- read_xml("../data/2526020042/radares.xml")
doc <- xml_ns_strip(doc) #Elimina los namespaces del XML para facilitar las búsquedas
radares <- xml_find_all(doc, ".//*[local-name()='predefinedLocation']") #Selecciona todos los nodos llamados 'predefinedLocation'
extraer_campos <- function(nodo) {
hijos <- xml_find_all(nodo, ".//*[not(*)]")
nombres <- make.unique(xml_name(hijos))
valores <- xml_text(hijos)
as.data.frame(as.list(setNames(valores, nombres)))
} #Función que extrae todos los datos de el XML y crea un df con el nombre de las columnas y los valores de cada campo
datos <- map_dfr(radares, extraer_campos) # Aplica la función a cada nodo de radares y combina todos los resultados en un único data frame El resultado es un dataframe que contiene información sobre la localización de los radares, incluyendo coordenadas geográficas (latitud y longitud) y otros atributos asociados. Sin embargo, como ocurre en muchos datos provenientes de XML, la estructura inicial no es completamente homogénea, por lo que ha sido necesario limpiar y organizar los campos para facilitar su posterior análisis. Además los datos cargados presentan ciertas limitaciones. Por ejemplo, no siempre incluye información completa sobre la vía o la velocidad permitida, o se observan datos duplicados en filas posteriores , lo que condiciona el tipo de análisis que se puede realizar.
Se trata de un data frame con 1474 obs y las variables mas importantes son:
latitude / longitude : Coordenadas del radar.
tpegLocationType : Tipo de radar ( nonLinkedPoint = radar fijo puntual, segment = radar de tramo)
roadNumber : Carretera donde esta el radar.
directionNamed : Dirección hacia una ciudad.
directionRelative : Sentido de circulación.
value : Nombre del radar.
value2 : Nombre de la provincia.
provinceINEIdentifier : Código de provincia INE.
singularity : Tipo de vía.
En este proyecto seleccionaremos las variables value2,longitude,latitude, ya que realizaremos un análisis de radares fijos a nivel provincial.
Tal y como ya conocemos , la DGT no proporciona datos sobre las comunidades autonómicas de Cataluña y País Vasco , por lo que es necesario consultar las paginas oficiales de estas comunidades y descargar los datos sobre radares fijos en estos territorios.
En el caso de Cataluña , la recopilación de estos datos ha sido a través de la pagina de datos abiertos del gobierno español. Este organismo nos proporciona los datos en un formato txt, el cual tiene 247 obs y contiene las siguientes variables:
Via : Nombre de la carretera donde está el radar.
PK : Punto kilométrico.
Velocitat : Límite de velocidad permitido en ese punto (km/h).
X/Y : Coordenada exacta del radar.
radares_cat <- read.table("../data/2526020042/radares_cataluña.txt", header = TRUE, fill = TRUE)Por último , para el caso de País Vasco , no he encontrado ninguna fuente oficial la cual ofrezca públicamente estos datos , en la pagina de Trafikoa (organismo encargado del tráfico en el País Vasco) simplemente ofrecen un mapa donde representan los radares fijos. Por lo que he decidido obtener los datos a traves de OSM:
bb <- getbb("Euskadi, Spain")
radares_pv <- bb %>%
osmdata::opq(timeout = 3600) %>%
osmdata::add_osm_feature(key = "highway", value = "speed_camera") %>%
osmdata_sf()
puntos <- radares_pv$osm_pointsLas variables más relevantes de este data frame son:
osm_id : Identificador único del radar.
name : Nombre o identificación del radar (a veces duplica información, pero puede ser útil).
highway : Tipo de elemento (en este caso, speed_camera).
maxspeed : Velocidad máxima permitida en ese punto (km/h).
direction : Dirección del radar (forward, backward, both).
description / description:es : Información adicional del radar (ubicación detallada o contexto).
ref : Referencia de la carretera.
geometry : Coordenadas del radar (LONG/LAT).
El objeto obtenido de OpenStreetMap contiene numerosas variables, sin embargo,al analizar el dataframe se observa que la mayoría de estas variables están prácticamente vacías, presentando valores NA en la mayor parte de los registros.
Sin embargo para este proyecto únicamente necesitaremos las coordenadas (a traves de geometry) y la velocidad máxima
En conjunto, este proceso ha permitido obtener datos de distintas procedencias, sin embargo los datos no están limpios,por lo que necesitaremos realizar un tratamiento diferente para cada tipo de data frame, con el fin de estudiar la distribución de radares fijos en España.
Tratamiento
Una vez obtenidos los distintos conjuntos de datos, se ha llevado a cabo un proceso de tratamiento con el objetivo de unificarlos y prepararlos para su análisis espacial. Dividiremos el tratamiento en cinco partes, una para cada conjunto de datos, seguido de un proceso para juntar cada uno de los resultados de las diferentes etapas, y una ultima para añadir la velocidad máxima permitida en la vía a la que pertenece el radar.
Tratamiento de los radares del conjunto nacional
En primer lugar, se trabaja con los datos procedentes de la DGT. A partir del dataframe generado desde el XML, se seleccionan únicamente las variables relevantes para el análisis tal y como hemos comentado anteriormente.
A continuación, se realiza una primera visualización muy simple para comprobar la correcta localización de los puntos:
leaflet(radares_españa) %>%
addTiles() %>%
addCircleMarkers()Durante esta fase se detecta la existencia de registros duplicados, probablemente asociados a distintos sentidos de circulación o tal vez debido a la función que he definido para importar el archivo XML. Para evitar una sobre-representación en el análisis, se eliminan los duplicados:
Tratamiento de los radares de Cataluña
En el caso de Cataluña, el principal problema esta en el formato de las coordenadas, que utilizan comas en lugar de puntos decimales y por lo tanto es de tipo character. Por ello, se realiza una conversión previa a formato numérico:
radares_cat$X <- as.numeric(gsub(",", ".", radares_cat$X))
radares_cat$Y <- as.numeric(gsub(",", ".", radares_cat$Y))
radares_cat <- radares_cat[!is.na(radares_cat$X) & !is.na(radares_cat$Y), ] # Nos quedamos únicamente con las filas donde las coordenadas no es NA.
radares_cat <- radares_cat[!duplicated(radares_cat[, c("X", "Y")]), ] # Eliminamos filas duplicadas basandonos en la combinación de X e Y.Posteriormente, los datos se convierten en objeto espacial y se reproyectan a WGS84:
coords_sf <- st_as_sf(radares_cat, coords = c("X", "Y"), crs = 25831)
coords_wgs84 <- st_transform(coords_sf, 4326)Para asegurar la correcta asignación territorial, se realiza un filtrado espacial por provincias mediante bounding boxes e intersecciones:
provincias <- esp_get_prov() # Descargamos los mapas de todas las provincias de España.
provincias_cat <- provincias %>%
filter(ine.prov.name %in% c("Barcelona", "Tarragona", "Girona", "Lleida")) %>%
st_transform(4326) # Seleccionamos solo las 4 provincias de Cataluña y cambiamos las coordendas al sistema estándar WGS84.Este proceso se repite para cada provincia:
#BARCELONA
barcelona <- provincias_cat %>%
filter(ine.prov.name == "Barcelona") # Obtenemos del df de provincias la provincia de Barcelona.
bb_bcn <- st_bbox(barcelona)
bb_bcn <- st_as_sfc(bb_bcn) #obtenemos el bbox de barcelona y lo convertimos en un objeto sf.
coords_bcn_bb <- coords_wgs84[ st_intersects(coords_wgs84, bb_bcn, sparse = FALSE),] # Selecciona los puntos que caen dentro del área aproximada de Barcelona mediante el bounding box
radares_bcn <- st_intersection(coords_bcn_bb, barcelona)
#TARRAGONA
tarragona <- provincias_cat %>%
filter(ine.prov.name == "Tarragona")
bb_trg <- st_bbox(tarragona)
bb_trg <- st_as_sfc(bb_trg)
coords_trg_bb <- coords_wgs84[st_intersects(coords_wgs84, bb_trg, sparse = FALSE),]
radares_trg <- st_intersection(coords_trg_bb, tarragona)
# GIRONA
girona <- provincias_cat %>%
filter(ine.prov.name == "Girona")
bb_grn <- st_bbox(girona)
bb_grn <- st_as_sfc(bb_grn)
coords_grn_bb <- coords_wgs84[st_intersects(coords_wgs84, bb_grn, sparse = FALSE),]
radares_grn <- st_intersection(coords_grn_bb, girona)
#LLEIDA
lleida <- provincias_cat %>%
filter(ine.prov.name == "Lleida")
bb_lld <- st_bbox(lleida)
bb_lld <- st_as_sfc(bb_lld)
coords_lld_bb <- coords_wgs84[st_intersects(coords_wgs84, bb_lld, sparse = FALSE),]
radares_lld <- st_intersection(coords_lld_bb, lleida)Y por último se combinan los resultados y se extraen las coordenadas,además de seleccionar las variables relevantes:
radares_cat_final <- bind_rows(
radares_bcn,
radares_trg,
radares_grn,
radares_lld
)
coords_cat <- st_coordinates(radares_cat_final)
radares_cat_final$longitud <- coords_cat[,1]
radares_cat_final$latitud <- coords_cat[,2]
radares_cat_final <-radares_cat_final[,c("ine.prov.name","geometry","longitud","latitud")]Representamos en un mapa los datos tras el tratamiento , para visualizar el resultado:
leaflet(radares_cat_final) %>%
addTiles() %>%
addCircleMarkers()Tratamiento de los radares del País Vasco
Para el País Vasco, en primer lugar, se convierten a objeto espacial los datos obtenido anteriormente y se extraen las coordenadas:
puntos_sf <- st_as_sf(puntos, wkt = "geometry", crs = 4326)
coords <- st_coordinates(puntos_sf)
df_final <- puntos_sf %>%
mutate(
longitud = coords[, 1],
latitud = coords[, 2]
)Posteriormente obtenemos las coordenadas de el data frame anterior y creamos puntos espaciales a partir de estas coordenadas :
coords <- st_as_sf(df_final, coords = c("longitud", "latitud"), crs = 25831)
coords<-st_transform(coords, 4326)Y tal y como hemos hecho con los datos de la comunidad de cataluña , procederemos a realizar un filtrado espacial por provincias para garantizar que los puntos están correctamente ubicados:
Este proceso se repite para cada provincia:
#Álava
alava <- provincias_pv %>%
filter(ine.prov.name == "Araba/Álava")
bb_alv <- st_bbox(alava)
bb_alv <- st_as_sfc(bb_alv)
coords_alv_bb <- coords[st_intersects(coords, bb_alv, sparse = FALSE),]
radares_alv <- st_intersection(coords_alv_bb, alava)
#Gipuzkoa
gipuzkoa <- provincias_pv %>%
filter(ine.prov.name == "Gipuzkoa")
bb_gzk <- st_bbox(gipuzkoa)
bb_gzk <- st_as_sfc(bb_gzk)
coords_gzk_bb <- coords[st_intersects(coords, bb_gzk, sparse = FALSE),]
radares_gzk <- st_intersection(coords_gzk_bb, gipuzkoa)
#Bizkaia
bizkaia <- provincias_pv %>%
filter(ine.prov.name == "Bizkaia")
bb_bzk <- st_bbox(bizkaia)
bb_bzk <- st_as_sfc(bb_bzk)
coords_bzk_bb <- coords[st_intersects(coords, bb_bzk, sparse = FALSE),]
radares_bzk <- st_intersection(coords_bzk_bb, bizkaia)Y por último se combinan los resultados y se extraen las coordenadas,además de seleccionar las variables relevantes:
radares_pv_final <- bind_rows(
radares_bzk,
radares_alv,
radares_gzk
)
coords_pv <- st_coordinates(radares_pv_final)
radares_pv_final$longitud <- coords_pv[,1]
radares_pv_final$latitud <- coords_pv[,2]
radares_pv_final <-radares_pv_final[,c("ine.prov.name","geometry","longitud","latitud")]Representamos en un mapa los datos tras el tratamiento , para visualizar el resultado:
leaflet(radares_pv_final) %>%
addTiles() %>%
addCircleMarkers()Integración de los datos
Una vez tratados los tres conjuntos de datos, se procede a su unificación. Para ello, se homogeneizan los nombres de las variables:
cat <- radares_cat_final %>%
rename(provincia = ine.prov.name) %>%
select(provincia,geometry,longitud,latitud)
pv <- radares_pv_final %>%
rename(provincia = ine.prov.name) %>%
select(provincia,geometry,longitud,latitud)
esp <- radares_españa %>%
rename(provincia = value.2,longitud=longitude,latitud=latitude) %>%
select(provincia,geometry,longitud,latitud)Tras esto visualizamos el tipo de dato que es cada variable de cada uno de los data frames, para estudiar la compatibilidad y normalizar los valores:
sapply(cat, class)$provincia
[1] "character"
$longitud
[1] "numeric"
$latitud
[1] "numeric"
$geometry
[1] "sfc_POINT" "sfc"
sapply(pv, class)$provincia
[1] "character"
$longitud
[1] "numeric"
$latitud
[1] "numeric"
$geometry
[1] "sfc_POINT" "sfc"
sapply(esp, class)$provincia
[1] "character"
$longitud
[1] "character"
$latitud
[1] "character"
$geometry
[1] "sfc_POINT" "sfc"
Tras esto observamos que las coordenadas del data frame de España son caracteres por lo que tenemos que hacerlas numéricas, además de cambiar la coma por el punto:
esp$longitud <- as.numeric(gsub(",", ".", esp$longitud))
esp$latitud <- as.numeric(gsub(",", ".", esp$latitud))También se normalizan los nombres de provincia para evitar problemas en las uniones:
radares_total <- bind_rows(cat, pv, esp)
radares_total$provincia <- toupper(radares_total$provincia)Se añade el código de provincia mediante un join con una tabla auxiliar:
tabla_prov <- provincias %>%
st_drop_geometry() %>%
select(ine.prov.name, cpro)
tabla_prov$ine.prov.name<-toupper(tabla_prov$ine.prov.name)
radares_total <- radares_total %>%
left_join(tabla_prov, by = c("provincia" = "ine.prov.name"))
radares_total <- radares_total %>%
select(provincia, cpro, everything())Visualización de los radares fijos en el territorio español:
leaflet(radares_total) %>%
addTiles() %>%
addCircleMarkers()Incorporación de la velocidad máxima
Para enriquecer el análisis, se incorporan datos de velocidad máxima a partir de OpenStreetMap. En primer lugar, obtenemos los datos de OSM:
bb_spain<- getbb("Spain")
radares_spain <- bb_spain %>%
osmdata::opq(timeout = 3600) %>%
osmdata::add_osm_feature(key = "highway", value = "speed_camera") %>%
osmdata_sf()
puntos_spain <- radares_spain$osm_pointspuntos_spain<- st_transform(puntos_spain, st_crs(radares_total)) # Importante : para poder hacer el joinRealizamos una unión espacial, usaremos la función (st_nearest_feature) para que al hacer el join se le atribuya a cada uno de los radares del data frame de toda españa, el radar más cercano de OSM :
radares_final <- st_join(radares_total,puntos_spain %>% select(geometry,maxspeed),join=st_nearest_feature)Tras investigar el data frame observamos que hay NAs , esto ocurre porque OSM es una plataforma colaborativa y posiblemente el creador de dicho objeto no supiera esta caracteristica , por lo que he optado rellenar estos NAs a traves de un if, siguiendo la siguiente lógica:
Si es una Autovia o Autopista se le asignará una velocidad máxima de 120 km/h.
Y si es una carretera nacional o autonómica se le asignará una velocidad máxima de 90 km/h.
Este proceso no es del todo correcto (ya que existen radares que tiene como máxima velocidad menos de las velocidades anteriores), sin embargo al tratarse de pocas observaciones faltantes y no tener un gran impacto frente a los 1130, he decidido seguir esta lógica.
radares_fallidos <- radares_final %>% filter(is.na(maxspeed)) # primero separamos los radares con velocidad maxima = NA del resto
carreteras_españa <-esp_get_roads() # obtenemos a traves de la función esp_get_roads() las carreteras de españa y su tipo de via, ya que en OSM no tenemos esta variable o no la tenemos como nos gustaria.
carreteras_españa<-st_transform(carreteras_españa,st_crs(radares_fallidos))
radares_fallidos <- st_join(
radares_fallidos,
carreteras_españa %>% select(t_ctra_desc),
join = st_nearest_feature
) # hacemos un join espacial para obtener el tipo de via al que pertenecen
radares_recuperados <- radares_fallidos %>%
mutate(maxspeed = case_when(
t_ctra_desc == "Autovía" ~ "120",
t_ctra_desc == "Autopista" ~ "120",
t_ctra_desc == "Carretera nacional" ~ "90",
t_ctra_desc == "Carretera autonómica" ~ "90",
TRUE ~ "90"
)) %>% select(-t_ctra_desc) # atribuimos a cada tipo de via una velocidad máxima y luego eliminamos la variable tipo de via.Finalmente, se combinan los datos originales con los imputados:
radares_final$maxspeed[is.na(radares_final$maxspeed)] <- radares_recuperados$maxspeed
radares_final <- radares_final %>%
select(provincia, cpro,maxspeed, everything()) # lo ordenamos a nuestro gustoOutput
Como resultado del proceso de tratamiento, se ha obtenido un único conjunto de datos espacial que integra la información de radares fijos procedente de distintas fuentes (DGT, Cataluña y OpenStreetMap).
El dataset final presenta la siguiente estructura:
Se trata de un objeto de tipo sf, en el que cada observación corresponde a un radar fijo situado en territorio español. Cada punto incluye información tanto de su localización como de atributos asociados.
En concreto, las variables del conjunto final son:
- provincia: Provincia en la que se encuentra el radar.
- cpro: Código de provincia (INE).
- maxspeed: Velocidad máxima permitida en ese punto (Contiene valores como : 120 ,90,60 … y además el valor signals para radares que estan controlados por señales dinámicas).
- latitud / longitud: Coordenadas geográficas.
- geometry: Representación espacial del radar.
Este conjunto de datos permite analizar la distribución de los radares fijos en España, tanto desde un punto de vista geográfico como en relación con la velocidad permitida.
A continuación, se muestra una visualización inicial del resultado:
pal <- colorFactor(
palette = "Set1",
domain = radares_final$provincia
)
leaflet(radares_final) %>%
addTiles() %>%
addCircleMarkers(
radius = 3,
color = ~pal(provincia),
fillColor = ~pal(provincia),
fillOpacity = 0.7,
popup = ~paste("Provincia:", provincia,
"<br>Velocidad máxima:", maxspeed, "km/h")
)sf::st_write(radares_final, "radares_final.gpkg")Análisis de radares fijos a nivel provincial
Además del análisis espacial, resulta de interés estudiar la distribución de los radares fijos a nivel provincial. Para ello, se ha construido una tabla resumen que recoge el número total de radares en cada provincia.
tabla_radares_prov <- radares_final %>%
st_drop_geometry() %>%
group_by(provincia, cpro) %>%
summarise(n_radares = n()) %>%
arrange(desc(n_radares))
tabla_radares_prov# A tibble: 50 × 3
# Groups: provincia [50]
provincia cpro n_radares
<chr> <chr> <int>
1 BARCELONA 08 102
2 GIPUZKOA 20 79
3 BIZKAIA 48 73
4 GIRONA 17 43
5 TARRAGONA 43 41
6 LLEIDA 25 40
7 MADRID 28 40
8 ARABA/ÁLAVA 01 37
9 ALICANTE/ALACANT 03 32
10 VALENCIA/VALÈNCIA 46 31
# ℹ 40 more rows
Es importante tener en cuenta que la distribución de radares por provincia está influida por la procedencia de los datos. En particular, las provincias de Cataluña y País Vasco no están incluidas en los datos de la DGT, pero han sido incorporadas a través de fuentes alternativas. Esto a provocado que dichas provincias presenten un mayor número de radares en el análisis, no necesariamente por una mayor presencia real, sino por diferencias en la cobertura y densidad de las fuentes utilizadas.
Conclusión
En este trabajo se ha conseguido reunir y unificar información sobre radares fijos en España a partir de distintas fuentes, que en un principio eran bastante diferentes entre sí. A lo largo del proceso, una de las partes más importantes ha sido limpiar y adaptar los datos, ya que venían en formatos distintos y con bastantes inconsistencias, como duplicados o valores incompletos.
Una vez tratado todo, se ha construido un único dataset que permite ver de forma clara cómo se distribuyen los radares por el territorio español. Gracias al uso de herramientas de análisis espacial, ha sido posible situarlos correctamente en el mapa y analizarlos por provincias, lo que facilita bastante la interpretación de los resultados.
Aun así, hay que tener en cuenta algunas limitaciones, sobre todo por la diferencia en las fuentes utilizadas y la falta de algunos datos, como en el caso del País Vasco o ciertas velocidades máximas que han tenido que estimarse. Esto ha influido ligeramente en los resultados.
En general, el trabajo muestra cómo, a partir de datos abiertos y con un buen tratamiento, se puede obtener información útil para entender mejor la distribución de los radares y su relación con la red de carreteras. Además, deja abierta la puerta a futuros análisis más completos sobre seguridad vial o comportamiento del tráfico.
Los ficheros generados con este procedimiento/técnica/metodología se pueden descargar de aquí.

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