Super-résolution 1 m a Cadalso de los Vidrios, Madrid avec Sentinel 2 (10m). Magique !

Passer d’une résolution de 10 mètres à 1 mètre change radicalement la perspective du suivi agricole : on ne regarde plus une parcelle dans sa globalité, on observe ce qui se passe à l’intérieur même des rangs de culture. Ce saut qualitatif est possible grâce à l’algorithme S2DR3, un modèle de Deep Learning qui ne se contente pas d’agrandir les pixels, mais reconstruit l’information manquante. En s’appuyant sur les corrélations entre les différentes bandes spectrales de Sentinel-2 et en s’entraînant sur des images de très haute résolution, l’IA parvient à synthétiser une image à 1 m/pixel d’une précision étonnante.

Figura 1 – Super-résolution Sentinel-2 à 1 m – Cadalso de los Vidrios, Madrid

Le processus est entièrement automatisé : le code interroge le catalogue Copernicus pour extraire la scène la plus récente sur Cadalso de los Vidrios, en écartant systématiquement les passages nuageux pour garantir une donnée pure. Pour un utilisateur sur geovisualization.net, cela signifie obtenir un fond de plan ultra-précis sans dépendre de vols de drones coûteux ou d’imagerie commerciale privée.

Une fois cette base de 1 mètre générée, l’exploitation en agriculture de précision devient chirurgicale. En calculant des indices comme le NDVI pour la vigueur végétative ou le NDWI pour le stress hydrique, on peut détecter des anomalies de croissance ou des besoins en irrigation sur des micro-zones spécifiques. L’utilisation d’indices plus fins comme le SAVI (qui ajuste l’influence du sol) ou l’EVI (plus sensible en zone de forte biomasse) permet de piloter chaque étape du cycle cultural.

Figura 2 – Extracción de índices de vegetación desde Sentinel -2
Figura 3 – S2DR3 Python Super-résolution Sentinel-2 à 1 m

On peut ainsi décider du moment exact de la plantation selon l’humidité résiduelle du sol, moduler l’apport d’engrais zone par zone, ou identifier les foyers de maladies avant qu’ils ne se propagent à toute l’exploitation. C’est le complément technique indispensable pour transformer la donnée satellite en une véritable aide à la décision sur le terrain.

# =========================================================
# S2DR3 - Super-résolution Sentinel-2 à 1 m
# Zone : Cadalso de los Vidrios, Madrid
# =========================================================
# --- 1. Monter Google Drive ---
from google.colab import drive
import os
from datetime import datetime
drive.mount('/content/drive')
# Nouveau dossier spécifique pour Cadalso
output_path = '/content/drive/MyDrive/Sentinel2_Cadalso1m'
!mkdir -p {output_path}
# Nettoyage et création du lien symbolique pour le moteur S2DR3
!rm -rf /content/output
!ln -s {output_path} /content/output
# --- 2. Installer le paquet S2DR3 ---
# Utilisation du wheel que vous avez validé
!pip -q install https://storage.googleapis.com/0x7ff601307fa5/s2dr3-20250905.1-cp312-cp312-linux_x86_64.whl
# --- 3. Importer le module principal ---
import s2dr3.inferutils
# --- 4. Définir la zone d'intérêt (Cadalso de los Vidrios) ---
# Coordonnées du centre du village.
# Le moteur S2DR3 traite généralement une zone autour de ce point (buffer inclus).
lonlat = (-4.44, 40.30)
# --- 5. Définir la date Sentinel-2 ---
# On utilise la date d'aujourd'hui pour forcer la recherche de la scène la plus récente
date_actuelle = datetime.now().strftime('%Y-%m-%d')
# --- 6. Lancer le traitement ---
# La fonction 'test' gère automatiquement le téléchargement de la scène
# la moins nuageuse proche de la date indiquée.
print(f"Lancement de la super-résolution pour Cadalso (Date cible : {date_actuelle})...")
s2dr3.inferutils.test(lonlat, date_actuelle)
print(f"✅ Traitement terminé. Les fichiers sont dans : {output_path}")

Le processus se déroule entièrement dans l’environnement Google Colab à l’aide d’un script Python.

J’espère que cela vous plaira,

Cordialement,
Alberto C.
Analyste géospatial

#AgroTech #S2DR3 #Télédétection #SuperResolution #AI #AgTech #Geovisualization #Sentinel2 #InteligenciaArtificia #Sentinel2 #Cadalso

https://www.sigterritoires.fr/index.php/es/uso-de-s2dr3-en-google-colab-para-el-estudio-de-los-corales-en-mauricio/
https://colab.research.google.com/
https://medium.com/@ya_71389/sentinel-2-deep-resolution-3-0-c71a601a2253
https://www.youtube.com/watch?v=zNX4vp1hGWI

Setting up Mapterhorn terrain in RStudio

¿Alguna vez has querido visualizar el relieve de un territorio en 3D directamente desde RStudio, sin depender de software GIS externo? Mapterhorn es un proyecto open source que distribuye modelos digitales de elevación (MDT) de alta resolución — hasta 2 metros en España — empaquetados en formato PMTiles, un estándar moderno que permite servir datos geoespaciales sin necesidad de un servidor propio.

En este post veremos cómo configurar Mapterhorn en R usando el paquete mapgl en Rstudio, que nos permite crear mapas interactivos con terreno 3D en pocas líneas de código. El resultado: visualizaciones como la que ves abajo, con sombreado de relieve (hillshade) generado directamente desde los datos de elevación del IGN.

No necesitas experiencia previa en cartografía — si sabes instalar paquetes en R, puedes seguir este tutorial.

Figura 1 – Empezamos a visualizar Mapterhorn

El siguiente paso es combinar el terreno de Mapterhorn con un estilo cartográfico que aporte contexto geográfico. En este ejemplo vemos la Sierra Norte de Madrid — con el embalse de Bustarviejo al fondo y municipios como Torrelaguna en primer plano — renderizada con una inclinación de cámara (pitch) de 60° y exageración de relieve moderada.

install.packages(c("mapgl", "terra", "elevatr", "sf", "tidyterra", "ggplot2"))
---------------------------------
library(mapgl)
library(terra)
library(elevatr)
library(sf)
maplibre(
style = carto_style("voyager"),
center = c(-3.703, 40.416),
zoom = 14,
pitch = 40
) |>
add_raster_dem_source(
id = "mapterhorn_pro",
url = "pmtiles://https://download.mapterhorn.com/planet.pmtiles",
tileSize = 512,
encoding = "terrarium"
) |>
set_terrain(source = "mapterhorn_pro", exaggeration = 0.1) |>
add_layer(
id = "sombras",
type = "hillshade",
source = "mapterhorn_pro",
paint = list(
"hillshade-exaggeration" = 0.5,
"hillshade-shadow-color" = "#333333",
"hillshade-illumination-direction" = 315
)
)

El resultado es un mapa donde el relieve deja de ser un dato abstracto y se convierte en algo inmediatamente legible: puedes ver de un vistazo la diferencia de altitud entre el fondo del valle del Jarama y las cumbres de la sierra, algo que un mapa plano convencional no transmite con la misma fuerza.

Figura 2 – Aportando contexto geográfico con un basemap (Google)
library(mapgl)
maplibre(
style = carto_style("voyager"),
center = c(-3.60, 40.75),
zoom = 11,
pitch = 60
) |>
add_raster_dem_source(
id = "mapterhorn",
url = "pmtiles://https://download.mapterhorn.com/planet.pmtiles",
tileSize = 512,
encoding = "terrarium"
) |>
set_terrain(source = "mapterhorn", exaggeration = 1.5)

Análisis de visibilidad: ¿se ven Cibeles y la Puerta de Alcalá entre sí?

Más allá de la visualización estética, los datos de elevación de Mapterhorn permiten hacer análisis geoespaciales reales. Un ejemplo clásico es el análisis de línea de visión (Line of Sight, LOS): dado un observador en un punto A, ¿puede ver el punto B sin que el terreno lo obstaculice?

En este caso trazamos el perfil de elevación entre la Fuente de Cibeles y la Puerta de Alcalá — apenas 500 metros de distancia en el centro de Madrid. La línea marrón representa el terreno real; la línea roja discontinua es la línea de visión teórica desde los ojos del observador (a 1,70 m de altura, ten en cuenta que la escala del eje Y está magnificada para mejor comprensión).

El resultado es claro: el terreno queda por encima de la línea de visión en todo el trayecto, lo que indica que el suelo sube progresivamente desde Cibeles hacia Alcalá. Curiosamente, aunque ambos monumentos son perfectamente visibles entre sí en la realidad (la calle es recta y despejada), el propio desnivel del terreno hace que la línea de visión rasante quede por debajo del suelo si no se tienen en cuenta los edificios ni la altura real de los monumentos.

Ahora comparemos nuestro Mapterhorn 2m con el Copernicus 30m, a pesar de que esta comparación es inconsistente más allá de una interpretación puramente visual.

Figura 4 – Visualizamos el DSM-ish 30m de Copernicus y el DTM-ish 2m del IGN (alerta de inconsistencia QC)
library(elevatr)
library(terra)
library(ggplot2)
# 1. Zona y descarga de ambos MDTs
puntos <- data.frame(x = c(-4.0, -3.8), y = c(40.7, 40.9))
dem_low <- rast(get_elev_raster(puntos, z = 8, prj = "EPSG:4326", clip = "bbox"))
dem_high <- rast(get_elev_raster(puntos, z = 14, prj = "EPSG:4326", clip = "bbox"))
# 2. Perfil de elevación (path profile) a lo largo de una transecta
pasos <- 200
lon_seq <- seq(-4.0, -3.8, length.out = pasos)
lat_seq <- seq(40.7, 40.9, length.out = pasos)
coords <- cbind(lon_seq, lat_seq)
z_low <- extract(dem_low, coords)[, 1]
z_high <- extract(dem_high, coords)[, 1]
dist_m <- seq(0, 25000, length.out = pasos) # ~25km de transecta
# 3. Path profile comparativo
perfil_df <- data.frame(
distancia = rep(dist_m, 2),
elevacion = c(z_low, z_high),
fuente = rep(c("Copernicus GLO-30 (~30m)", "Mapterhorn IGN (~2m)"), each = pasos)
)
ggplot(perfil_df, aes(x = distancia / 1000, y = elevacion, color = fuente)) +
geom_line(linewidth = 0.8, alpha = 0.85) +
scale_color_manual(values = c("Copernicus GLO-30 (~30m)" = "#e74c3c",
"Mapterhorn IGN (~2m)" = "#2c3e50")) +
labs(
title = "Perfil de elevación: Copernicus vs Mapterhorn",
subtitle = "Transecta SW-NE sobre la Sierra de Guadarrama",
x = "Distancia (km)",
y = "Altitud (m)",
color = NULL
) +
theme_minimal(base_size = 13) +
theme(legend.position = "top")
# 4. RMSE — remuestrea el MDT de alta res a la resolución del bajo
dem_high_resampled <- resample(dem_high, dem_low, method = "bilinear")
z_low_full <- values(dem_low)
z_high_full <- values(dem_high_resampled)
# Elimina NAs
idx <- complete.cases(z_low_full, z_high_full)
rmse <- sqrt(mean((z_high_full[idx] - z_low_full[idx])^2))
cat(sprintf("RMSE entre Copernicus GLO-30 y Mapterhorn IGN: %.2f metros\n", rmse))
# 5. Diferencia espacial (mapa de error)
diff_raster <- dem_high_resampled - dem_low
plot(diff_raster,
main = "Diferencia de elevación: Mapterhorn - Copernicus (m)",
col = hcl.colors(100, "RdBu", rev = TRUE))
Figura 5 – RMSE entre Copernicus GLO-30 y Mapterhorn IGN: 9.34 metros (alerta de inconsistencia QC)

Pero como avanzaba, Copernicus GLO-30 y Mapterhorn IGN no son dos mediciones del mismo fenómeno a distinta resolución — son dos productos con orígenes, metodologías y fechas de captura completamente diferentes. Copernicus GLO-30 se deriva del radar de apertura sintética del satélite TanDEM-X, que mide la superficie de la Tierra desde el espacio con una resolución nominal de 30 metros. Al ser una medición radar, captura la parte superior de la vegetación y los edificios, no el suelo desnudo — lo que en cartografía se llama un Modelo Digital de Superficie (MDS), no un Modelo Digital del Terreno (MDT) estricto.

Mapterhorn distribuye los datos del IGN España, obtenidos mediante vuelos LiDAR que permiten filtrar la vegetación y los edificios para obtener el suelo real. El resultado es un MDT de 2 metros de resolución que representa la topografía con una precisión altimétrica muy superior.

Por tanto, el RMSE que calculamos no mide únicamente el error de Copernicus — mide la diferencia acumulada entre dos modelos distintos, que incluye diferencias reales de resolución, diferencias metodológicas y el efecto de la vegetación sobre el radar. En zonas boscosas como la sierra de Guadarrama, estas diferencias pueden ser especialmente pronunciadas, llegando a varios metros en las áreas más densamente arboladas.

Figura 6 – Mapterhorn en Rstudio

Lo que hemos visto en este post es solo el punto de entrada. Una vez que tienes los datos de elevación conectados, las posibilidades se multiplican rápidamente.

En el ámbito del análisis de terreno puedes calcular pendientes y orientaciones para estudios de riesgo de erosión, planificación de infraestructuras o modelado de radiación solar. A partir del mismo MDT puedes delimitar cuencas hidrográficas automáticamente, trazar la red de drenaje teórica o calcular el área de captación de cualquier punto del territorio. Para proyectos de energía renovable, cruzar las orientaciones de ladera con datos de irradiación permite identificar zonas óptimas para instalaciones solares con pocas líneas de código.

En visualización avanzada puedes combinar el terreno 3D con cualquier capa vectorial propia — rutas de senderismo, límites administrativos, localizaciones de campo — y exportar el resultado como HTML interactivo para publicar directamente en la web. El paquete mapgl permite animar la cámara con fly_to() para crear recorridos virtuales sobre el terreno, algo especialmente útil para presentaciones o divulgación. También puedes generar mapas de sombreado artístico cambiando el ángulo de iluminación para conseguir efectos visuales dignos de cartografía profesional.

Para aplicaciones más especializadas, los datos de Mapterhorn son la base ideal para análisis de intervisibilidad a escala regional — por ejemplo, calcular desde cuántos puntos del territorio es visible una antena, un parque eólico o una torre de vigilancia de incendios. En urbanismo y ordenación del territorio permiten modelar el impacto visual de nuevas construcciones o calcular perfiles de ruido considerando la topografía real.

Todo esto sin salir de RStudio, sin licencias de software GIS y con datos de la máxima resolución disponible para España.

Mapterhorn ha eliminado uno de los principales obstáculos que tenían los usuarios de R para trabajar con datos de elevación de alta resolución: la complejidad de acceder, descargar y procesar MDTs de fuentes institucionales. Con una sola URL y tres líneas de código tienes acceso a los mejores datos topográficos disponibles para España y buena parte de Europa, listos para visualizar o analizar.

Espero que te haya interesado,

Alberto C.
GIS Analyst

#RStats #Mapterhorn #GIS #Cartografía #MapGL #TerrainAnalysis #OpenData #Geovisualización #RStudio #LiDAR #IGN #SpatialData #Mapping #DataViz #Topografía #RemoteSensing #OpenSource #GeospatialR #MDT #Hillshade

https://mapterhorn.com/
https://protomaps.com/blog/mapterhorn-terrain/
https://github.com/mapterhorn
https://posit.co/download/rstudio-desktop/
https://centrodedescargas.cnig.es/

Aventuras y desventuras de un geógrafo en “desarrollo”

La cartografía siempre ha sido un oficio de precisión, paciencia y criterio espacial. Durante años, el flujo de trabajo de cualquier geógrafo pasaba inevitablemente por entornos de escritorio como ArcGIS Pro o QGIS: cargar capas, ajustar simbología, exportar mapas. Herramientas sólidas, probadas, indispensables. Pero algo está cambiando.

Cada vez más, el análisis espacial ocurre en la nube, en navegadores, en entornos de código. En anteriores post habéis visto algunos test/ideas/aplicaciones que he desarrollado con Javascript Google Earth Engine, que procesa imágenes satelitales a escala planetaria sin mover un solo archivo. Deck.gl y Maplibre renderizan millones de puntos en 3D directamente en el navegador. React convierte un mapa en una aplicación interactiva con pocas líneas de código.

Figura 1 – GEE, segmentación de edificios con imagenes centimétricas en Tallin (Estonia)

Este post es el diario de ese viaje: el de un geógrafo que ha empezado a escribir código no para dejar de ser geógrafo, sino para serlo mejor. Desde la segmentación de ortofotos con algoritmos de machine learning en GEE, hasta la visualización de datos de población en 3D con Maptiler y Deck.gl, pasando por entornos propietarios y de código abierto.

Sin renunciar al criterio espacial. Solo añadiendo nuevas herramientas.

Figura 2 – Entorno de codesandbox.io, eligiendo React

El primer paso fue elegir dónde trabajar. CodeSandbox permite lanzar un entorno React completo directamente en el navegador, sin instalar nada. Basta elegir la plantilla, pegar el código y ver el resultado en tiempo real. Para un geógrafo acostumbrado a instalar plugins y configurar proyectos, la inmediatez es llamativa.

React actúa aquí como el pegamento: gestiona el estado de la aplicación (qué año se muestra, qué capa está activa) y renderiza la interfaz. No es imprescindible saber todo sobre React para empezar — con los hooks básicos useState y useEffect ya se puede construir bastante.

Figura 3 – Conectando con ficheros subidos a Maptiler

Con el entorno listo, el siguiente reto fue conectar datos reales. Los edificios del centro de Madrid — subidos previamente a Maptiler Cloud como GeoJSON — se visualizan en 3D extruidos usando Maplibre GL JS, cargado directamente desde CDN sin necesidad de instalar nada.

El atributo ESTAGL de cada polígono define la altura de la extrusión. El resultado: una vista aérea inclinada de Madrid donde cada edificio tiene volumen real. El basemap de Maptiler topo-v2 aporta el contexto geográfico debajo.

Figura 4 – Simbolización y generación de código para hacer un change detection de población en Madrid 2015-2020-2025

El siguiente experimento usó datos de WorldPop — una fuente de población global a resolución de 100×100 metros — para tres años: 2015, 2020 y 2025. Los datos, originalmente en formato raster, se convirtieron a polígonos y se subieron a Maptiler Cloud como tres datasets independientes.

El reto aquí era visual: las diferencias de población entre años son pequeñas y una escala lineal las hace invisibles. La solución fue aplicar una escala logarítmica (log1p) que comprime los valores extremos y amplifica las diferencias sutiles. El resultado es el mapa amarillo-verdoso de la imagen: la mancha urbana de Madrid claramente definida, con los núcleos de mayor densidad destacados.

Pero la visualización más interesante llegó al pasar al 3D. En lugar de mostrar los valores absolutos, se calculó el delta de cada píxel respecto a 2015 — cuánto ha cambiado la población en cada celda de 100m.

Ese delta se convierte en altura: más cambio, más altura. El color va de verde (poco cambio) a rojo (mucho cambio). El resultado es una especie de paisaje urbano donde las zonas de mayor transformación demográfica emergen literalmente del mapa, extruidas sobre el basemap de Maptiler.

La animación entre 2020 y 2025 — con transición suave usando requestAnimationFrame y una función easeInOut — permite ver cómo evoluciona ese paisaje en el tiempo.

Figura 5 – Probando con diferentes modos de simbolización, interfaces, secuencias temporales, etc

Una de las lecciones más claras de este ejercicio es que la visualización es iterativa. La primera versión del mapa 3D mostraba todos los cambios en verde — funcionaba, pero no comunicaba. ¿Verde intenso significa mucho cambio o poco?

La segunda versión introduce una paleta divergente verde→rojo donde el color codifica la magnitud del delta: verde para cambios pequeños, amarillo para cambios medios, rojo para las zonas de mayor transformación. La altura sigue siendo proporcional al delta, pero ahora el color refuerza ese mensaje.

El resultado — visible en esta imagen de abajo— es un mapa que se lee de forma intuitiva: las zonas rojas y altas son las que más han cambiado demográficamente respecto a 2015. Un lenguaje visual que cualquier persona puede entender sin necesidad de una leyenda detallada.

Figura 6 – El archivo “respira” pasando por los años 2015-2020-2025!!

Pues aquí tenemos el resultado final, un mapa de cambios VIVO (que cambia los slides a lo largo de la linea temporal elegida 2015-2020-2025). Pareciera que se infla 🙂

Espero que os guste, yo por mi parte os aclaro que mi intención no es otra que renovar mi portfolio, mostrar cositas nuevas que sé hacer desde hace poco y enseñar las potencialidades de estas metodologías y procedimientos de alguna manera nuevos para alguien como yo, acostumbrado a unos entornos con mucho fondo pero mucho menos dinámicos y mucho más encorsetados. si ves algo que te interesa, no tienes más que contármelo y hablamos. Por cierto, estoy buscando trabajo!! (por si coincide).

Saludos.
Alberto C.

https://cloud.maptiler.com/
https://codesandbox.io/templates

ESTIMATED GHSL vs INE 2025

He desarrollado este COMPARADOR DE POBLACIÓN GHSL vs PADRÓN INE 2025 en JavaScript/Google Earth Engine que cruza rapidísimamente estimaciones satelitales de población con los datos oficiales del censo español municipio a municipio.

La herramienta permite seleccionar cualquier provincia y municipio de España, visualizar la distribución espacial de población estimada por el GHSL con el último dato oficial del INE 2025, detectando municipios con alta presión turística, despoblación real o población no registrada.

Una aplicación directa para planificación de infraestructuras, gestión de emergencias o análisis de cohesión territorial donde el padrón no refleja la ocupación real del territorio.

Alberto C.

Flood assessment using RADAR (Sentinel1), SRTM v3+ and GHSL pop estimation in Spain

I have developed this NRT FLOOD RISK ASSESSMENT monitor in GEE that merges the power of RADAR and GHSL to detect impact over POPULATIONS under any weather conditions.

You can use geospatial intelligence to quickly transform satellite data into critical decisions during climate emergencies. GIS technology driving global resilience!

Hope you like it. If you need further explanation please dont hesitate to contact me.

Alberto C.
GIS Analyst

From LIDAR USGS to DSM in a few lines of code. The magic of R

Figura 1 – Exporting LIDAR to DSM (properly) (Global Mapper 26.2)

The USGS LiDAR Explorer, hosted via gishub.org, serves as a high-performance web gateway for interacting with the USGS 3D Elevation Program (3DEP) datasets. Developed by Qiusheng Wu, the platform leverages Cloud Optimized Point Clouds (COPC) and Entwine Point Tile (EPT) formats to enable seamless 3D visualization of massive LiDAR clouds directly in the browser without the need for local downloads.

Figura 2 – https://usgs-lidar.gishub.org/ search your COPC

By integrating data from Microsoft Planetary Computer and AWS Open Data, it provides an essential infrastructure for researchers and developers to search, visualize, and eventually pull high-resolution elevation data into R for sophisticated urban and environmental modeling.

Now we can start playing with LIDAR! No need to download anything, just save the names of your COPCs and you can put R to work right away.

First thing, go to this GITHUB repository https://github.com/opengeos/maplibre-gl-usgs-lidar, download code for the project (code>download ZIP), get connected with RStudio, save new project and open a script window… It’s all set up!

Figura 3 – Set up your R project

Cloud Optimized Point Clouds (COPC) represent a modernization of the standard LAZ format, specifically designed to handle massive LiDAR datasets within cloud-native environments. By reorganizing the data into a clustered octree structure, COPC allows software to stream only the specific portions or levels of detail needed for a task without downloading the entire file. This efficiency makes it the backbone of platforms like the USGS LiDAR Explorer, enabling high-speed 3D visualization and analysis directly from remote servers while maintaining full backward compatibility with traditional LAS/LAZ readers.

Figura 4 – visualize LIDAR data, make it thinner, filter by height, switch to a different classification method…
library(lidR)
library(terra) # Necesaria para manejar el DSM como una imagen/superficie
# Definimos la ruta exacta que proporcionaste
ruta_boston <- "C:/R/maplibre_lidar/maplibre-gl-usgs-lidar-main/lidar_sample/USGS_LPC_MA_Sndy_CMPG_2013_19TCG315920_LAS_2015.copc.laz"
# Leemos el archivo (usamos un filtro para no saturar la RAM si es muy denso)
las <- readLAS(ruta_boston, select = "xyzc")
# Verificamos que se ha cargado correctamente
print(las)
-------------------
# Generamos el DSM con una resolución de 1 metro
# El algoritmo p2r(0.2) rellena huecos pequeños para que parezca sólido
dsm <- rasterize_canopy(las, res = 1, algorithm = p2r(0.2))
# Visualización rápida en 2D (Mapa de calor)
plot(dsm, col = terrain.colors(50), main = "Modelo Digital de Superficie (DSM) - Boston")
-------------
# Convertimos el raster a un formato que RGL pueda renderizar como superficie
plot_dtm3d(dsm, bg = "black")
---------------------
# Crear mapa de sombras
pendiente <- terrain(dsm, v = "slope", unit = "radians")
aspecto <- terrain(dsm, v = "aspect", unit = "radians")
sombras <- shade(pendiente, aspecto, angle = 45, direction = 315)
# Visualizar el DSM con sombras
plot(sombras, col = grey(0:100/100), legend = FALSE)
plot(dsm, col = terrain.colors(50, alpha = 0.5), add = TRUE)
-------------
library(lidR)
library(terra)
# 1. Cargar el archivo completo (Ruta que confirmaste)
ruta_boston <- "C:/R/maplibre_lidar/maplibre-gl-usgs-lidar-main/lidar_sample/USGS_LPC_MA_Sndy_CMPG_2013_19TCG315920_LAS_2015.copc.laz"
las <- readLAS(ruta_boston, select = "xyzc")
# 2. Elegimos una zona con edificios (puedes ajustar estos números tras ver el summary)
# Estos valores suelen estar en el rango del summary(las)
subset_las <- clip_rectangle(las, 331500, 4691500, 331700, 4691700)
# 3. Generar el DSM (Superficie sólida) de alta resolución
# Usamos res = 0.5 para que el zoom se vea nítido
dsm_zoom <- rasterize_canopy(subset_las, res = 0.5, algorithm = p2r(0.3))
--------------------
library(lidR)
library(terra)
# 1. Cargar el archivo de Boston
ruta_boston <- "C:/R/maplibre_lidar/maplibre-gl-usgs-lidar-main/lidar_sample/USGS_LPC_MA_Sndy_CMPG_2013_19TCG315920_LAS_2015.copc.laz"
las <- readLAS(ruta_boston, select = "xyzc")
# 2. Selección automática de la zona central (Cubo de 160m x 160m)
x_mid <- mean(c(las@header@PHB$`Max X`, las@header@PHB$`Min X`))
y_mid <- mean(c(las@header@PHB$`Max Y`, las@header@PHB$`Min Y`))
subset_las <- clip_rectangle(las, x_mid - 80, y_mid - 80, x_mid + 80, y_mid + 80)
# 3. Crear el DSM (Superficie continua)
# El parámetro p2r(0.3) rellena los espacios vacíos entre rebotes del láser
dsm_final <- rasterize_canopy(subset_las, res = 0.5, algorithm = p2r(0.3))
# 4. Visualización 3D Sólida
plot_dtm3d(dsm_final, bg = "black", col = "lightblue")
--------------
install.packages("viridis")
--------------------
library(lidR)
library(terra)
library(viridis) # Ahora que ya está instalado
# 1. Aseguramos que la superficie esté bien calculada
# Usamos un radio de 0.4 para cerrar huecos sin crear "picos"
dsm_raw <- rasterize_canopy(subset_las, res = 0.5, algorithm = p2r(0.4))
# 2. Suavizado (El secreto de la visualización NEAT)
# Aplicamos un filtro de media para que los techos se vean planos y lisos
dsm_smooth <- focal(dsm_raw, w = matrix(1, 3, 3), fun = mean, na.rm = TRUE)
# 3. Renderizado 3D Estilo Maqueta
# 'border = NA' elimina las líneas negras que ensucian la imagen
# 'col = viridis(256)' aplica el degradado profesional
plot_dtm3d(dsm_smooth,
bg = "black",
col = viridis(256),
border = NA,
lighting = TRUE)
----------------
plot_dtm3d(dsm_smooth,
bg = "white",
col = "grey90",
border = NA,
lighting = TRUE)
-----------------
install.packages("magick")
library(magick)
-----------------
library(rgl)
# 1. Asegúrate de que la ventana de RGL esté abierta con tu modelo
# 2. Definimos la función de giro (alrededor del eje Z)
girar_camara <- spin3d(axis = c(0, 0, 1), rpm = 3) # 3 revoluciones por minuto
# 3. Grabar la animación
# Esto creará un archivo llamado "boston_360_02.gif" en tu carpeta de trabajo
movie3d(
f = girar_camara,
duration = 20, # Duración en segundos
dir = getwd(), # Se guarda donde tengas el proyecto
movie = "boston_360_02",
type = "gif",
clean = TRUE # Borra los fotogramas sueltos al terminar
)
----------------------
play3d(spin3d(axis = c(0, 0, 1), rpm = 10), duration = 10)
---------------------
movie3d(
f = spin3d(axis = c(0, 0, 1), rpm = 3),
duration = 10,
dir = getwd(),
movie = "boston_360_03",
type = "gif",
clean = TRUE
)
-------------------
# Guarda la vista actual como una imagen PNG nítida
snapshot3d("Boston_Maqueta_Pro.png", fmt = "png", width = 1200, height = 900)
Figura 5 – All set up!

Integrating LiDAR with Open Data in R transforms your POINTS into decisions by fusing i.e physical geometry (OSMs) with precise height (LIDARs), roof pitch, and volume of every structure in a neighborhood, which is essential for determining solar energy potential or tax assessments without manual surveys.

Beyond architecture, this workflow excels in environmental management when you link city-managed tree inventories with point cloud segments. By isolating individual tree crowns in the LiDAR data and joining them to open databases via their geographic coordinates, R can report on the specific health, biomass, and carbon capture of different species across the city. You can also derive micro-topography from a LiDAR ground model and intersect it with open hydrography or sewage network data to simulate precise flood pathways during extreme weather, predicting exactly which street corners will flood based on the actual height of the curbs and pavement.

Figura 6 – Split it into tiles, play with it!

It’s a lot what you can do integrating different technologies and different kind of data…

Figura 7 – My recent official certification in Global Mapper / LIDAR module

Do you want me to surf your LIDAR dataset and extract, classify, integrate, merge or challenge it with other data? Let’s talk. I’m free and easy:-) and the most important, looking for interesting projects, well, a job:-)

Alberto C.
Geodata analyst

Sources:
https://github.com/opengeos/maplibre-gl-usgs-lidar
https://usgs-lidar.gishub.org/

GEMINI AI GENERATIVA con NANO BANANA: ¡La gran tormenta en Benidorm!

La IA generativa aplicada a mapas está transformando profundamente la forma en que entendemos, producimos y comunicamos la información geográfica. A diferencia de los enfoques clásicos, que se basan en reglas fijas o en clasificación supervisada, la IA generativa es capaz de crear nuevas representaciones espaciales a partir de patrones aprendidos en grandes volúmenes de datos geoespaciales.

Figura 1 – IA generativa sobre NY

En el ámbito cartográfico, estos modelos permiten generar mapas sintéticos, completar zonas con datos faltantes, aumentar la resolución espacial (super-resolución) o simular escenarios futuros, como la expansión urbana o el impacto del cambio climático. Modelos como GANs, diffusion models o transformers espaciales ya se están utilizando para generar imágenes satelitales realistas, mapas de uso del suelo plausibles o distribuciones probabilísticas de fenómenos territoriales.

Uno de los grandes valores de la IA generativa en mapas es su capacidad para integrar múltiples fuentes (satélite, censos, movilidad, clima) y producir salidas coherentes desde el punto de vista espacial. Esto resulta especialmente útil en regiones con escasez de datos, donde los mapas generados pueden servir como apoyo a la planificación o a la toma de decisiones.

Sin embargo, su uso plantea retos importantes: interpretabilidad, validación y sesgo espacial. Un mapa generado no es necesariamente un mapa verdadero, sino una hipótesis basada en datos previos. Por ello, la IA generativa debe entenderse como una herramienta de apoyo al análisis territorial, no como un sustituto del conocimiento geográfico ni del criterio experto.

En este contexto, el papel del geógrafo y del analista geoespacial es clave: evaluar, contextualizar y validar los mapas generados por IA para que su uso sea responsable y científicamente sólido.

Segun la página mappinggis, la IA generativa está cambiando las reglas del juego. Antes, las visualizaciones en la planificación urbana y paisajística se asociaban estrechamente con licencias costosas y largos tiempos de procesamiento. Hoy en día, una fotografía aérea y un buen prompt son suficientes para obtener un fascinante «mapa» generado automáticamente por IA.

Este enfoque muestra un potencial significativo para la visualización cartográfica automatizada. Si bien aún no está listo para producción, los resultados demuestran que la IA puede comprender y traducir imágenes aéreas a estilos de mapas reconocibles con una precisión razonable… y sin duda ofrece un anticipo de lo que nos espera en los próximos meses (¡no años!).

David Oesch

Ejemplo 1 en Benidorm, España.

Figura 2 – Captura de Google Earth en Benidorm, España
Figura 3 – Misma imagen pasada por el filtro de Gemini

Ejemplo 2 en Benidorm, España.

Figura 4 – Captura de Google Earth en Benidorm, España
Figura 5 – Misma imagen pasada por el filtro de Gemini

Ejemplo 3 en Benidorm, España.

Figura 6 – Captura de Google Earth en Benidorm, España
Figura 7 – Misma imagen pasada por el filtro de Gemini
Figura 8 – Ahora añadimos contexto: lluvia, emergencias, embotellamiento, etc

Este es el complejo promt que he usado, el propietario es David Oesch (citado en fuentes más abajo):

[COMPLETE FRESH START]
You are generating a completely new cartographic map. This is NOT an edit or modification of a previous image.
Disregard all conversation history and prior image states.
OBJECTIVE: Transform the provided satellite image into a precise Swiss national topographic map
TRANSFORMATION REQUIREMENT: Complete replacement of all satellite imagery with vector-style cartographic symbolism. Maintain scale, proportions, and preserve the layout and spatial relationships from the source satellite image.
OUTPUT GUARANTEE: The result must contain ZERO visible satellite photograph elements
PHASE 1 - IMAGE ANALYSIS:
Identify all geographic features in the satellite image (buildings, roads, water, vegetation, terrain)
PHASE 2 - CARTOGRAPHIC TRANSFORMATION:
Replace every satellite image element with exact Swiss topographic symbology
PHASE 3 - VERIFICATION:
Confirm the output is a pure cartographic map with no satellite imagery, no photographic texture, no aerial photography
RENDERING SPECIFICATIONS:
- Orthographic projection (flat, top-down view)
- Exact RGB color values as specified
- Maintain scale
- Professional, clean cartographic presentation suitable for official swisstopo standards
## Background
- Base color: rgb(253, 253, 254) (off-white)
- Apply subtle gray hillshading for terrain relief
## Buildings and Structures
- Standard buildings: Fill with rgb(170, 172, 174) (medium gray)
- Building outlines: Dark gray rgb(154, 156, 158) with line width 0.5-2pt
- Roofs and cooling towers: Lighter fill rgb(196, 198, 200) with outline rgb(180, 182, 184)
- Construction platforms: Light gray rgb(222, 220, 220)
- Solar panels: Pale yellow rgb(245, 246, 189) with brownish outline rgb(152, 142, 132)
- Render as solid filled polygons with clean, precise edges
## Roads and Transportation Networks
**IMPORTANT: All roads rendered as clean, continuous centerlines without surface vehicles. Apply vehicle occlusion removal to show underlying road surface only.
**Unified Road Classification (Simplified Homogeneous System):**
- **Major roads** (motorways, highways, trunk roads, routes):
- Fill: rgb(248, 207, 117) (golden yellow/beige) - unified color for all major routes
- Casing: rgb(70, 55, 30) (dark brown)
- Width: 3-11pt fill with 4-14pt casing (scale by importance, not by type)
- Apply consistent styling across all motorway and trunk classifications
- **Secondary roads** (all secondary and tertiary routes):
- Fill: rgb(250, 243, 158) (pale yellow) - single unified color
- Casing: rgb(65, 65, 25) (olive brown)
- Width: 2-9pt fill with 3.5-11pt casing
- Homogeneous representation regardless of route number
- **Minor roads and local streets** (service roads, residential, tracks):
- Fill: rgb(255, 255, 255) (white) - unified appearance
- Casing: rgb(60, 60, 60) (dark gray)
- Width: 1.5-8pt fill with 2.5-10pt casing
- Consistent styling for all local access roads
- **Footpaths and trails:**
- Color: rgb(60, 60, 60) (dark gray)
- Style: Dashed line pattern with 16-40pt dashes, 2-4pt gaps
- Width: 0.75-2pt
- **Mountain trails:**
- Color: rgb(20, 20, 20) (nearly black)
- Style: Dashed line
- Width: 2-3.5pt
- **Railways and transit:**
- Color: rgb(203, 77, 77) (red-brown)
- Width: 0.75-2pt solid lines
- **Ferries:**
- Color: rgb(77, 164, 218) (blue)
- Style: Dashed lines (6-18pt dashes, 2-4pt gaps)
- Width: 0.4-2.5pt
**Road Surface Rendering:**
- Remove all vehicles, cars, trucks, and moving objects from road surfaces using inpainting techniques
- Reconstruct underlying pavement/surface texture where vehicles were present
- Maintain road boundary precision and lane marking continuity
- Show only static road infrastructure (pavement, markings, surfaces)
## Water Bodies and Features
- Remove all boats, ships in harbour or anchoring and moving objects from water surfaces using inpainting techniques
- Lakes and oceans:
- Fill: rgb(210, 238, 255) (light sky blue)
- Rivers and streams:
- Fill: rgb(181, 225, 253) (slightly darker blue)
- Waterways (rivers, canals):
- Line color: rgb(77, 164, 218) (medium blue)
- Width: 0.25-10pt depending on water body size
- Intermittent streams:
- Line color: rgb(0, 0, 0) (black)
- Opacity: 0.4 (40%)
- Style: Dashed pattern (0.75-4pt dashes with gaps)
- Water boundaries/shorelines:
- Line color: rgb(77, 164, 218) (blue)
- Width: 0.1-1.5pt
- Blur: 0.25pt
## Vegetation and Landcover
- Forests and woodlands:
- Primary: rgb(62, 168, 0) (vibrant green)
- Alternative: rgb(62, 153, 10) (darker green)
- Opacity: 0.1-0.25
- Parks and green spaces:
- Fill: rgb(211, 235, 199) (pale green)
- Opacity: 0.25-0.35
- Park boundaries:
- Line color: rgb(112, 180, 70) (medium green)
- Width: 1-4pt with 0.4pt blur
- Residential/urban green:
- Fill: rgb(246, 219, 164) transitioning to rgba(188, 186, 185, 0.4) (beige to gray)
- Sports fields/pitches:
- Fill: rgb(231, 243, 225) (very pale green)
- Sand/beach areas:
- Fill: rgb(239, 176, 87) (sandy orange)
- Glaciers and ice:
- Fill: rgb(205, 232, 244) (ice blue)
- Opacity: 0.1-0.3
## Terrain and Elevation
- Hillshading (shadows):
- Gray scale gradient from rgb(173, 188, 199) (darkest) to rgb(251, 252, 252) (lightest)
- Apply based on luminosity values -15 to 0
- Opacity: 0.5-1.0
- Sunny slopes:
- Tint: rgb(255, 235, 5) (yellow)
- Opacity: 0.04 (very subtle)
- Contour lines:
- Standard: rgba(180, 110, 13, 0.35) (brown, semi-transparent)
- Width: 0.75-3pt (major contours thicker)
- Blur: 0.25-0.4pt
- Scree, barren and rock fields:
- Apply stippled/textured patterns in gray tones
- Opacity: 0.25-0.35
## Special Features
- Parking areas:
- Fill: rgb(255, 255, 255) (white)
- Outline: rgb(60, 60, 60) (dark gray)
- **Remove parked vehicles** - show empty parking spaces only
- Dams and weirs:
- Fill: rgb(196, 198, 200) (light gray)
- Outline: rgb(154, 156, 158) (medium gray)
- Retaining walls:
- Line: rgb(132, 132, 132) (medium gray)
- Width: 0.5-3pt
## Overall Technical Specifications
- Line blur: 0.25-0.4pt for smooth appearance
- Line cap: Round for roads, butt for boundaries
- Line join: Round for curves, miter for sharp angles
- Projection: Top-down orthographic (flat, no perspective)
- Antialiasing: Smooth edges for all features except terrain fills
- Layering order (bottom to top): Background → Hillshading → Water → Landcover → Roads → Buildings → Boundaries
## Color Accuracy Notes
- Use exact RGB values provided (no approximations)
- Maintain opacity/transparency as specified
- Apply subtle blurring (0.25-0.4pt) to soften hard edges
## Vehicle Removal Specifications
- Detect and mask all vehicles (cars, trucks, buses, motorcycles, trains, boats, ships, airplanes) on roads, traintracks, airports and parking areas
- Apply deep learning-based inpainting to reconstruct road surface beneath vehicles
- Preserve road markings, lane boundaries, and surface texture continuity
- Ensure seamless integration of reconstructed areas with surrounding pavement
- Maintain edge fidelity and structural features of roads during vehicle removal
**Output Requirements:** Vector-style cartographic map with Swiss precision, clean geometry, harmonious homogeneous road classification, vehicle-free representation, proper layering hierarchy, and professional presentation quality suitable for official topographic analysis.

Luego, imaginando una situación en la que varios layers fueran necesarios, he tratado de integrar algunas capas como POIs o modelos 3D realistas como helicopteros, tiendas de campaña de emergencias, coches colapsados, etc

Figura 9 – Integración de layers IA generativa. Gemini con Nano Banana

Lo que funciona bien
Precisión geométrica: La IA mantiene una alta precisión posicional, colocando correctamente las características geográficas. Reconocimiento de estilo: Fuerte fidelidad a las convenciones cartográficas (colores, símbolos, capas). Extracción de información: Buena detección de carreteras, árboles y edificios. Viabilidad de automatización: El flujo de trabajo es reproducible y puede procesar múltiples mosaicos sistemáticamente.

Limitaciones actuales
Desafíos de consistencia: Las interpretaciones generadas varían entre los mosaicos, lo que crea discontinuidades visuales. Procesamiento incompleto: no todos los elementos de imágenes aéreas se interpretan de manera consistente: vegetación y cuerpos de agua (pequeños estanques verdosos). Variaciones de estilo: la IA no siempre aplica el mismo estilo cartográfico en toda el área. Objetos transitorios: los automóviles y las características temporales no siempre se eliminan automáticamente.

Generación final. Benidorm aftermath

Realmente, están pasando muchas cosas a todos los niveles relacionados con la IA, la visualuización, el GIS, los datos en general. Esto no es sino un intento de comprender desde dentro cómo puede estar afectando a la parte que me toca a mí.

Sources
https://gemini.google.com/
https://mappinggis.com/2025/10/generar-mapas-topograficos-automaticamente-con-ia/
https://github.com/davidoesch/ai-topographic-maps/blob/main/prompt.txt

¡Con R de running!

Un registro constante: más de 11 años (desde Agosto 2014 hasta hoy) con más de 1,150 sesiones documentadas. Un proyecto vital; no son solo números, es la cronología de mi disciplina. Puedo decir de nuevo que R me ha roto mis esquemas de geógrafo de ArcGIS, de Global Mapper y QGIS, ahora no todo pasa por el filtro de tener coordenadas, por ejemplo estos insights no tienen coordenadas pero son analizables y se pueden tomar conclusiones que te permiten tomar decisiones rápidas… Echemos un vistazo a mis carreras los últimos años.

Esta década de kilómetros no es solo una base de datos; es una narrativa de superación :-). Para descifrarla, he usado el rigor estadístico de R, que me permite “limpiar” el ruido del esfuerzo diario mediante el uso de percentiles, transformando variables caóticas en tendencias de rendimiento.

El recorrido que he diseñado sigue una secuencia lógica de descubrimiento:

  1. La Densidad: Primero visualizamos la “forma” de tu carrera, identificando dónde se concentra tu volumen habitual mediante crestas de densidad.
  2. La Composición: Luego diseccionamos tu “ADN” anual con gráficos de donas para entender el peso relativo de la intensidad vs. el volumen.
  3. El Radar: Finalmente, llegamos a la joya de la corona: una brújula radial que actúa como un mapa de calor estacional. Aquí, la precisión de R para gestionar gradientes tricolores se une a la estética de proyección polar, permitiéndote ver cómo tu rendimiento “orbita” alrededor de los meses y cómo tu esfuerzo se expande año tras año. Es la unión perfecta entre la estadística descriptiva y la visualización de datos de alto impacto.

Antes de empezar, mi último 2025 que será por cierto el último de este tipo. Los años no pasan en balde y mi próximo proyecto running personal tendrá mucho que ver con el GIS (Python, R) pero eso os lo contaré otro día!

Figura 1 – Todo el año 2025 corriendo. Creado con ggplot2 en R

Empecé a darle vueltas a qué hacer y después de mucho tiempo (una semana, juasssss) me pareció que combinar mis carreras con mis análisis GIS podía ser una buena idea, sobre todo ahora que estoy moviendo mi portfolio para encontrar nuevas oportunidades laborales (trabajo, vamos).

El archivo csv contiene columnas clave como un ID, el trimestre, la fecha exacta en formato dd/mm/yyyy, la distancia total (en km), el ritmo (pace, en min/km), el esfuerzo (cociente entre distancia y ritmo), el esfuerzo que toma en cuenta el desnivel recorrido (effort_h) y la clasificación desde la primera hasta la última entrada.

Figura 1b – Un poco lioso así, ¿no?. ¡Vamos a darle unas vueltas!

Para empezar a darles valor a estos datos y ayudarte con este proyecto, lo primero es quedarse mirando el CSV durante al menos 10minutos, no hacer nada más… una vez (y solo entonces realizado este primera pasito) empezar con los demás 🙂

Figura 2 – La madre de todas las tablas del running 🙂 el CSV.

Empecemos a analizar, ¿han sido todos los años iguales? Lo primero que vemos es una constancia y una regularidad infinita, eso es bueno cuando hablamos de coherencia pero hoy no vamos a hablar de coherencia, vamos a hablar de comprensión, de tomar de perspectiva rápida, de toma de decisiones. En lugar de running puedes aplicarlo con casi cada tema. Paso a visualizar todos los años a la vez, todavía no veo mucho pero empiezo a comprender más de la base de datos…

Figura 3 – Todas las tedencias de todos los años (si os las pongo juntas parecen spaguettis!)

Puntos con Presencia: Al subir el tamaño a 1.5, cada sesión individual ahora tiene peso visual, permitiéndote ver si la línea de tendencia realmente está representando bien la “nube” de ese año.

Claridad en la Leyenda (Label): He añadido el texto el “esfuerzo” (columna effort_h, que mide la relacion entre el tiempo, la distancia y los metros de desnivel positivo) en la parte superior de cada cuadro.

Figura 4 – No sabía que podía hacer gráicos tan bonitos (bueno a mí me gustan!)

Orden Cronológico Vertical: Puedes ver año tras año cómo la “montaña” de los ritmos se mueve. Si el pico de 2025 está más a la derecha que el de 2014, hay progreso real.

Consistencia vs. Dispersión: Si una montaña es muy alta y estrecha, ese año fui un reloj (siempre al mismo ritmo). Si es baja y plana, fui más irregular.

El Color es el Esfuerzo: El granate oscuro marca visualmente los años donde “me exprimí” más, permitiéndome ver si ese esfuerzo se tradujo en ser más rápido (montaña más a la derecha).

Cero Solapamiento Sucio: Al estar una encima de otra pero ligeramente desplazadas, se puede ver todo sin que nada se tape.

Figura 5 – Cómo y cuánto he ido corriendo a lo largo de los años

Labels “Ghost”: Las distancias máximas (ej. 21.1k o 42.2k) aparecen en un gris suave (gray40) y con transparencia. Esto permite que el dato esté ahí si lo buscas, pero no ensucie la visión general de la montaña. Cronología Perfecta: El eje X corre de 2014 a 2025 sin saltos.

Referencia de Maratón: He añadido el hito de los 42.2k en el eje de distancias. Cada maratón, verás el “pico” o el label asomando por esa zona en el año correspondiente (arriba del todo están mis tres maratones, mi orgullo máximo, el 2017 en Valencia, el 2019 en Madrid y 2022 en Nantes).

Hitos de secuencia: marcados discretamente el “Inicio” y el “Actual” para dar contexto temporal a la evolución.

Figura 6 – Uno de los que más me gusta

Nº de carreras (runs): Da el contexto del volumen. No es lo mismo un año con 100% de intensidad sobre 10 carreras que sobre 100.

ø (Media): Te da el valor exacto del esfuerzo medio anual (effort_h).

Leyenda Descriptiva: He titulado la leyenda de forma más técnica (“Nivel de Esfuerzo”) para que quede claro que los colores representan la variable effort_h.

Limpieza: El uso de theme_void() asegura que toda la atención se centre en la forma y el color de tus datos.

Figura 7 – La famosa brújula radial

Al usar x = 0.5 en el comando annotate, los números de los años ahora actúan como una “cremallera” que separa el final y el principio de cada ciclo anual, dejando que los colores de todos los meses brillen.

Mapa de Intensidad Completo: La inclusión del Verde permite identificar tus ritmos “crucero” (aeróbicos), diferenciándolos claramente de los días de recuperación (Amarillo) y los de máxima exigencia (Rojo).

Visión 360°: El gráfico ahora parece un instrumento de precisión. Puedes ver perfectamente cómo en ciertos años “conquistaste” la zona roja y en otros te mantuviste en la zona verde/amarilla de base.

Aquí el código del último gráfico:

library(tidyverse)
library(lubridate)
# 1. CARGA Y FILTRADO (PERCENTILES 10-90)
df_radar <- read_delim("run_r.csv", delim = ";",
locale = locale(decimal_mark = ","),
show_col_types = FALSE) %>%
mutate(
date = dmy(date),
anio = year(date),
mes = month(date, label = TRUE, abbr = TRUE),
pace_decimal = case_when(
ID == 181 ~ 5.516,
str_detect(as.character(pace), ":") ~ {
p <- str_split(as.character(pace), ":", simplify = TRUE)
as.numeric(p[1]) + as.numeric(p[2])/60
},
TRUE ~ as.numeric(str_replace(as.character(pace), ",", "."))
)
)
limites <- quantile(df_radar$pace_decimal, probs = c(0.10, 0.90), na.rm = TRUE)
# 2. PROCESAMIENTO MENSUAL
df_radar_f <- df_radar %>%
filter(anio >= 2014, anio <= 2025) %>%
group_by(anio, mes) %>%
summarise(
ritmo_medio = mean(pace_decimal),
esfuerzo_total = sum(effort_h),
.groups = "drop"
)
# 3. GRÁFICO CON LABELS ENTRE MESES Y TRICOLOR
ggplot(df_radar_f, aes(x = mes, y = as.factor(anio), fill = ritmo_medio)) +
# Celdas de ritmo
geom_tile(color = "white", size = 0.2) +
# Círculos de esfuerzo (blancos, con tamaño dinámico)
geom_point(aes(size = esfuerzo_total), color = "white", alpha = 0.6) +
coord_polar() +
# ESCALA TRICOLOR: Rojo (Rápido) -> Verde -> Amarillo (Lento)
scale_fill_gradientn(
colors = c("#C0392B", "#27AE60", "#F1C40F"), # Rojo -> Verde -> Amarillo
name = "Ritmo Medio",
limits = c(limites[1], limites[2]),
oob = scales::squish,
labels = function(x) sprintf("%d:%02d", floor(x), round((x - floor(x)) * 60))
) +
scale_size_continuous(name = "Volumen Esfuerzo", range = c(0.5, 9)) +
# LABELS DE AÑO: Situados en el borde entre meses (x = 0.5 es entre Dic y Ene)
annotate("text", x = 0.5, y = as.factor(2014:2025), label = 2014:2025,
color = "gray30", size = 2.8, fontface = "bold", hjust = 0.5) +
labs(
title = "EVOLUCIÓN DE RENDIMIENTO 2014-2025",
subtitle = "Rojo: Velocidad | Verde: Aeróbico | Amarillo: Recuperación\nLos años se indican en la línea divisoria para no tapar los datos",
x = "", y = ""
) +
theme_minimal(base_size = 14) +
theme(
panel.grid.major = element_line(color = "gray94", linetype = "dotted"),
axis.text.x = element_text(face = "bold", size = 12, color = "black"),
axis.text.y = element_blank(),
plot.title = element_text(face = "bold", size = 20, hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, size = 11, color = "gray40"),
legend.position = "right"
)

El análisis exhaustivo de esta década de entrenamiento muestra mi capacidad para sostener cargas de trabajo elevadas (y yo no lo sabía!), que ha evolucionado hacia una mayor inteligencia biológica o comprensíón del cuerto de uno mismo, permitiéndome identificar y explotar tus ventanas de máxima eficiencia con una precisión que los registros brutos no alcanzaban a mostrar. Esto es lo que buscaba!

Espero que os haya gustado. En breve más!

Alberto C.
(Geo)data Analyst

Fuentes
https://posit.co/download/rstudio-desktop/

R analysis for HR corporate talent management

I am a geographer by training, and my professional career has always had a predominantly geospatial focus. Having recently completed a forty-hour course in R, using RStudio and GitHub, I feel that a whole new world of analysis has opened up before me. This work represents the meeting point between my basic geographical instinct and the technical capabilities of statistical programming. It is important to emphasise that I have invented this data and model entirely, so the results have no real meaning and contain inevitable biases. Their sole purpose is to learn and demonstrate the capabilities of this language. I believe that geographical knowledge and code are interdependent, as one without the other would not function successfully. It is precisely this symbiosis that I hope will make a difference in my current job search.

In terms of visual results, the analysis integrates layers of complexity that facilitate understanding of the territory. Using logarithmic scaling, we have managed to bring cities with very different data volumes together on the map, allowing us to identify small talent hubs that would normally be hidden by large capital cities. Furthermore, we do not simply place points on the map, but use colour gradients to link average age with location, making it easier to detect groups of young talent as opposed to more senior profiles. The inclusion of a histogram allows us to validate the general distribution of the data in relation to its geographical dispersion. This exercise demonstrates that the combination of territorial analysis and programming can transform data into a clear and strategic visual narrative.

Let us imagine an HR company that seeks to understand when its clients, with certain educational backgrounds and occupations, are most likely to decide to move to another city. In other words, is the profile of those willing to move mostly young or older? Urban or rural? With low or high levels of education? Where are they concentrated?

The database is a CSV file with only nine fields, thoroughly cleaned up so we can start playing around with the tidyverse library.

Figura 2 – Invented RH data model for decision making 🙂

Once the project has been set up, which can actually be one of the most complex parts (directories, files with appropriate separators, overall consistency of information, etc.), we start to get our hands on the code (please don’t look too closely, I’m just a humble geographer… and blond!). Below, I begin to understand the basics and create a histogram of the number of people broken down by age.

Figura 3 – Adding my first ggplot2 in this R project

But, what is an analysis without a map to overlay it?? 🙂

Figura 4 – Adding the base map, and start sketching the “invented” model

Inserting a map, adding labels, playing with sizes, colors, alphas, shapes… I have a lot to thank to my “geography visualization” teachers at the UVA in Valladolid!

Figura 5 – If “SI/YES” they’re willing to move to switch to a better job

A little detour, let’s focus on Andalucía…

Figura 6 – Focusing on specific regions

Here comes the fun. Adding colors to disaggreagate profiles and size to rapidly get to understand where is the bigger amount of people willing to move…

Figura 7 – A bigger title for a quicker understanding. ¿Have we saved one second? It’s definitely worth.

¿Do I need to disaggregate by bigger regions (CCAA in Spain)?

Figura 8 – disaggregating in R using facet wrap

Please don’t blame me for the incoherence… this is FULLY RANDOM!!!! 🙂 Do we understand if we use a color fade for average age?

Figura 8 – Getting closer to the final result

Should we try a violin-like diagram showing that I don’t just make pretty maps or graphs, but that you establish aKPI? Any HR recruiter can see which groups are ‘aged’ or which are the ‘youngest’ in relation to the total workforce. A Senior Geodata Analyst should know how to interpret the social reality behind the data 🙂

Or even better, a disaggregation and highlight of MAX-MIN cases per level of education.

And the final result today (so far): the main cities, the age factor, the average candidate profile. Everything has an explanation:-)

Figura 9 – OUTCOM: Those willing to relocate is mostly young, urban and highly educated, concentrated in the country’s major economic hubs

Urban Concentration: The map shows that willingness to relocate is not uniform; it is concentrated heavily in Madrid, Barcelona, Seville, and Valencia. The larger circles in these areas confirm that they are the main ‘talent engines’.

The Age Factor: The trend line and histogram reveal a clear negative correlation: the younger the age, the greater the willingness. Younger talent (light/yellow colours) is the most flexible, while from the age of 45-50 (dark colours), the intention to move falls dramatically.

Geographical Balance: Thanks to logarithmic scaling, we see that although capital cities dominate, there is a constant flow of profiles in medium-sized cities, suggesting that talent is distributed but needs incentives depending on the stage of life.

Candidate Profile: The histogram confirms that the bulk of those interested are between 25 and 38 years old. Outside this range, mobility becomes exceptional.

Borders: The visualisation by autonomous community allows us to identify that regions such as Andalusia and Catalonia have a network of secondary cities with high mobility, unlike other regions where everything is concentrated in a single point.

In conclusion: the profile of those willing to relocate is mostly young, urban and highly educated, concentrated in the country’s major economic hubs.

I hope you enjoyed it. If you can think of any other scenarios where we don’t have to make up the data, I’ll give it some thought and write another post!

Alberto C.
Geospatial analyst and someone who is looking for a job. ¿Do you have one?

URBAN ATLAS 2018 + WORLDPOP 100m/GHSL 100m estimates over Madrid

Urban Atlas: Precision Geoespacial en el Corredor de Copernicus. Usos del Suelo combinados con estimaciones de población sobre cada una de las clases, un paso más en combinación de fuentes de Datos Abiertos en la nube.

Este análisis representa un pequeño test rápido desarrollado por Alberto C (Geovisualization.net) para mostrar las potencialidades de uso de un asset externo en la plataforma Google Earth Engine (GEE) mediante JavaScript. Se trata de un mapa de usos del suelo (LULC / Clutter) de media-alta resolución que ofrece una precisión temática y espacial muy precisa (significativamente superior a la de Corine Land Cover) a lo que añadimos una estimación de población con una base de datos transnacional como WOLDPOP 100m y otra en paralelo GHSL 100m. Este flujo de trabajo, que integra capas externas con datasets globales de computación en la nube, se ha ejecutado íntegramente en unos pocos minutos, demostrando la agilidad operativa de las herramientas cloud actuales.

Urban Atlas (UA) representa el estándar de oro dentro del Copernicus Land Monitoring Service (CLMS) para el análisis de la morfología urbana en Europa. A diferencia de Corine Land Cover, UA ofrece una resolución temática y espacial drásticamente superior (Unidad Mínima de Mapeo de 0.25 ha para clases urbanas), permitiendo discriminar entre tejidos urbanos continuos y discontinuos con una precisión de densidad del 10% al 80%.

Figura 1. Interfaz de GEE visualizando el ASSET de URBAN ATLAS

Casos de Uso de Vanguardia: Del Urbanismo a la Resiliencia

En la actualidad, el Urban Atlas se ha consolidado como la capa base para modelos críticos:

  • Modelización de Islas de Calor Urbanas (UHI): Gracias a la diferenciación entre superficies selladas y áreas verdes, UA es el input fundamental para correlacionar la temperatura de superficie (LST) con la tipología edificatoria.
  • Gestión de Escorrentía y Riesgo de Inundación: La clase High/Low Imperviousness permite calcular coeficientes de escorrentía precisos para el diseño de infraestructuras hidráulicas.
  • Planificación de la “Ciudad de los 15 minutos”: Se utiliza para analizar la fragmentación del ecosistema urbano y la accesibilidad a servicios según el tejido residencial.
  • Cuentas de Ecosistemas: Monitorización del “Urban Sprawl” (expansión urbana) y la pérdida de suelo agrícola o forestal colindante a las Funcional Urban Areas (FUA).

Integración en Google Earth Engine: Escalando el Análisis

La verdadera potencia del Urban Atlas se libera al integrarse en motores de Cloud Computing como GEE. Pasar de un análisis local por municipio a un análisis continental es ahora una cuestión de código, no de capacidad de hardware.

Ventajas de la automatización en GEE:

  1. Zonal Statistics a Gran Escala: Mediante el uso de reduceRegions, se pueden extraer perfiles de uso de suelo para miles de ciudades en segundos, cruzándolos con datos de población.
  2. Fusión Multi-Sensor: GEE permite intersectar el ráster categórico de Urban Atlas con series temporales de Sentinel-2 (NDVI) o Sentinel-1 (Backscatter) para validar la salud de la vegetación urbana o la altura de las estructuras.
  3. Remapping Dinámico: Como hemos visto en flujos de trabajo previos, la capacidad de aplicar un .remap() instantáneo permite simplificar las 27 clases originales de UA en indicadores binarios (Gris vs. Verde) para generar histogramas de resiliencia en tiempo real.

Ejemplo de flujo lógico en GEE:

JavaScript

// Agregación de clases para análisis de infraestructura verde
var greenSpace = ua_image.remap([14100, 14200, 31000], [1, 1, 1], 0);
var stats = greenSpace.reduceRegion({
  reducer: ee.Reducer.mean(),
  geometry: region_interes,
  scale: 10
});

El Futuro: Automatización y Deep Learning

El siguiente paso que estamos viendo en la industria es el uso de Urban Atlas como Ground Truth (verdad terreno) para entrenar redes neuronales convolucionales (CNN) sobre imágenes de muy alta resolución (VHR), permitiendo actualizar los mapas de uso de suelo de forma continua sin esperar a los ciclos de actualización trienales de Copernicus.

Figura 2. Histograma en consola de % de área por cada clase.

Las estimaciones de población se obtuvieron mediante la intersección espacial de los datos de población en cuadrículas de WorldPop 2020 con clases categóricas de uso del suelo y la agregación de los recuentos de población por clase.

Figura 3. Clases corregidas para mejor comprensión.

Cambiamos fácilmente la leyenda puesto que necesitamos unos colores más más adecuados

Figura 4. Leyenda más adecuada al tejido urbano.

Las clases de uso del suelo se obtuvieron del Atlas Urbano Copernicus y se agruparon en categorías temáticas siguiendo la nomenclatura oficial del Atlas Urbano. El scrip de GEE saca directamente esta tabla en formato CSV.

Figura 5. Población por clases

Si bien no hay correspondencia con la población real del distrito esto es porque por un lado tenemos una fuente vectorial de media-alta resolución (urban atlas) mientras que los datos de población vienen de una fuente continua de 100m (100 veces menor). Este análisis advierte de las limitaciones del estudio mientras que se enfoca en las potencialidades de uso de fuentes en la nube que con toda lógica, deben de hacerse coincidir en aras de una completa coherencia.

Si te interesa el tema, pídeme el ASSET de URBAN Atlas (lo puedes ver en las fuentes abajo del post) o el ASSET de población sobre el AOI para que puedas importarlo en tu workspace o si no quieres replicarlo simplemente dime qué te parece este enfoque!
Un saludo!

Alberto C.
Geodata Analyst

Sources:
https://code.earthengine.google.com/afe5dbf9b75c53dda2b82e4cad6d0b4e
https://land.copernicus.eu/en/products/urban-atlas/urban-atlas-2018#download
https://developers.google.com/earth-engine/datasets/catalog/WorldPop_GP_100m_pop?hl=es-419
https://human-settlement.emergency.copernicus.eu/
https://hub.worldpop.org/project/categories?id=3
https://developers.google.com/earth-engine/datasets/catalog/JRC_GHSL_P2023A_GHS_BUILT_V