opengeos / leafmap

A Python package for interactive mapping and geospatial analysis with minimal coding in a Jupyter environment
https://leafmap.org
MIT License
3.21k stars 384 forks source link

MapLibre GL JS (Deck GL and 3D Buildings) Maps not work in Streamlit App. Show the only blank display and ground basemap. #801

Closed datafyresearcher closed 4 months ago

datafyresearcher commented 4 months ago

Environment Information

Description: Issue-1

This Leafmap code is not working in the Streamlit app, resulting in a blank screen.. URL: https://leafmap.org/maplibre/add_deckgl_layer/

import leafmap.maplibregl as leafmap
import streamlit as st
import requests

st.set_page_config(layout="wide")  # Optional: for a wider layout

data = requests.get("https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json").json()

m = leafmap.Map(
    style="positron",
    center=(-122.4, 37.74),
    zoom=12,
    pitch=40,
)
deck_grid_layer = {
    "@@type": "GridLayer",
    "id": "GridLayer",
    "data": data,
    "extruded": True,
    "getPosition": "@@=COORDINATES",
    "getColorWeight": "@@=SPACES",
    "getElevationWeight": "@@=SPACES",
    "elevationScale": 4,
    "cellSize": 200,
    "pickable": True,
}

m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}")
m.to_streamlit(height=800)

Description: Issue-2

This is a Leafmap code implementation in a Streamlit app, displaying ground map details such as basemap information. However, it does not provide a 3D building map in the Streamlit app, although it works in Jupyter Notebook. In this implementation, I used my MAPTILER_KEY. URL: https://leafmap.org/maplibre/3d_buildings/

MAPTILER_KEY = leafmap.get_api_key("MAPTILER_KEY")
style = f"https://api.maptiler.com/maps/basic-v2/style.json?key={MAPTILER_KEY}"

m = leafmap.Map(center=[-74.0066, 40.7135], zoom=16, pitch=45, bearing=-17, style=style)
source = {
    "url": f"https://api.maptiler.com/tiles/v3/tiles.json?key={MAPTILER_KEY}",
    "type": "vector",
}

layer = {
    "id": "3d-buildings",
    "source": "openmaptiles",
    "source-layer": "building",
    "type": "fill-extrusion",
    "min-zoom": 15,
    "paint": {
        "fill-extrusion-color": [
            "interpolate",
            ["linear"],
            ["get", "render_height"],
            0,
            "lightgray",
            200,
            "royalblue",
            400,
            "lightblue",
        ],
        "fill-extrusion-height": [
            "interpolate",
            ["linear"],
            ["zoom"],
            15,
            0,
            16,
            ["get", "render_height"],
        ],
        "fill-extrusion-base": [
            "case",
            [">=", ["get", "zoom"], 16],
            ["get", "render_min_height"],
            0,
        ],
    },
}
m.add_source("openmaptiles", source)
m.add_layer(layer)
m.to_streamlit(height=800)

What I Did

Please review this issue. Either resolve it in Leafmap or create a new feature in Streamlit to execute the relevant maps.

Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
giswqs commented 4 months ago

The 3D buildings app works fine on my end.

image

giswqs commented 4 months ago

I can confirm that the app with deckgl layers does not work. I guess there are some additional JS code in the deckgl app that prevent the HTML from rendering in streamlit.

The to_streamlit() function was contributed by @cboettig. He might have some insights. I don't know JS or HTML well enough to resolve the issue.

    def to_streamlit(
        self,
        width: Optional[int] = None,
        height: Optional[int] = 600,
        scrolling: Optional[bool] = False,
        **kwargs: Any,
    ) -> Any:
        try:
            import streamlit.components.v1 as components
            import base64

            raw_html = self.to_html().encode("utf-8")
            raw_html = base64.b64encode(raw_html).decode()
            return components.iframe(
                f"data:text/html;base64,{raw_html}",
                width=width,
                height=height,
                scrolling=scrolling,
                **kwargs,
            )

        except Exception as e:
            raise Exception(e)
datafyresearcher commented 4 months ago

@giswqs

"The 3D buildings app works fine on my end." can you share the code and other details?

How does a 3D building graph work? Because this code on my laptop doesn't work, so I want to know about it.

Could you give the related Packages names or other help codes, to display 3D Building in Streamlit?

I already used an updated version of Streamlit and Leafmap so, this method does not work.

giswqs commented 4 months ago

I use the same source as yours. Are you using VS Code? It might be because the environment variable is not passed to VS Code if you don't start VS Code from the command line.

Try replacing the MAPTILER_KEY with your real key without using the leafmap.get_api_key() funciton.

MAPTILER_KEY = "YOUR-API-KEY"

image

crazycapivara commented 4 months ago

@datafyresearcher Consider using Shiny for Python as shown in this example and what MapLibre for Python initially mainly was made for. Shiny Express is very close to Streamlit but in my opinion more powerful, Streamlit always re-rendes the wiget on updates.

But I will also check the Streamlit support, because I already tested this a while ago succesfully.

datafyresearcher commented 4 months ago

@giswqs Thank you for your help, Description: Issue-2 has been solved by my side.

But in Description: Issue-1 have now the same issue. If possible resolve this issue.

m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}")

Here, this code can't work to display in Streamlit.

giswqs commented 4 months ago

@datafyresearcher I guess the deckgl layer issue is related to the upstream package py-maplibregl, or it maybe a streamlit bug. You might want to open an issue here so that @crazycapivara can help resolve it.

crazycapivara commented 4 months ago

@datafyresearcher Can you please check if this pyMapLibre streamlit example is working for you.

@giswqs I am not sure why it is not working in leafmap, the code seems to be correct, I use more or less the same code for the maplibre streamlit component. Does the example above work for you?

giswqs commented 4 months ago

@crazycapivara Thank you for the pyMapLibre streamlit example. I compared the HTML file generated from pyMapLibre and leafmap and identified the bug caused by the leamfap to_html() method. It has been fixed in #804.

The deckGL example now works.

import leafmap.maplibregl as leafmap
import streamlit as st

st.set_page_config(layout="wide")
st.title("SF Bike Parking")
cell_size = st.slider("cell size", 100, 600, value=200, step=5)

data = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json"

m = leafmap.Map(
    style="positron",
    center=(-122.4, 37.74),
    zoom=12,
    pitch=40,
)
deck_grid_layer = {
    "@@type": "GridLayer",
    "id": "GridLayer",
    "data": data,
    "extruded": True,
    "getPosition": "@@=COORDINATES",
    "getColorWeight": "@@=SPACES",
    "getElevationWeight": "@@=SPACES",
    "elevationScale": 4,
    "cellSize": cell_size,
    "pickable": True,
}

m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}")
m.to_streamlit(height=800)

image