更改地图图层时,使用 Folium 在 Shiny for Python 中保留缩放和地图中心

问题描述 投票:0回答:1

我正在开发一个使用 Shiny for Python 和 Folium 来可视化天气预报的项目。目标是允许用户与滑块交互来选择预报时间,并在地图上显示相应的天气数据。

我遇到的问题: 每次用户使用滑块更改预测时间时,地图都会重置为初始缩放级别和中心位置。理想情况下,我希望地图保留当前的缩放和中心,即使在切换显示图层(即预测时间)时也是如此。

我尝试在应用程序中实现自定义 JavaScript 以捕获缩放和居中更改并在滑块更新后重新应用它们,但它似乎没有按预期工作。缩放和中心始终重置为首次创建地图时定义的初始值。检查 Web 应用程序的源代码后,我的 JavaScript 似乎无法与应用程序的其余部分正确集成。

我的设置: Shiny for Python 用于 Web 应用程序结构。 Folium 用于渲染地图并将天气数据添加为图层。 用于选择不同预测时间步长的滑块。 更改滑块时尝试使用 JavaScript 捕获并恢复地图的视图设置(缩放级别和中心)。

问题: 使用我当前的堆栈更改预测时间时,是否可以在更新中保留缩放级别和地图中心(可能类似于在闪亮中,如何修复(锁定)传单地图视图缩放和中心?,仅在Python中而不是在Python中)在 R 中)?如果是这样,我怎样才能正确地包含 JavaScript 来实现这一目标?

这是仍然存在问题的最短版本:

from shiny import App, render, ui
import folium

# Define the Shiny UI
app_ui = ui.page_fluid(
    ui.h2("Dynamic Folium Map with Layer Switching"),
    ui.input_slider("layer_slider", "Choose Layer (1 = Marker, 2 = Circle)", min=1, max=2, value=1),
    ui.output_text_verbatim("slider_value"),
    ui.output_ui("map")  # Output container for the map
)

# Define the Shiny server
def server(input, output, session):
    
    # Display the slider value
    @output
    @render.text
    def slider_value():
        return f"Layer selected: {input.layer_slider()}"
    
    # Generate and display the map dynamically
    @output
    @render.ui
    def map():
        
        # Create a base map centered on Germany
        m = folium.Map(location=[51.1657, 10.4515], zoom_start=6)
        
        # Add layers depending on the slider value
        if input.layer_slider() == 1:
            folium.Marker([51.1657, 10.4515], popup="Marker in Germany").add_to(m)
        elif input.layer_slider() == 2:
            folium.Circle([51.1657, 10.4515], radius=50000, color='blue', popup="Circle Layer").add_to(m)

        # Return the map as an HTML string
        map_html = m._repr_html_()
        
        # Add custom JavaScript to preserve zoom level and map center
        custom_js = """
        <script>
        let mapZoom = 6;
        let mapCenter = [51.1657, 10.4515];

        // Function to capture map's zoom and center
        function captureMapState() {
            mapZoom = map.getZoom();
            mapCenter = map.getCenter();
        }

        // Function to restore map's zoom and center
        function restoreMapState() {
            map.setView(mapCenter, mapZoom);
        }

        // Capture state after zoom or move events
        map.on('zoomend', captureMapState);
        map.on('moveend', captureMapState);

        // Restore the view after the map is updated
        document.addEventListener('DOMContentLoaded', function() {
            restoreMapState();
        });
        </script>
        """
        
        # Embed the map and attach the custom JavaScript
        return ui.HTML(f"""
        <div style="width:100%; height:500px;">
            {map_html}
        </div>
        {custom_js}
        """)

# Create the Shiny app
app = App(app_ui, server)

# Run the app
app.run(port=8000)

javascript python leaflet folium py-shiny
1个回答
0
投票

我建议使用

ipyleaflet
而不是
folium
,因为它是
ipywidget
,因此支持创建后更新地图对象的方法。

这是 Shinylive 中的可编辑代码

from ipyleaflet import Map, Marker, Circle
from shiny.express import ui, input
from shinywidgets import render_widget
from shiny import reactive, render


def remove_all_layers(m):
    for i in range(1, len(m.layers)):
        m.remove_layer(m.layers[1])
        
ui.h2("An ipyleaflet Map")

ui.input_slider('layer_slider', 'Layer Slider', 0, 3, 0, step=1)

@render_widget  # <<
def map():
    center = (51.1657, 10.4515)
    m = Map(center=center, zoom=6)
    # marker = Marker(location=center)
    # m.add(marker)
    return m

@reactive.effect
def add_layer():
    m = map.widget
    remove_all_layers(m)
    if input.layer_slider() == 1:
        marker = Marker(
            location=[51.1657, 10.4515],
            title="Marker in Germany"
        )
        m.add(marker)
    elif input.layer_slider() == 2:
      circle = Circle(
          location=[51.1657, 10.4515], 
          radius=50000, 
          color='blue', 
          title="Circle Layer"
      )
      m.add(circle)
© www.soinside.com 2019 - 2024. All rights reserved.