Stewie
Stewie

Reputation: 33

Loop over geoJson features and add plotly graph in popup html string to each

I've got a geoJson file with a bunch of features which I'm trying to display on an interactive folium map and I'm trying to add a plotly graph that pops up when you click on a polygon. At this moment I already have a folder with all plotly graphs for each city, 'currentWorkingDirectory/graphs/CityName.HTML'. I also have the interactive folium map with the different polygons which I can hover over or click for a popup.Current popup for poly

Now I'm having trouble with adding the plotly graphs as a html string to the geojson popups. Could someone help me with this? I'll add a code snippet of the folium map and what I've tried:

import folium
import geopandas as gpd
import codecs

map = folium.Map(location=['51.096246199999996', '4.178629103169916'], tiles="cartodbpositron", zoom_start=9)
geojson_file_df = gpd.read_file('Refgem_geojson.json')

loc = 'Project GEO ICT'
title_html = '''
             <h3 align="center" style="font-size:20px"><b>{}</b></h3>
             '''.format(loc)
map.get_root().html.add_child(folium.Element(title_html))


g_map = folium.GeoJson(
    geojson_file,
    name="GeoJson",
    style_function=lambda x: {'fillColor': 'orange'}
).add_to(map)


folium.GeoJsonTooltip(
    fields=['NISCODE','NAAM', 'OPPERVL'],
    aliases=['NISCODE', 'Naam', 'Oppervlakte'],
    sticky=False
).add_to(g_map)


folium.GeoJsonPopup(
    fields=["NAAM", "Average Prices: " ,"Woonhuis", "Villa", "Studio"],
    aliases=["Naam", "Average Prices: ","Woonhuis", "Villa", "Studio"]
).add_to(g_map)

html="""
    <iframe src=\"""" + codecs.open("graphs/AARTSELAAR.html", 'r').read() + """\" width="850" height="400"  frameborder="0">    
    """
popup1 = folium.Popup(folium.Html(html, script=True))

folium.Marker(['51.096246199999996','4.178629103169916'],popup=popup1,icon=folium.Icon( icon='home', prefix='fa')).add_to(map)

map

Here ^ I tried to add the popup to a marker, but that didn't work for me (it's also not really what I want, I want to add the popup to a polygon). I believe I should make some sort of loop that iterates over all features in the geoJson and adds a popup for every iteration.

Upvotes: 1

Views: 424

Answers (1)

Rob Raymond
Rob Raymond

Reputation: 31236

You have not provided sample data / geometry so used standard geopandas sample data

  • this will create popups / tooltips for each geometry. The popup is a plotly figure convented to an embedded URI encoded image. A pie chart of population of country as %age of population of all geometries.
  • investigated customising GeoJsonPopup() but found no solution
  • hence create a layer for each feature with it's own popup
import geopandas as gpd
import folium
from statistics import mean
import plotly.express as px
import base64, io

# some geometry
gdf = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres")).loc[
    lambda d: d["continent"].eq("Europe") & ~d.bounds.lt(-30).any(axis=1)
]

# create the map, nicely centered and zoomed
bounds = gdf.total_bounds
x = mean([bounds[0], bounds[2]])
y = mean([bounds[1], bounds[3]])
location = (y, x)

m = folium.Map(location=location)
m.fit_bounds([[bounds[1], bounds[0]], [bounds[3], bounds[2]]])

# given need to create a geojson layer for each figure,  create
# feature group to contain them
fg = folium.FeatureGroup(name="Europe", show=False)

# create an encocded image of graph...
# change to generate graph you want
def b64image(vals=[1, 2]):
    fig = (
        px.pie(values=vals)
        .update_layout(margin={"l": 0, "r": 0, "t": 0, "b": 0})
        .update_traces(texttemplate="%{percent:.0%}")
    )

    b = io.BytesIO(fig.to_image(format="png", width=80, height=80))
    b64 = base64.b64encode(b.getvalue())
    return "data:image/png;base64," + b64.decode("utf-8")


tot_pop = gdf["pop_est"].sum()

# create a geojson layer for each feature
for i, r in gdf.iterrows():
    # geodataframe of row
    gdf_ = gpd.GeoDataFrame(r.to_frame().T, crs=gdf.crs)
    # URI encoded image of plotly figure
    img_ = f'<img src="{b64image([r["pop_est"], tot_pop-r["pop_est"]])}"/>'

    choro_ = folium.GeoJson(
        gdf_.__geo_interface__,
        name=r["name"],
        style_function=lambda x: {"fillColor": "orange"},
        tooltip=folium.GeoJsonTooltip(gdf_.drop(columns="geometry").columns.tolist()),
    )
    # this is the real work around, add to layer which is a choro
    folium.Popup(img_).add_to(choro_)
    choro_.add_to(fg)

fg.add_to(m)

m

enter image description here

Upvotes: 1

Related Questions