question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Choropleths, tooltips/popups and pandas dataframes

See original GitHub issue

Is your feature request related to a problem? Please describe. Sorry if this has been resolved already or if I’m missing something obvious. I’m pretty new to Folium and this is my first time posting a question/request on github. I think I have seen this issue mentioned before but I’m uncertain if solutions where implemented or rejected.

I have been trying to wrap my head around how to create polygon specific popups or tooltips when creating choropleth-map. I would like to display data concerning the specific region from the dataframe in a popup or tooltip when clicking or hovering the pointer over the region on the map.

As far I understand the documentation it is only possible to display information that’s in the GeoJson using folium.features.GeoJsonTooltip. The problem with that solution is that you need to merge the data from the pandas dataframe into the GeoJson, which goes against the convenience handling the data in pandas and the geographical data in GeoJson.

I have tried using both a folium.Choropleth object and making my own using folium.GeoJson.

Is there some example of how this could be achieved using Folium and regular pandas?

Describe the solution you’d like I would like to be able to code something like this to access data from the pandas dataframe:

m = folium.Map(location=[lat, long], width=600, height=1000, zoom_start=5)

choro = folium.Choropleth(
        geo_data=GeoJsonshape,
        name='Unemployment',
        data=dataframe,
        columns=['region', 'rate'],
        key_on='feature.properties.name',
        legend_name='Unemployment'
        tooltip=dataframe['rate'].where([feature['properties']['name']]
        )

choro.add_to(m)

or

choro = folium.GeoJson(
        GeoJsonshape,
        name='Unemployment',,
        tooltip=dataframe['rate'].where([feature['properties']['name']],
        style_function=lambda feature: {
        'fillColor': colormap(data[feature['properties']['name']]),
        'color': 'black',
        'weight': 1,
        'fillOpacity': 0.6,
        'line_opacity': 0.2,
    }
    )

Describe alternatives you’ve considered Another solution I think would be to use Geopandas to join the GeoJson with the dataframe, but this is not practically feasible solution for me. Geopandas is dependent on GDAL. My current environment makes it more or less impossible to install this.

If anyone can give me any pointers on how to solve this I would be grateful. I have tried to look at the folium code to perhaps implement a work around for myself but it is above my current ability.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
jtbakercommented, Aug 26, 2020

Describe alternatives you’ve considered Another solution I think would be to use Geopandas to join the GeoJson with the Dataframe, but this is not practically feasible solution for me. Geopandas is dependent on GDAL. My current environment makes it more or less impossible to install this.

@ostropunk the core issue is that the fields you want to show in the tooltip need to be available in the geojson properties. If you can’t do use geopandas to do the join, one option would be to do it iteratively in python by looping through each of the features and finding the corresponding data point you want to put in to the tooltip. Then you should be able to add the keys you want to display to the GeoJsonTooltip.fields list.

Maybe something like:

import json
import pandas as pd

def merge(geojson_path: str, df: pd.DataFrame, geo_key: str = "id", df_key: str = "id"):
    with open(geojson_path, 'r') as file:
        __geo_interface__ = json.load(file)

    records = json.loads(df.to_json(orient="records"))

    for feature in __geo_interface__.get('features'):
        id = feature['properties'].get(geo_key)
        for record in records:
            if record.get(df_key) == id:
                feature['properties'].update(**record)
    return __geo_interface__

write out the returned object from merge to a json file, and pass that path to the geo_data property of Choropleth

I am not at all familiar with jQuery, which is what some of this looks like, but I did work out that the lookup of the value that is being displayed in the tooltip comes from this bit:

@markhudson42 the $ syntax in tick marks is an ES6 method for string interpolation. For example:

const x = "test"
const y = `${x}`
console.log(x === y) // true

It’s helpful for interpolating special or escape characters that might otherwise break string concatenation methods, which has posed some issues in the past.

2reactions
markhudson42commented, Aug 26, 2020

Hello,

I am quite a beginner with javascript and the underlying folium code, too, but what I have been doing lately when trying to understand what folium is doing behind the scenes is to create an html file of an example that I want to investigate and maybe extend, and then I play around with the html and work through things in the browser javascript debugger and see what I can work out.

For your example, I don’t think it can be done using existing functionality but I started with this example:

m = folium.Map(location=[48, -102], zoom_start=3)

choro = folium.GeoJson(
            state_geo,
            name='Unemployment',
            style_function=lambda feature: {
            'fillOpacity': 0.5,
            'weight': 1,
            'fillColor': 'black',
            'color': 'black'
            }
        )

choro.add_to(m)

# add geojsontooltip?
gjs_tooltip = folium.GeoJsonTooltip( 
    fields=['name'], 
    aliases=['State'], 
    localize=True, 
    style=('background-color: grey; color: white; font-family:' 
           'courier new; font-size: 24px; padding: 10px;') 
)

gjs_tooltip.add_to(choro)

folium.LayerControl().add_to(m)

m.save("geojson_tooltip_example.html")
m

and looked at what it is in the corresponding javascript that is used to extract the fields from the data that is passed to the folium.GeoJson function so that it can be used by the folium.GeoJsonTooltip function.

In my HTML, I have the following:

    geo_json_938c91e9ef9041a8872f48536c232b94.bindTooltip(
    function(layer){
    let div = L.DomUtil.create('div');
    
    let handleObject = feature=>typeof(feature)=='object' ? JSON.stringify(feature) : feature;
    let fields = ["name"];
    let aliases = ["State"];
    let table = '<table>' + String(fields.map((v,i)=> `<tr><th>${aliases[i].toLocaleString()}</th><td>${handleObject(layer.feature.properties[v]).toLocaleString()}</td></tr>`).join('')) +'</table>';

    div.innerHTML=table;
    
    return div
    }
    ,{"className": "foliumtooltip", "sticky": true});

I am not at all familiar with jQuery, which is what some of this looks like, but I did work out that the lookup of the value that is being displayed in the tooltip comes from this bit:

${handleObject(layer.feature.properties[v]).toLocaleString()

I don’t know what the map function is doing, but v contains "name", and layer.feature,properties[v] returns the value of "name" for the current feature under the mouse pointer, which could be, say, “Alaska”.

So, as an experiment I created a dummy variable in the javascript section of the html and defined a random number for each state:

let dummy = {"Alabama": 0.127147429, "Alaska": 0.951358548, "Arizona": 0.405535878, ...

and then modified the code involving $handleObject so that it looks up in this dummy variable:

let table = '<table>' + String(fields.map((v,i)=> `<tr><th>${aliases[i].toLocaleString()}</th><td>${handleObject(dummy[layer.feature.properties[v]]).toLocaleString()}</td></tr>`).join('')) +'</table>';

and this works:

image

So… I am presuming that someone with much more knowledge of writing good javascript could probably work out how to do something similar to this by modifying the javascript macro. I think that what would be needed is a way to define various data objects storing the output values for each input field value, and then use that object as a lookup on the original feature property.

I think doing this for multiple fields would be more complicated than in my simple hack, but it looks possible.

Mark.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Text as tooltip, popup or labels in folium choropleth GeoJSON ...
I would be happy with a solution that allows this to be a popup, tooltip or a simple text label written on top....
Read more >
Text as tooltip, popup or labels in folium choropleth GeoJSON ...
I would be happy with a solution that allows this to be a popup, tooltip or a simple text label written on top....
Read more >
GeoJsonTooltip on Choropleth showing a pandas column?
I want to have a tooltip on my Choropleth to show the actual value ... refer to the attached dataframe for showing the...
Read more >
Python's Folium to create choropleth maps - Nagaraj Bhat
Folium is python library built on top of leaflet.js. It is used to visualize data through interactive maps, choropleth visualization, ...
Read more >
Quickstart — Folium 0.12.1 documentation - GitHub Pages
Choropleth can be easily created by binding the data between Pandas DataFrames/Series and Geo/TopoJSON geometries. Color Brewer sequential color schemes are ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found