pythonfoliumpy-shiny

Folium map refuses to fill the height of its container


I am trying to display a folium map in a card in a shiny for Python app using the code below (Reproducible example):

from shiny import App, ui, render, reactive
import folium

app_ui = ui.page_fluid(
    ui.tags.style(
        """
        /* apply css to control height of some UI widgets */
        .map-container {
            height: 700px !important;
            width: 100%;
            overflow: hidden;
        }
        .map-container > * {
            height: 100% !important;
        }
        """
    ),
    ui.column(6,
        ui.navset_card_tab(
            ui.nav_panel("Map",
                ui.div(
                    ui.output_ui("map"),
                    class_="map-container"
                )
            ),
            ui.nav_panel("Data",
                ui.p(
                    ui.output_table("data_table")
                )
            )
        )
    )
)

def server(input, output, session):
    @output
    @render.ui
    def map():
        m = folium.Map(location=[51.509865, -0.118092], zoom_start=12)
        return ui.HTML(m._repr_html_())

    @output
    @render.table
    def data_table():
        # Your data table rendering logic here
        pass

app = App(app_ui, server)

I want the element containing the map to fill the height of my screen but my the map itself is refusing to fill the whole height of the card. It is only occupying the upper half of the card area.

In Shiny for R, I had achieved the same result using the as_fill_carrier() function from the bslib library like this:

  card(
    style = "height: 80vh;",
    full_screen = TRUE,
    #card_header("Map"),
    card_body(
      tmapOutput("map") %>%
        withSpinner(type = 6, color = "#30804e") %>% as_fill_carrier() 
      )
    )
  )

What can I do?


Solution

  • This is an issue with folium, where m._repr_html_() causes the map to be embedded within a div which has padding-bottom: 60%; by default. This has to be overwritten if you want to use the whole space. Below is an example where this is done by just replacing the css with more suitable one, in particular, I remove the padding-bottom: 60%; and apply height: 100%; which yields the desired filling layout.

    from shiny import App, ui, render
    import folium
    
    app_ui = ui.page_fluid(
        ui.tags.style(
            """
            /* apply css to control height of some UI widgets */
            .map-container {
                height: 700px !important;
                width: 100%;
                overflow: hidden;
            }
            .map-container > * {
                height: 100% !important;
            }
            """
        ),
        ui.column(6,
            ui.navset_card_tab(
                ui.nav_panel("Map",
                    ui.div(
                        ui.output_ui("map"),
                        class_="map-container"
                    )
                ),
                ui.nav_panel("Data",
                    ui.p(
                        ui.output_table("data_table")
                    )
                )
            )
        )
    )
    
    def server(input, output, session):
        @output
        @render.ui
        def map():
            m = folium.Map(location=[51.509865, -0.118092], zoom_start=12)
            m = m._repr_html_()
            m = m.replace(
                '<div style="width:100%;"><div style="position:relative;width:100%;height:0;padding-bottom:60%;">', 
                '<div style="width:100%; height:100%;"><div style="position:relative;width:100%;height:100%;>', 
                1)
            return ui.HTML(m)
    
        @output
        @render.table
        def data_table():
            # Your data table rendering logic here
            pass
    
    app = App(app_ui, server)
    

    enter image description here