Aprendiendo Datos Geoespaciales
Python Geospatial Analysis // Learning Geospatial Data
Uno de los aspectos más desafiantes del análisis geoespacial son los datos. Los datos geoespaciales ya incluyen docenas de formatos de archivo y estructuras de bases de datos, y continúan evolucionando para incluir nuevos tipos de datos y estándares. Además, casi cualquier formato de archivo puede contener técnicamente información geoespacial simplemente agregando una ubicación.
En este capítulo exploraremos los formatos de datos comunes, examinaremos las características compartidas de los datos geoespaciales, comprenderemos la indexación espacial, conoceremos los tipos de datos vectoriales y ráster más utilizados, y obtendremos información sobre tipos más complejos como nubes de puntos, servicios web y bases de datos geoespaciales.
Formatos de Datos Comunes
Como analista geoespacial, frecuentemente encontrarás los siguientes tipos generales de datos. Cada formato contiene sus propios desafíos para el acceso y procesamiento. Cuando realizas análisis sobre datos, generalmente debes hacer alguna forma de preprocesamiento primero.
Estructuras de Datos
Los datos geoespaciales generalmente caen en dos categorías fundamentales: datos vectoriales y datos ráster. Entender la diferencia entre estas dos estructuras es fundamental para cualquier analista geoespacial.
Indexación Espacial
La indexación espacial organiza los datos geoespaciales de manera que permite un rápido acceso mediante ubicación geográfica. Los conjuntos de datos geoespaciales pueden contener millones de puntos o polígonos, así como imágenes que contienen billones de píxeles. Sin la indexación espacial, incluso las operaciones más simples serían impracticables.
Algoritmos de Indexación
La mayoría de los algoritmos de indexación espacial mapean coordenadas de punto flotante a una grilla de enteros de tamaño fijo. Las comparaciones con enteros son mucho más eficientes computacionalmente que trabajar con números de punto flotante.
Quadtree Index
El índice Quadtree subdivide recursivamente un espacio bidimensional en cuatro cuadrantes. Es particularmente efectivo para datos con distribución desigual, ya que subdivide más las áreas con mayor densidad de datos.
# Representación conceptual de un Quadtree class QuadTreeNode: def __init__(self, bounds, max_objects=10): self.bounds = bounds # (x, y, width, height) self.max_objects = max_objects self.objects = [] self.children = [] # [NW, NE, SW, SE] def subdivide(self): """Divide el nodo en 4 cuadrantes""" x, y, w, h = self.bounds hw, hh = w / 2, h / 2 self.children = [ QuadTreeNode((x, y + hh, hw, hh)), # NW QuadTreeNode((x + hw, y + hh, hw, hh)), # NE QuadTreeNode((x, y, hw, hh)), # SW QuadTreeNode((x + hw, y, hw, hh)) # SE ] def insert(self, point): """Inserta un punto en el quadtree""" if not self._contains(point): return False if len(self.objects) < self.max_objects: self.objects.append(point) return True if not self.children: self.subdivide() for child in self.children: if child.insert(point): return True return False
R-tree Index
El índice R-tree agrupa objetos cercanos y los representa con su rectángulo delimitador mínimo (MBR). Es el algoritmo de indexación espacial más utilizado en bases de datos geoespaciales debido a su eficiencia con datos dinámicos.
256 x 256 para formatos de archivo simples, o tan grandes como 3 millones x 3 millones en bases de datos geoespaciales diseñadas para incorporar cada sistema de coordenadas y resolución posible.
Overviews (Pirámides)
Los datos de overview son versiones remuestreadas y de menor resolución de conjuntos de datos ráster que proporcionan vistas en miniatura o simplemente vistas de carga más rápida a diferentes escalas de mapa. También se conocen como pirámides.
Metadatos Geoespaciales
Los metadatos son cualquier dato que describe el conjunto de datos asociado. Los ejemplos comunes incluyen elementos básicos como la huella del conjunto de datos en la Tierra, así como información más detallada como la proyección espacial e información que describe cómo se creó el conjunto de datos.
Estructura de Archivos Típica
Tipos de Datos Vectoriales
Los datos vectoriales representan características geográficas usando geometrías definidas matemáticamente: puntos, líneas y polígonos con coordenadas precisas. Esta precisión hace que los datos vectoriales sean ideales para representar límites, carreteras, edificios y otras características discretas.
Shapefiles
El formato shapefile, desarrollado por Esri, es el estándar de facto para datos vectoriales geoespaciales. A pesar de sus limitaciones (nombres de campo de 10 caracteres, tamaño máximo de 2GB), sigue siendo el formato más ampliamente soportado.
import struct # Leer el bounding box de un shapefile def read_shapefile_bbox(shp_path): """Lee el bounding box desde el header del shapefile""" with open(shp_path, 'rb') as f: # Saltar al byte 36 donde inicia el bbox f.seek(36) # Leer 8 valores double (64 bytes) bbox_data = f.read(64) bbox = struct.unpack('<8d', bbox_data) return { 'x_min': bbox[0], 'y_min': bbox[1], 'x_max': bbox[2], 'y_max': bbox[3], 'z_min': bbox[4], 'z_max': bbox[5], 'm_min': bbox[6], 'm_max': bbox[7] } # Uso bbox = read_shapefile_bbox('mi_archivo.shp') print(f"Extensión: {bbox['x_min']:.4f}, {bbox['y_min']:.4f}") print(f" a {bbox['x_max']:.4f}, {bbox['y_max']:.4f}")
a -89.0200, 30.6470
GeoJSON
GeoJSON es un formato basado en JSON para codificar estructuras de datos geográficos. Su sintaxis amigable con JavaScript lo convierte en el formato preferido para aplicaciones web y APIs REST geoespaciales.
# GeoJSON es válido tanto como JSON como Python dict geometry_collection = { "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [-89.33, 30.0] }, { "type": "LineString", "coordinates": [ [-89.33, 30.30], [-89.36, 30.28] ] }, { "type": "Polygon", "coordinates": [[ [-104.05, 48.99], [-97.22, 48.98], [-96.58, 45.94], [-104.03, 45.94], [-104.05, 48.99] ]] } ] } # Acceder a los datos directamente print(geometry_collection['geometries'][0]) # {'type': 'Point', 'coordinates': [-89.33, 30.0]}
GeoPackage
GeoPackage es un estándar abierto de OGC basado en SQLite. Es un contenedor de base de datos basado en archivos que es independiente de plataforma, proveedor y software. Representa un intento de alejarse de los problemas generados por formatos de datos propietarios o limitados.
Tipos de Datos Ráster
Los datos ráster consisten en filas y columnas de celdas o píxeles, donde cada celda representa un único valor. La forma más fácil de pensar en los datos ráster es como imágenes. Sin embargo, los conjuntos de datos ráster no necesariamente se almacenan como imágenes; también pueden ser archivos de texto ASCII o BLOBs en bases de datos.
Resolución Geoespacial
Una diferencia clave entre los datos ráster geoespaciales y las imágenes digitales regulares es cómo expresan la resolución. Las imágenes digitales expresan la resolución como puntos por pulgada (DPI) o megapíxeles totales. Los datos ráster geoespaciales usan la distancia en el terreno que cada celda representa.
| FORMATO | TIPO | CARACTERÍSTICAS |
|---|---|---|
TIFF/GeoTIFF |
Imagen | Metadatos geoespaciales embebidos, múltiples bandas |
JPEG |
Comprimido | Con pérdida, pequeño tamaño, requiere world file |
PNG |
Comprimido | Sin pérdida, transparencia, ideal para web |
ASCII Grid |
Texto | Legible por humanos, portable pero grande |
MrSID/ECW |
Wavelet | Alta compresión, acceso aleatorio eficiente |
NetCDF/HDF |
Científico | Multidimensional, metadatos extensivos |
ASCII Grid Format
Los ASCII Grids son archivos de texto plano que representan datos ráster. Aunque no son eficientes en espacio, son fáciles de leer, crear y depurar, haciéndolos ideales para intercambio de datos y aprendizaje.
ncols 5 nrows 4 xllcorner -89.5 yllcorner 30.0 cellsize 0.1 NODATA_value -9999 100 110 120 115 105 105 115 125 120 110 110 120 130 125 115 115 125 135 130 120
def read_ascii_grid(filepath): """Lee un archivo ASCII Grid y retorna header + datos""" with open(filepath, 'r') as f: # Leer header (6 líneas) header = {} for _ in range(6): line = f.readline().split() header[line[0].lower()] = float(line[1]) # Leer datos como matriz data = [] for line in f: row = [float(val) for val in line.split()] data.append(row) return header, data # Uso header, elevation = read_ascii_grid('terrain.asc') print(f"Grid: {int(header['ncols'])}x{int(header['nrows'])}") print(f"Cell size: {header['cellsize']} degrees") print(f"Max elevation: {max(max(row) for row in elevation)}")
Cell size: 0.1 degrees
Max elevation: 135.0
World Files
Los world files son archivos sidecar que proporcionan información de georreferenciación para imágenes que no tienen metadatos espaciales embebidos. La extensión sigue un patrón: primera letra, última letra, 'w' (ej: .jgw para .jpg, .pgw para .png).
0.000278 # Tamaño de pixel en X (grados) 0.000000 # Rotación sobre eje Y 0.000000 # Rotación sobre eje X -0.000278 # Tamaño de pixel en Y (negativo = norte arriba) -89.500000 # Coordenada X del centro del pixel superior izq 30.500000 # Coordenada Y del centro del pixel superior izq
Datos de Nubes de Puntos
Las nubes de puntos representan datos 3D como colecciones de puntos individuales, cada uno con coordenadas X, Y, Z y opcionalmente atributos adicionales como intensidad o color. Son producidas principalmente por sensores LIDAR (Light Detection and Ranging).
LIDAR
LIDAR usa pulsos láser para medir distancias con precisión milimétrica. Un sensor LIDAR aerotransportado puede capturar millones de puntos por segundo, creando representaciones 3D detalladas del terreno y las estructuras sobre él.
import struct # Estructura básica de un punto LAS class LidarPoint: """Representa un punto LIDAR en formato LAS""" def __init__(self, x, y, z, intensity=0, classification=0): self.x = x # Coordenada Este self.y = y # Coordenada Norte self.z = z # Elevación self.intensity = intensity # Intensidad del retorno self.classification = classification @property def class_name(self): """Clasificación ASPRS estándar""" classes = { 0: 'Never Classified', 1: 'Unassigned', 2: 'Ground', 3: 'Low Vegetation', 4: 'Medium Vegetation', 5: 'High Vegetation', 6: 'Building', 7: 'Low Point (Noise)', 9: 'Water', } return classes.get(self.classification, 'Other') # Ejemplo de uso point = LidarPoint(500000.5, 4500000.3, 125.7, 45, 6) print(f"Punto: ({point.x}, {point.y}, {point.z})") print(f"Clasificación: {point.class_name}")
Clasificación: Building
Servicios Web Geoespaciales
Los servicios web permiten el acceso a datos geoespaciales a través de internet usando protocolos estándar. El Open Geospatial Consortium (OGC) ha definido especificaciones estándar que aseguran la interoperabilidad entre diferentes sistemas.
Ejemplo: Consulta WFS
import requests def get_features_wfs(base_url, typename, bbox=None): """Consulta un servicio WFS y retorna GeoJSON""" params = { 'service': 'WFS', 'version': '2.0.0', 'request': 'GetFeature', 'typeName': typename, 'outputFormat': 'application/json', 'srsName': 'EPSG:4326' } if bbox: params['bbox'] = ','.join(map(str, bbox)) response = requests.get(base_url, params=params) response.raise_for_status() return response.json() # Uso try: features = get_features_wfs( 'https://example.com/geoserver/wfs', 'namespace:layer_name', bbox=[-90, 30, -89, 31] ) print(f"Features encontrados: {len(features['features'])}") except Exception as e: print(f"Error: {e}")
Bases de Datos Geoespaciales
Las geodatabases son bases de datos convencionales con extensiones espaciales que permiten almacenar, consultar y analizar datos geométricos. La geometría se almacena típicamente en una columna de tipo espacial junto con las columnas de atributos tradicionales.
PostgreSQL/PostGIS
PostGIS es la extensión espacial más popular para PostgreSQL. Proporciona soporte para objetos geográficos, permitiendo consultas SQL espaciales avanzadas.
-- Crear tabla con geometría CREATE TABLE cities ( id SERIAL PRIMARY KEY, name VARCHAR(100), population INTEGER, geom GEOMETRY(Point, 4326) ); -- Insertar punto INSERT INTO cities (name, population, geom) VALUES ( 'New Orleans', 390144, ST_SetSRID(ST_MakePoint(-90.0715, 29.9511), 4326) ); -- Consulta espacial: ciudades dentro de 100km SELECT name, population, ST_Distance( geom::geography, ST_SetSRID(ST_MakePoint(-90.0, 30.0), 4326)::geography ) / 1000 AS distance_km FROM cities WHERE ST_DWithin( geom::geography, ST_SetSRID(ST_MakePoint(-90.0, 30.0), 4326)::geography, 100000 -- 100 km en metros );
Resumen del Capítulo
01 // Formatos de Datos
Los datos geoespaciales vienen en docenas de formatos: CSV, imágenes, XML, JSON, bases de datos y servicios web. Cada uno tiene sus desafíos únicos.
02 // Vector vs Ráster
Vectoriales para características discretas (puntos, líneas, polígonos). Ráster para datos continuos (imágenes, elevación, temperatura).
03 // Indexación Espacial
Quadtree, R-tree y grillas permiten búsquedas espaciales eficientes en millones de registros o billones de píxeles.
04 // Shapefiles & GeoJSON
Shapefile sigue siendo el estándar de facto. GeoJSON domina las APIs web por su simplicidad y compatibilidad con JavaScript.
05 // Nubes de Puntos
LIDAR genera millones de puntos 3D por segundo. Cada punto tiene coordenadas XYZ, intensidad y clasificación.
06 // Servicios OGC
WMS para mapas renderizados, WFS para vectores, WCS para ráster, WMTS para tiles. Estándares que garantizan interoperabilidad.