Fácil de decir, pero muy difícil de obtener: hasta donde he visto no hay ningún dataset del IGN que aporte ese dato, por lo que hay que calcularlo.
¿Cómo? pues a partir de los ficheros GeoTIFF presentes en el modelo digital de elevaciones, es necesario descargarse todos los relativos a la zona a analizar, y utilizar un script al que se le pase una coordenada y devuelva el cálculo.
Consideraciones:
- Cuanto más fina sea la malla, mayor precisión: MDT02 es la que da un resultados más adecuados
- Cada fichero de la malla MDT02 ocupa unos 100Mb.
- Para analizar una zona es muy probable que se necesiten múltiples hojas, que habría que combinar en una sola. Se puede hacer con QGIS.
Se me ha ocurrido pedir un script en python a chatGPT
para que me calcule si una coordenada es prominente o no. Tal vez en QGIS exista algún plugin más acertado pero lo desconozco:
SCRIPT ACTUALIZADO
import argparse
import rasterio
import numpy as np
from pyproj import Transformer
def estimar_pixel_metros(src):
px_x = abs(src.transform[0])
px_y = abs(src.transform[4])
px_m = (px_x + px_y) / 2
if px_m < 0.1:
px_m = 2 # Forzamos 2m si sale ridículamente pequeño
return px_m
def destacar_punto(tif_path, lon, lat, ele, osm_id):
radios_peque = [1, 3, 5]
radios_grande = [10, 20, 50, 100, 200, 500]
pesos_grande = [1, 2, 3, 4, 5, 6, 7] # len=7, 500 incluido
total_peso = sum(pesos_grande)
with rasterio.open(tif_path) as src:
px_m = estimar_pixel_metros(src)
transformer = Transformer.from_crs("EPSG:4326", src.crs, always_xy=True)
x, y = transformer.transform(lon, lat)
row, col = src.index(x, y)
elev_data = src.read(1)
# Aplicar offset del DEM con la elevación oficial (ele)
delta_ele = ele - elev_data[row, col]
elev_data_adj = elev_data + delta_ele
# Función interna para evaluar criterio p95 (radios grandes)
def criterio_p95(r):
r_pix = r
row_start = max(row - r_pix, 0)
row_end = min(row + r_pix + 1, elev_data_adj.shape[0])
col_start = max(col - r_pix, 0)
col_end = min(col + r_pix + 1, elev_data_adj.shape[1])
ventana = elev_data_adj[row_start:row_end, col_start:col_end].flatten()
ventana = ventana[ventana > -10000]
if ventana.size == 0:
return False, 0
p95 = np.percentile(ventana, 95)
cumple = ele >= p95
return cumple, p95 * px_m
# Función interna para criterio local (radios pequeños)
def criterio_local(r):
r_pix = r
row_start = max(row - r_pix, 0)
row_end = min(row + r_pix + 1, elev_data_adj.shape[0])
col_start = max(col - r_pix, 0)
col_end = min(col + r_pix + 1, elev_data_adj.shape[1])
ventana = elev_data_adj[row_start:row_end, col_start:col_end]
ventana_val = ventana[ventana > -10000]
if ventana_val.size == 0:
return False, 0
media_vecinos = np.mean(ventana_val)
cumple = ele > media_vecinos
return cumple, media_vecinos * px_m
# Evaluamos radios grandes con pesos
resultados_p95 = []
suma_pesos = 0
for i, r in enumerate(radios_grande):
cumple, val = criterio_p95(r)
resultados_p95.append((cumple, val))
if cumple:
suma_pesos += pesos_grande[i]
cumple_p95 = suma_pesos >= total_peso / 2
# Evaluamos radios pequeños sin pesos
resultados_local = []
for r in radios_peque:
cumple, val = criterio_local(r)
resultados_local.append((cumple, val))
cumple_local = sum([1 for c, _ in resultados_local if c]) >= len(radios_peque) / 2
cumple_total = cumple_p95 and cumple_local
icono_final = "✅" if cumple_total else "❌"
alerta = ""
delta_dem = ele - elev_data[row, col]
if abs(delta_dem) > 10:
alerta = "❗"
# Formateo ticks para p95 y local
def ticks(resultados):
return "".join("✔" if r else "✘" for r, _ in resultados)
rango_p95 = f"{radios_grande[0]*px_m:.1f}m -> {radios_grande[-1]*px_m:.1f}m"
rango_local = f"{radios_peque[0]*px_m:.1f}m -> {radios_peque[-1]*px_m:.1f}m"
print(f"{icono_final} | criteria: {ticks(resultados_p95)} (p95: {rango_p95}) {ticks(resultados_local)} (local: {rango_local}) | ΔDEM: {delta_dem:+.2f}m {alerta} | https://www.openstreetmap.org/node/{osm_id}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Estimación simple de prominencia local en un DEM.")
parser.add_argument("tif_path", help="Archivo GeoTIFF")
parser.add_argument("lon", type=float, help="Longitud")
parser.add_argument("lat", type=float, help="Latitud")
parser.add_argument("ele", type=float, help="Elevación oficial a comprobar")
parser.add_argument("osm_id", help="ID nodo OSM")
args = parser.parse_args()
destacar_punto(args.tif_path, args.lon, args.lat, args.ele, args.osm_id)
Para probar el script podéis obtener un CSV así, y luego guardarlo en un fichero:
[out:csv(::lon, ::lat, "ele", ::id)][timeout:25];
node[natural=peak][!"name"]["ele"]({{bbox}});
out geom;
y luego ejecutarlo en bucle:
while IFS=$'\t' read -r lon lat ele id;
do python prominencia.py <fichero>.tif $lon $lat $ele $id;
done < prominencias
Se necesita un fichero GeoTIFF