es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Integración de Datashader para polígonos en el mapbox de Plotly.

Estoy utilizando Scattermapbox de plotly para superponer un mapa con una imagen sombreada de polígonos creada por la función shade de datashader (basada en https://plotly.com/python/datashader/), pero las proyecciones no parecen coincidir, vea la imagen a continuación. ¿Algún consejo sobre cómo puedo solucionar este problema utilizando Scattermapbox y datashader de plotly?

Ejemplo reproducible:

import geopandas as gpd
import plotly.graph_objects as go
import spatialpandas as spd
import datashader as ds
from colorcet import fire
import datashader.transfer_functions as tf

# cargar datos
world = gpd.read_file(
    gpd.datasets.get_path('naturalearth_lowres')
)

# convertir proyección
world = world.to_crs(epsg=3857)

# crear DataFrame de spatialpandas
df_world = spd.GeoDataFrame(world)

# crear canvas de datashader y agregar
cvs = ds.Canvas(plot_width=1000, plot_height=1000)
agg = cvs.polygons(df_world, geometry='geometry', agg=ds.mean('pop_est'))

# crear imagen sombreada
tf.shade(agg, cmap=fire)

Imagen sombreada

# crear imagen sombreada y convertirla en una imagen de Python
img = tf.shade(agg, cmap=fire)[::-1].to_pil()

coords_lat, coords_lon = agg.coords['y'].values, agg.coords['x'].values

# Esquinas de la imagen, que deben pasarse a mapbox
coordinates = [
    [coords_lon[0], coords_lat[0]],
    [coords_lon[-1], coords_lat[0]],
    [coords_lon[-1], coords_lat[-1]],
    [coords_lon[0], coords_lat[-1]],
]

fig = go.Figure(go.Scattermapbox())
fig.update_layout(
    mapbox_style="open-street-map",
    mapbox_layers=[
        {
            "sourcetype": "image",
            "source": img,
            "coordinates": coordinates,
        }
    ]
)
fig.show()

Sobreponer mapa

Leí que Scattermapbox solo admite la proyección de Mercator, lo cual me resultó confuso ya que los ejemplos en la documentación de plotly parecen estar en formato de longitud/latitud, pero intenté convertir las coordenadas del GeoDataFrame a epsg 3857, vea:

world = world.to_crs(epsg=3857)

El resultado es que la imagen sombreada se vuelve invisible. Cualquier ayuda sería muy apreciada.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Es probable que las proyecciones de datashader y Scattermapbox no estén coincidiendo, de ahí el desalineamiento de la imagen sombreada y el mapa.

    Una forma de solucionar este problema es reproyectar el GeoDataFrame a la proyección Mercator antes de crear el lienzo de datashader, en lugar de convertir las coordenadas a EPSG 3857. De esta manera, las coordenadas en el lienzo de datashader estarán en la misma proyección que la gráfica Scattermapbox, y la imagen sombreada debería alinearse perfectamente con el mapa.

    Para hacer esto, puedes utilizar el método to_crs() del GeoDataFrame para reproyectar a la proyección Mercator, de la siguiente manera:

    import geopandas as gpd
    import plotly.graph_objects as go
    import spatialpandas as spd
    import datashader as ds
    from colorcet import fire
    import datashader.transfer_functions as tf
    
    
    # cargar los datos y reproyectar a Mercator
    world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
    world = world.to_crs(epsg=3857)
    
    # crear el DataFrame de spatialpandas
    df_world = spd.GeoDataFrame(world)
    
    # crear el lienzo de datashader y agregar
    cvs = ds.Canvas(plot_width=1000, plot_height=1000)
    agg = cvs.polygons(df_world, geometry='geometry', agg=ds.mean('pop_est'))
    
    # crear la imagen sombreada y convertirla a imagen Python
    img = tf.shade(agg, cmap=fire)[::-1].to_pil()
    
    # obtener las coordenadas en la proyección Mercator
    coords_lat, coords_lon = agg.coords["y"].values, agg.coords["x"].values
    
    # calcular las esquinas de la imagen en la proyección Mercator
    nw, se = (coords_lon[0], coords_lat[-1]), (coords_lon[-1], coords_lat[0])
    coordinates = [nw, (se[0], nw[1]), se, (nw[0], se[1])]
    
    # plot del mapa superpuesto
    fig = go.Figure(go.Scattermapbox())
    fig.update_layout(
        mapbox_style="open-street-map",
        mapbox_layers=[
            {"sourcetype": "image", "source": img, "coordinates": coordinates}
        ]
    )
    fig.show()
    

    Con este código, la imagen sombreada debería alinearse perfectamente con el mapa.

Comments are closed.