sábado, 24 de enero de 2026

Análisis geoespacial con Python - Parte 9 - Datos en Tiempo Real

Datos en Tiempo Real - Python Geospatial Analysis
Python // Geospatial Analysis

Datos en Tiempo Real

Capítulo 9: Revolucionando el análisis geoespacial con datos vivos

Un dicho común entre analistas geoespaciales es: "Un mapa queda obsoleto tan pronto como se crea". Esto refleja el hecho de que la Tierra y todo lo que hay en ella está en constante cambio. La edad de los datos geoespaciales en un mapa se conoce como data currency (vigencia de datos).

El mapeo web, los módems celulares inalámbricos y las antenas GPS de bajo costo han cambiado este enfoque. Ahora es logísticamente factible y económico monitorear un objeto o sistema que cambia rápidamente y transmitir esos cambios a millones de personas en línea. Este concepto de conectar dispositivos en una red de datos e información se llama The Internet of Things (IoT).

MÓDULO_01

Limitaciones de Datos en Tiempo Real

El término datos en tiempo real típicamente significa casi en tiempo real. Algunos dispositivos de rastreo capturan datos en tiempo real y pueden actualizarse varias veces por segundo. Sin embargo, las limitaciones de la infraestructura que transmite esos datos pueden restringir la salida a cada 10 segundos o más.

Flujo de Datos en Tiempo Real
Dispositivo GPS
Red Celular
API REST
Aplicación

El radar meteorológico es un ejemplo perfecto. Un Radar Meteorológico Doppler (DWR) barre continuamente, pero los datos típicamente están disponibles en línea cada cinco minutos.

Limitaciones Principales

  • Ancho de banda de red - Restricciones en el tamaño de datos transmitidos
  • Latencia de red - Limita la frecuencia de actualización de datos
  • Disponibilidad de fuente - Restricciones como vida de batería del dispositivo
  • Falta de control de calidad - Datos disponibles instantáneamente sin verificación
  • Vulnerabilidades de seguridad - Ingesta rápida de datos no verificados
Los mashups web a menudo usan datos en tiempo real, pero típicamente están limitados a mostrar datos preprocesados en un mapa y dar acceso a una API JavaScript. Para usar datos en tiempo real para análisis geoespacial, necesitas poder acceder a ellos como datos de puntos o un raster georreferenciado.
MÓDULO_02

Rastreo de Vehículos

Para nuestra primera fuente de datos en tiempo real, usaremos la excelente NextBus API. NextBus es un servicio comercial que rastrea transporte público para municipalidades incluyendo autobuses, tranvías y trenes.

Arquitectura NextBus API
NextBus REST API
agencyList
routeList
vehicleLocations

NextBus publica datos de rastreo a través de una API REST. Usando llamadas URL API, los desarrolladores pueden solicitar información sobre un vehículo y recibir un documento XML sobre su ubicación.

Obtener Lista de Agencias

http://webservices.nextbus.com/service/publicXMLFeed?command=agencyList

Esta llamada devuelve un documento XML conteniendo información de agencias:

<agency tag="lametro" 
        title="Los Angeles Metro" 
        regionTitle="California-Southern"/>

Obtener Lista de Rutas

http://webservices.nextbus.com/service/publicXMLFeed?command=routeList&a=lametro

Obtener Ubicaciones de Vehículos

http://webservices.nextbus.com/service/publicXMLFeed?command=vehicleLocations&a=lametro&r=2&t=0

El parámetro t=0 retorna datos de los últimos 15 minutos. El documento XML devuelto contiene:

<vehicle id="7582" 
         routeTag="2" 
         lat="34.097992" 
         lon="-118.350365" 
         secsSinceReport="44" 
         speedKmHr="0"/>

Script de Rastreo

vehicle_tracker.py
import urllib.request
import urllib.parse
import urllib.error
from xml.dom import minidom

# Nextbus API command mode
command = "vehicleLocations"
# Nextbus customer to query
agency = "lametro"
# Bus we want to query
route = "2"
# Time: 0 returns last 15 minutes
epoch = "0"

# Build query URL
url = "http://webservices.nextbus.com"
url += "/service/publicXMLFeed?"
url += "command=" + command
url += "&a=" + agency
url += "&r=" + route
url += "&t=" + epoch

# Access the REST URL
feed = urllib.request.urlopen(url)
if feed:
    # Parse the xml feed
    xml = minidom.parse(feed)
    # Get the vehicle tags
    vehicles = xml.getElementsByTagName("vehicle")
    
    if vehicles:
        bus = vehicles.pop()
        att = bus.attributes
        print(att["lon"].value, ",", att["lat"].value)
    else:
        print("No vehicles found.")
python vehicle_tracker.py
-118.350365 , 34.097992
Los sistemas de transporte público no funcionan todo el tiempo. Muchos cierran a las 10:00 p.m. hora local. Si encuentras un error, usa el sitio web de NextBus para localizar un sistema que esté funcionando.
MÓDULO_03

Mapeo de Ubicaciones NextBus

La mejor fuente de datos de mapeo de calles disponible gratuitamente es el proyecto OpenStreetMap (OSM). Usaremos la API de MapQuest para crear imágenes de mapas estáticos con marcadores de ubicación.

Pipeline de Generación de Mapas
NextBus API
→ lat/lon →
MapQuest API
PNG Image

Función nextbus()

nextbus_function.py
def nextbus(a, r, c="vehicleLocations", e=0):
    """Returns the most recent latitude and
    longitude of the selected bus line using
    the NextBus API (nbapi)
    Arguments: a=agency, r=route, c=command,
    e=epoch timestamp"""
    
    nbapi = "http://webservices.nextbus.com"
    nbapi += "/service/publicXMLFeed?"
    nbapi += "command={}&a={}&r={}&t={}".format(c, a, r, e)
    
    xml = minidom.parse(urllib.request.urlopen(nbapi))
    # If more than one vehicle, just get the first
    bus = xml.getElementsByTagName("vehicle")[0]
    
    if bus:
        at = bus.attributes
        return (at["lat"].value, at["lon"].value)
    else:
        return (False, False)

Función nextmap()

nextmap_function.py
def nextmap(a, r, mapimg):
    """Plots a nextbus location on a map image
    and saves it to disk using MapQuest API"""
    
    # Fetch the latest bus location
    lat, lon = nextbus(a, r)
    if not lat:
        return False
    
    # Build MapQuest Static Map API URL
    osmapi = "https://www.mapquestapi.com/staticmap/v4/getmap?"
    osmapi += "type=map&"
    # Red pushpin marker at bus location
    osmapi += "mcenter={},{}|&".format(lat, lon)
    # Zoom level (1-18, higher = lower scale)
    osmapi += "zoom=18&"
    # Center map around bus location
    osmapi += "center={},{}&".format(lat, lon)
    # Set map image size
    osmapi += "&size=1500,1000"
    # Add API Key
    osmapi += "&key=YOUR_API_KEY_HERE"
    # Create PNG image
    osmapi += "imagetype=png&"
    
    img = urllib.request.urlopen(osmapi)
    # Save the map image
    with open("{}png".format(mapimg), "wb") as f:
        f.write(img.read())
    
    return True

Loop de Rastreo

tracking_loop.py
import time

# Nextbus API variables
agency = "lametro"
route = "2"
nextimg = "nextmap"

# Number of updates
requests = 3
# Update frequency (seconds)
freq = 5

# Map the bus location every few seconds
for i in range(requests):
    success = nextmap(agency, route, nextimg)
    if not success:
        print("No data available.")
        continue
    print("Saved map {} at {}".format(i, time.asctime()))
    time.sleep(freq)
python tracking_loop.py
Saved map 0 at Sun Nov 1 22:35:17 2015
Saved map 1 at Sun Nov 1 22:35:24 2015
Saved map 2 at Sun Nov 1 22:35:32 2015
Este script guarda una imagen de mapa cada 5 segundos mostrando la ubicación actual del autobús. Necesitas una clave API gratuita de MapQuest Developer disponible en: https://developer.mapquest.com/
MÓDULO_04

Storm Chasing: Datos Meteorológicos

Ahora queremos ir más allá de lo que hace el mashup de NextBus. Agregaremos otra fuente de datos en tiempo real importante: el clima. El programa Mesonet de Iowa State University proporciona datos meteorológicos gratuitos y pulidos.

Arquitectura de Mapa Combinado
Mapa Final
Basemap OSM
Weather WMS
Bus Icon

Podemos usar el estándar Web Map Service (WMS) del OGC para solicitar una imagen sobre nuestra área de interés. Un WMS es un estándar para servir imágenes de mapas georreferenciados a través de la web.

URL de Ejemplo WMS Mesonet

http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=nexrad-n0r&STYLES=&SRS=EPSG:900913&BBOX=-15269659.42,2002143.61,-6103682.81,7618920.15&WIDTH=600&HEIGHT=600&FORMAT=image/png

Función de Conversión Lat/Lon a Metros

coordinate_conversion.py
import math

def ll2m(lon, lat):
    """Lat/lon to meters (Web Mercator)"""
    x = lon * 20037508.34 / 180.0
    y = math.log(math.tan((90.0 + lat) * 
        math.pi / 360.0)) / (math.pi / 180.0)
    y = y * 20037508.34 / 180
    return (x, y)

Función WMS Genérica

wms_function.py
def wms(minx, miny, maxx, maxy, service, 
        lyr, epsg, style, img, w, h):
    """Retrieve a WMS map image from
    the specified service and save as JPEG."""
    
    wms = service
    wms += "?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&"
    wms += "LAYERS={}".format(lyr)
    wms += "&STYLES={}&".format(style)
    wms += "SRS=EPSG:{}&".format(epsg)
    wms += "BBOX={},{},{},{}&".format(minx, miny, maxx, maxy)
    wms += "WIDTH={}&".format(w)
    wms += "HEIGHT={}&".format(h)
    wms += "FORMAT=image/jpeg"
    
    wmsmap = urllib.request.urlopen(wms)
    with open(img + ".jpg", "wb") as f:
        f.write(wmsmap.read())

Combinación de Mapas con PIL

blend_maps.py
from PIL import Image

# Transparency level (0=invisible, 1=opaque)
opacity = 0.5
icon_size = 30

# Open the basemap image in PIL
im1 = Image.open("basemap.png").convert('RGBA')

# Open the weather image in PIL
im2 = Image.open("weather.png").convert('RGBA')

# Convert weather image mode to match basemap
im2 = im2.convert(im1.mode)

# Create blended image
im3 = Image.blend(im1, im2, opacity)

# Open bus icon image
im4 = Image.open("busicon.png")
im4.thumbnail((icon_size, icon_size))

# Calculate center position
w, h = im3.size
w2, h2 = im4.size
center_width = int((w/2) - (w2/2))
center_height = int((h/2) - (h2/2))

# Paste icon in center of image
im3.paste(im4, (center_width, center_height), im4)

# Save the finished map
im3.save("bus-weather-map.png")
El mapa final muestra la precipitación en la ubicación actual del autobús. La rampa de colores va desde azul claro para precipitación ligera, luego verde, amarillo, naranja, hasta rojo para lluvia más intensa. Operadores de autobuses pueden usar esta imagen para indicar a los conductores que vayan más despacio.
MÓDULO_05

Reportes desde el Campo

Los smartphones, tablets y laptops modernos nos permiten actualizar un GIS y ver esas actualizaciones desde cualquier lugar. Usaremos HTML, GeoJSON, la librería JavaScript Leaflet, y una librería Python pura llamada Folium para crear una aplicación cliente-servidor.

{ }
GeoJSON
🗺️
Leaflet
🐍
Folium
📍
HTML5 Geo
Arquitectura Cliente-Servidor
Formulario HTML
→ POST →
myjson.com
→ GET →
Folium Map

Primero, necesitamos un formulario web que muestre tu ubicación actual y actualice el servidor cuando envíes el formulario con comentarios sobre tu ubicación.

Creación de Mapa con Folium

Folium proporciona una API Python simple para crear mapas web Leaflet. Este script son solo unas pocas líneas y producirá una página web llamada map.html:

field_report_map.py
import folium

# Create base map
m = folium.Map()

# Add GeoJSON layer from URL
m.geo_json(geo_path="https://api.myjson.com/bins/467pm")

# Save map to HTML file
m.create_map(path="map.html")

Pasamos la URL GeoJSON al objeto mapa, que trazará las ubicaciones en el mapa. El mapa interactivo resultante mostrará puntos como marcadores. Cuando hagas clic en un marcador, se mostrará la información del formulario.

Puedes usar esta página en dispositivos móviles, moverla a cualquier servidor web, o incluso usarla en tu disco duro local. El formulario publica a una URL pública en myjson.com donde puedes ver el GeoJSON sin procesar.

Instalación de Folium

terminal
pip install folium
Folium hace que producir un mapa Leaflet sea extremadamente simple. Puedes encontrar más información sobre Folium aquí: https://github.com/python-visualization/folium

Resumen del Capítulo

01 // Data Currency

La edad de los datos geoespaciales es crítica. Los datos en tiempo real revolucionan cómo hacemos análisis geoespacial.

02 // REST APIs

APIs como NextBus permiten acceder a feeds crudos de ubicación en tiempo real mediante llamadas URL simples.

03 // WMS Services

El estándar OGC Web Map Service permite adquirir subconjuntos de datos raster en tiempo real como imágenes meteorológicas.

04 // Data Blending

PIL permite combinar diferentes tipos de datos en tiempo real en productos de análisis de mapas personalizados.

05 // Client-Server GIS

Folium y Leaflet permiten construir aplicaciones cliente-servidor para actualizar un GIS en tiempo real.

06 // Internet of Things

El concepto IoT conecta dispositivos en una red de datos, desde GPS hasta sensores meteorológicos.

Basado en "Python Geospatial Analysis" // Capítulo 9: Real-Time Data