Manual/Referencia

Paquete Python (Jupyter)

Meratyn distribuye un paquete Python, meratyn, que incrusta la app completa de Meratyn dentro de una celda de Jupyter notebook como un anywidget, con una API al estilo leafmap.

El widget carga la app completa de Meratyn (menús, paneles, herramientas de procesamiento) en un iframe. El estado se sincroniza en ambos sentidos a través de un único proyecto .meratyn.json, de modo que los datos que añades desde Python aparecen en la interfaz, y las ediciones que haces en la interfaz (paneo, zoom, añadido de capas) se pueden leer de vuelta desde Python.

Instalación

pip install -e ./python

Actualmente el paquete se instala desde este checkout; este repositorio no lo publica en PyPI ni en conda.

Extras opcionales para add_geojson() / add_gdf() desde un GeoDataFrame y para leer archivos vectoriales locales con add_vector() / add_geoparquet() / add_flatgeobuf() / add_shp() / add_kml() / add_gpkg() (las URLs remotas de esos formatos no necesitan extras):

pip install "meratyn[all]"   # añade GeoPandas y Shapely

El extra opcional [all] es solo de pip. Si instalaste vía conda, añádelo con pip install "meratyn[all]" dentro del mismo entorno.

Capas de Earth Engine

Instala por separado el SDK de Earth Engine y autentica el kernel de Python:

pip install earthengine-api
import ee
from meratyn import Map

ee.Authenticate()
ee.Initialize(project="your-cloud-project")
m = Map()
m.add_ee_layer(
    ee.Image("COPERNICUS/S2_SR_HARMONIZED/20240101T000000_20240106T000000"),
    {"bands": ["B4", "B3", "B2"], "min": 0, "max": 3000},
    name="Sentinel-2",
)

add_ee_layer() acepta un ee.Image, ee.ImageCollection (se compone antes de renderizar), ee.FeatureCollection, ee.Feature o ee.Geometry. Los objetos vectoriales se estilizan con las claves admitidas por el estilo de la colección; la visualización de imágenes acepta bands, min, max, palette y parámetros relacionados de Earth Engine. Earth Engine evalúa el objeto en el kernel y solo entrega al navegador una capa de teselas XYZ normal.

La autenticación y la cuota siguen siendo responsabilidad de Earth Engine. Si falta el SDK, el cliente no está inicializado o se supera la cuota del proyecto de Cloud, Meratyn devuelve un error privado sin conservar cuerpos de petición, credenciales ni URL de servicio sin procesar. Los identificadores de mapa y las URL de teselas de Earth Engine caducan; regenere la capa cuando las teselas de un proyecto reabierto ya no sean válidas. El widget vivo y co-localizado conserva el token de teselas de runtime solo para renderizar; el archivo de proyecto guarda únicamente la referencia de la capa. to_project() y save_project() siempre redactan los tokens de URL de teselas de Earth Engine, incluso con keep_credentials=True; esa opción solo puede conservar credenciales no relacionadas en un artefacto local de confianza. to_html() y las incrustaciones externas siempre se redactan, y add_ee_layer() nunca persiste credenciales.

Inicio rápido

from meratyn import Map

m = Map(center=(-100, 40), zoom=4)
m.add_geojson("https://example.com/data.geojson", name="Data")
m

La interfaz completa de Meratyn se renderiza en la celda. Añade más datos y controla la vista:

m.add_tile_layer(
    "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    name="OpenStreetMap",
    attribution="(c) OpenStreetMap contributors",
)
m.add_cog("https://example.com/dem.tif", name="DEM", colormap="terrain")
m.add_basemap("dark")
m.set_center(-120, 47, zoom=8)

Añade tablas de puntos y simbología opcional dirigida por datos:

m.add_csv("cities.csv", x="longitude", y="latitude", name="Cities")
m.add_heatmap([(-75.5636, 6.2442), (-75.5, 6.3)], radius=35)
m.add_gdf(cities, name="Cities", column="population", colormap="blues")

add_raster / add_cog también aceptan una ruta de GeoTIFF local en la máquina del kernel: el archivo lo sirve el servidor localhost incluido para que la app pueda leerlo. Esto solo funciona donde el navegador puede alcanzar el localhost del kernel (Jupyter local, VS Code); en configuraciones remotas o con navegador separado (Colab, JupyterHub, servidores remotos) la ruta localhost es inalcanzable, así que allí pasa una URL alojada. La URL servida también tiene alcance de sesión, por lo que un proyecto guardado con un ráster local no lo restaurará al reabrirlo más tarde — pasa una URL alojada para proyectos duraderos.

Añade marcadores y simbología dirigida por datos sin precalcular estilos:

m.add_marker(-76.522, 3.4372, properties={"name": "Cali"})
m.add_marker_cluster([(-75.5144, 10.391), (-74.7813, 10.9685), (-74.1982, 11.2408)])
m.add_choropleth(
    "https://example.com/counties.geojson",
    column="population",
    colormap="blues",
    scheme="quantile",
)

Añade una leyenda, una barra de color y una comparación swipe (mapa dividido):

# Una leyenda integrada de cobertura del suelo, o tu propio dict {etiqueta: color}.
m.add_legend(builtin="nlcd")
m.add_legend(legend_dict={"Water": "#0000ff", "Land": "#00ff00"})

# Una barra de color para un ráster continuo.
m.add_colorbar(colormap="terrain", vmin=0, vmax=4000, label="Elevation", units="m")

# Compara dos capas (o una capa contra el mapa base) con un deslizador swipe.
before = m.add_cog("https://example.com/before.tif", name="Before")
after = m.add_cog("https://example.com/after.tif", name="After")
m.split_map(before, after)

Sincronización bidireccional

Como el proyecto se sincroniza en ambos sentidos, puedes hacer pan o zoom en la interfaz y luego leer el estado vivo desde Python:

proj = m.to_project()
proj["mapView"]["center"]              # refleja la vista viva de la interfaz
[layer["name"] for layer in proj["layers"]]

to_project(), save_project() y to_html() redactan credenciales: los tokens de URL de servicios, cabeceras, variables de entorno, claves de geocodificadores y ajustes opacos de plugins se eliminan de sus copias del proyecto desacopladas/exportadas. to_project() y save_project() aceptan keep_credentials=True de solo-keyword al escribir un artefacto local de confianza. Esa opción solo conserva credenciales no relacionadas; los tokens de URL de teselas de Earth Engine siguen redactados. El HTML independiente y las incrustaciones externas siempre permanecen redactados; el anywidget co-localizado vivo conserva la fidelidad completa del proyecto.

Guarda y recarga proyectos, totalmente intercambiables con las apps de escritorio y web:

m.save_project("my-map.meratyn.json")

m2 = Map()
m2.load_project("my-map.meratyn.json")
m2

Integración con Dash

El componente DashMap de meratyn-dash incrusta la aplicación Meratyn en un layout de Plotly Dash y conserva el mismo ciclo de vida de proyecto e iframe que el anywidget. La integración es opcional, por lo que las importaciones normales de Jupyter no requieren Dash. Admite meratyn>=2.8.0,<3.0 y se instala por separado:

pip install -e ./python -e ./python/dash
from dash import Dash, Input, Output, html
from meratyn_dash import DashMap

app = Dash(__name__)
m = DashMap(center=(-119.0, 39.8), zoom=5, basemap="dark", height="800px")
app.layout = html.Div([m, html.Pre(id="click-output")])


@app.callback(Output("click-output", "children"), Input(m, "clickData"))
def show_click(click_data):
    return "Haz clic en el mapa" if click_data is None else str(click_data)

clickData contiene lngLat como un objeto {lng, lat} y una lista de entidades GeoJSON identificadas. selectionData contiene la capa y los IDs de las entidades seleccionadas. viewData queda reservado para un futuro evento de cámara. Las API existentes Map.on_click() y Map.on_selection_change() no cambian. Cada DashMap posee su propio ciclo de vida de iframe y listener; los mapas sin id reciben IDs generados independientes para que varios mapas compartan un layout Dash.

Opciones del mapa

Map(
    center=(-100, 40),   # [lng, lat]
    zoom=4,
    basemap="dark",      # un nombre de mapa base o una URL de estilo MapLibre
    height="800px",
    layout="embed",      # "embed" (interfaz compacta), "full" (interfaz de escritorio) o "maponly"
    theme="light",       # "light" o "dark"
)

Scripting interactivo

Más allá de añadir datos, el widget puede consultar la app viva y reaccionar a ella — la misma superficie que la Consola Python de la app. Estas llamadas hacen un viaje de ida y vuelta al mapa en ejecución, así que el mapa debe mostrarse primero (muestra m en una celda) y luego ejecutar las consultas en una celda posterior.

m.get_center()                 # [lng, lat] vivo, reflejando paneos/zooms de la interfaz
m.get_bounds()                 # [oeste, sur, este, norte]
m.fly_to(-75.6961, 4.8143, zoom=10)
m.identify(-75.6961, 4.8143)       # features en un punto, como hacer clic en el mapa

# Objetos de capa: lee y muta capas, lee sus features
for layer in m.layers:
    layer.opacity = 0.6
features = m.layers[0].get_features()   # lista de objetos GeoJSON Feature

# Ejecuta un algoritmo de procesamiento; las capas resultado se añaden al mapa
m.list_algorithms()
m.run_algorithm("buffer", {"layer": layer_id, "distance": 1000})

# Lee lo que el usuario seleccionó o dibujó en el mapa
m.get_selected_features()      # las feature(s) clicadas como objetos Feature
m.get_drawn_features()         # features dibujadas con el Geo Editor
m.user_rois                    # las ROIs dibujadas como GeoJSON FeatureCollection
m.get_drawn_features(as_gdf=True)   # lo mismo, como GeoDataFrame (requiere GeoPandas)

png = m.to_image()             # bytes PNG (o m.to_image("map.png"))
m.to_html("map.html")          # HTML independiente que incrusta el proyecto vivo

Reacciona a la interacción del usuario con callbacks de eventos:

m.on_click(lambda e: print("clicked", e["lngLat"]))
m.on_selection_change(lambda e: print("selected", e))
m.on_layer_change(lambda e: print("layers", e["layerIds"]))

Consultas bloqueantes Las consultas interactivas bloquean el kernel hasta que la app responde (vía jupyter_ui_poll, instalado automáticamente). Pasa timeout= para llamadas lentas, p. ej. m.run_algorithm(..., timeout=300).

Referencia de la API

Consultas, eventos y procesamiento interactivos

MétodoDescripción
get_view() / get_center() / get_bounds()Lee la cámara / centro / límites del viewport vivos.
fly_to(lng, lat, zoom=, bearing=, pitch=, duration=)Anima la cámara.
fit_bounds([w, s, e, n])Ajusta la cámara a un bounding box.
zoom_to_bounds(bounds) / zoom_to_layer(layer)Ajusta la vista a límites o a una capa por id, nombre o manejador Layer.
identify(lng, lat, layer_id=None)Consulta las features renderizadas en un punto.
get_features(layer_id)Las features de una capa como objetos Feature.
get_selected_features(as_gdf=False)La(s) feature(s) seleccionada(s) en la app, como objetos Feature (o un GeoDataFrame).
get_drawn_features(as_gdf=False) / user_roisFeatures dibujadas con el Geo Editor; user_rois las devuelve como FeatureCollection.
layers / get_layer(id)Manejadores Layer (leen estado; ajustan name/visible/opacity, set_style, get_features, zoom_to, remove).
list_algorithms()Algoritmos de procesamiento disponibles (id, parameters, …).
run_algorithm(id, parameters=None, timeout=)Ejecuta un algoritmo; devuelve {logs, resultLayerIds}.
to_image(path=None, timeout=)Captura el mapa como bytes PNG, o escribe en path.
to_html(path=None, title=, width=, height=, app_url=)Exporta una página HTML independiente con un proyecto redactado; devuelve el HTML o escribe en path.
on(event, cb) / on_click / on_selection_change / on_layer_changeRegistra callbacks de eventos; devuelve una función de cancelación.
request(method, params=None, timeout=)Primitiva de comando de bajo nivel detrás de los métodos anteriores.

Datos, vista y proyectos

MétodoDescripción
Map(center, zoom, basemap=, height=, layout=, theme=)Crea un mapa.
add_geojson(data, name=, **style)Añade GeoJSON desde un dict, ruta de archivo, URL, cadena JSON o GeoDataFrame.
add_gdf(gdf, name=, column=None, **style)Añade un GeoDataFrame, opcionalmente como coropletas graduadas.
add_csv / add_xy_data (data, x=, y=, name=, **style)Añade puntos WGS84 desde CSV, DataFrame o mapeos de filas; los campos restantes son propiedades.
add_marker(lng, lat, name=, properties=, **style)Añade un marcador de punto único (mostrado como círculo; properties aparecen al hacer clic).
add_markers(points, name=, **style)Añade marcadores de punto desde pares (lng, lat), dicts {lng/lon/x, lat/y, …}, GeoJSON o un GeoDataFrame.
add_circle_markers(points, name=, radius=, **style)Añade marcadores circulares con radius explícito.
add_marker_cluster(points, name=, cluster_radius=, cluster_max_zoom=, **style)Añade marcadores de punto agrupados.
add_heatmap(points, name=, radius=, intensity=, **style)Añade datos de puntos con el renderizador de mapa de calor por densidad.
add_choropleth(data, column, name=, class_count=, colormap=, scheme=, **style)Añade una capa GeoJSON con simbología graduada calculada de una column numérica.
add_data(data, column=None, name=, **kwargs)Añade datos; un coropletas cuando se da column, si no una capa GeoJSON simple (paridad con leafmap).
add_vector(data, name=, render_mode=, data_format=, source_layer=, **style)Añade un dataset vectorial desde una URL (GeoParquet, FlatGeobuf, Shapefile zipeado, KML/KMZ, GeoPackage, GeoJSON, …) o un archivo local (leído vía GeoPandas e incluido).
add_geoparquet(data, name=, **style)Añade un dataset GeoParquet (URL o archivo local).
add_flatgeobuf(data, name=, **style)Añade un dataset FlatGeobuf (URL o archivo local).
add_shp(data, name=, **style)Añade un Shapefile (URL zipeada o .shp local).
add_kml(data, name=, **style) / add_gpkg(data, name=, layer=, **style)Añade datos KML/KMZ o GeoPackage; layer selecciona una tabla local de GeoPackage.
add_vector_tiles(url, name=, source_layers=, source_layer=, **style)Añade una capa de teselas vectoriales desde un endpoint TileJSON.
add_pmtiles(url, name=, tile_type=, source_layers=, **style)Añade un archivo PMTiles (vectorial o ráster).
add_tile_layer(url, name=, tile_size=, attribution=)Añade una capa de teselas XYZ ráster.
add_wms(endpoint, layers, name=, styles=, image_format=, transparent=, tile_size=, **style)Añade una capa WMS (GetMap, ráster en teselas).
add_wmts(url, name=, tile_size=, **style)Añade una capa WMTS desde una plantilla de URL de teselas.
add_wfs(endpoint, type_name, name=, version=, output_format=, srs_name=, max_features=, **style)Añade una capa WFS (GetFeature GeoJSON, descargada e incluida).
add_cog(url, name=, bands=, colormap=, rescale=, **style)Añade un Cloud Optimized GeoTIFF (URL o ruta local de GeoTIFF en el kernel).
add_raster(url, name=, bands=, colormap=, rescale=, **style)Añade un ráster (COG/GeoTIFF), URL o ruta local; alias de add_cog.
add_ee_layer(ee_object, vis_params=None, name=, shown=, opacity=)Añade una imagen, colección, elemento o geometría autenticados de Earth Engine como capa XYZ; las URL caducan y no se guardan con credenciales.
add_3d_tiles(url, name=, altitude_offset=, request_headers=, **style)Añade un tileset.json de 3D Tiles.
add_video(urls, coordinates, name=, **style)Añade un vídeo georreferenciado (cuatro esquinas [lng, lat]).
add_basemap(basemap)Fija el mapa base de fondo.
split_map(left_layers=None, right_layers=None, orientation=, position=, control_position=)Añade un deslizador de comparación swipe (mapa dividido) entre dos conjuntos de capas.
add_legend(title=None, legend_dict=, labels=, colors=, builtin=, position=, shape=)Añade una leyenda desde un dict {etiqueta: color}, labels/colors paralelos o un preset builtin ("nlcd", "esa_worldcover").
add_colorbar(colormap=, vmin=, vmax=, label=, units=, colors=, orientation=, position=)Añade una barra de color para un ráster continuo, desde un mapa de color con nombre o colors personalizados.
add_colormap(colormap, vmin=, vmax=, label=, **kwargs)Añade una barra de color desde un mapa de color con nombre (alias de add_colorbar al estilo leafmap).
set_center(lng, lat, zoom=None)Centra (y opcionalmente hace zoom) el mapa.
set_center_zoom(lng, lat, zoom=None)Alias de set_center (compatibilidad con leafmap).
layer_names / find_layer(name) / find_layer_index(name)Inspecciona las capas por nombre de visualización.
set_layer_visibility(layer, visible) / set_layer_opacity(layer, opacity)Cambia una capa por id, nombre o manejador Layer; la opacidad está limitada a [0, 1].
remove_layer(layer_id) / clear_layers()Elimina capas.
to_project(keep_credentials=False)Devuelve por defecto un dict de proyecto desacoplado y redactado.
load_project(src)Reemplaza el proyecto desde un dict, cadena JSON o ruta .meratyn.json.
save_project(path, keep_credentials=False)Escribe un proyecto redactado por defecto.

Los argumentos por palabra clave de estilo (por ejemplo fillColor, strokeColor, strokeWidth, circleRadius) se corresponden con los campos de estilo de capa de Meratyn.

Cómo funciona

La rueda incluye el build web de Meratyn. Al importarse, el paquete arranca un pequeño servidor estático en localhost que sirve la app incluida; el widget renderiza esa app en un iframe y intercambia el proyecto por window.postMessage. Añadir datos desde Python reescribe el proyecto sincronizado y lo empuja a la app; las ediciones de la interfaz fluyen de vuelta por la misma vía.

Soporte de entornos El widget interactivo funciona en Jupyter local, VS Code, Google Colab y JupyterHub / servidores remotos:

  • Jupyter local / VS Code - la app se sirve directamente desde localhost.
  • Google Colab - enruta por el proxy de puertos integrado de Colab (google.colab.kernel.proxyPort) automáticamente.
  • JupyterHub (incluyendo hubs administrados/compartidos, detectado en tiempo de ejecución vía JUPYTERHUB_SERVICE_PREFIX) - el front-end sondea dos rutas de mismo origen y usa la que esté viva, así que un host necesita solo una de ellas:
    • la extensión de Jupyter Server incluida con meratyn, montada en {base_url}mappview/app/ en el origen del propio servidor de notebooks. Se habilita automáticamente con pip install meratyn y no necesita jupyter-server-proxy ni puertos extra, así que funciona en hubs restringidos que bloquean el proxy de puertos crudos — pero solo se registra tras reiniciar el servidor de Jupyter, pues carga desde un drop-in de configuración de arranque.
    • jupyter-server-proxy en {base_url}proxy/{port}/, que alcanza el bundle localhost del kernel en el servidor en ejecución sin reinicio, dondequiera que jupyter-server-proxy esté instalado.
  • Otros servidores remotos (Binder, JupyterLab remoto por SSH/red) - pasa Map(server_proxy=True) para usar esa misma ruta remota de doble ruta.

Establece Map(server_proxy=False) para forzar la ruta directa por localhost. Si la app no carga en un hub, instala jupyter-server-proxy, o confirma que la extensión está habilitada con jupyter server extension list (busca meratyn; ejecuta jupyter server extension enable meratyn si falta) y reinicia el servidor de Jupyter para que la extensión cargue.

Descarga de URLs add_csv(url) / add_xy_data(url) y add_wfs() descargan datos desde el kernel, revalidan cada redirección y limitan la respuesta a 50 MB. Se rechazan hosts que resuelvan a direcciones privadas, de loopback o link-local (incluidos endpoints de metadatos de nube). add_geojson(url) y las capas de teselas y servicios se descargan desde el navegador. Los archivos GeoJSON locales se sirven mediante el endpoint de archivos de la sesión. El GeoJSON en memoria conserva un límite de 50 MiB; las colecciones de más de 4 MiB utilizan una referencia a un archivo temporal para evitar copiarlas por el canal de mensajes del cuaderno. No cargues proyectos .meratyn.json ni URLs no confiables en un kernel compartido/multi-inquilino.

Construir desde la fuente

El paquete vive en python/. La app incluida se produce desde el monorepo con:

bun run build:embed      # construye la app y la deja lista para la rueda
python -m build          # construye la rueda
python -m twine upload dist/*  # sube a PyPI
pip install -e python    # instalación editable para desarrollo

Los cambios en el código Python se recogen al reiniciar el kernel. Los cambios en la app (TypeScript) requieren re-ejecutar bun run build:embed y reiniciar el kernel.