Skip to content

Enable GeoPandas drawing #296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 15, 2015
Merged

Conversation

BibMartin
Copy link
Contributor

Addresses #34.

Note that it only works with GeoDataFrame, because GeoSeries.to_json method fails (it's inherited from pandas.Series and does not link to the feature transformation code).

As for example notebook, the link is here :
http://nbviewer.ipython.org/github/bibmartin/folium/blob/geopandas/examples/GeoPandas.ipynb

@@ -241,6 +241,14 @@ def __init__(self, data, style_function=None):
else: # This is a filename
self.embed = False
self.data = data
elif hasattr(data, 'to_crs'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use isinstance I have a few custom object with to_crs() defined 😉
Also, if we use geopandas.geodataframe.GeoDataFrame we can probably drop the next if below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal was to avoid importing geopandas explicitely in folium.
Otherwise, we'll have another dependency.

Maybe there's some other way to filter that.

'geopandas' in data.__class__().__str__()

is ugly, but may work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know what would be the best approach 😑

Sometimes I think that empowering users by showing how to convert a GeoDataFrame to GeoJSON, and its pyproj like crs to EPSG would be better than wrapping all that in folium...

@ocefpaf
Copy link
Member

ocefpaf commented Dec 14, 2015

How about we do something like this:

if/elif(s) and at the else:

else:
    from geopandas import GeoDataFrame, GeoSeries
    self.embed = True
    if isinstance(data, GeoDataFrame):
        self.data = json.loads(data.to_crs(epsg='4326').to_json())
    elif isinstance(data, GeoSeries):
        self.data = json.loads(data.__geo_interface__)
    else:
        raise ValueError('Unhandled data type {!r}.'.format(data))

This is untested, but I prefer this patter because the ImportError has the information we need and the code reads better (IMO 😉).

If you disagree let's at least put the {!r} when raising the ValueError. That might help people to debug what object triggered the problem.

@BibMartin
Copy link
Contributor Author

@ocefpaf
If I try to pass an unappropriate object to GeoJson, I shall get

ValueError('Unhandled data type {!r}.'.format(data))

Yet with your piece of code, if I have not installed geopandas, I will get

ImportError

Well, this is a matter of readability ; I'll try to think to it further in the coming days.

@ocefpaf
Copy link
Member

ocefpaf commented Dec 15, 2015

@BibMartin you are right. My way can hinder some other failure unrelated to the optional dependency.

However, one might argue that whatever we supported was already tested. An alternative would be to use a try/except ImportError and re-raise it with a new message. "Need optional dependecy geopandas. Could not parse object {!r}."

Anyway, this is not crucial to this PR. I guess that the main goal is to avoid the need of Geopandas as a core dependency. If that starts to get in the way we might try to make this external in a different way. Not sure how yet. I trust your judgement here. Just let me know when you are OK with this so I can merge it.

@BibMartin
Copy link
Contributor Author

@ocefpaf
Thanks to geopandas/geopandas#262, I've realized that geopandas objects now have a __geo_interface__ attribute that does (almost) all the job.
So here is another suggestion to handle the pb...

Please tell me if you're okay with this ; I'll squash commits then.

if hasattr(data, '__geo_interface__'):
# We have a GeoPandas 0.2 object
self.embed = True
self.data = json.loads(json.dumps(data.to_crs(epsg='4326').__geo_interface__))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The self.embed = True and data.to_crs(epsg='4326') could be up one level to avoid repetition.

PS: __geo_interface__ is a dict, right? Just making sure that we need the json.dumps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The self.embed = True could be up one level to avoid repetition

True. I do it.

data.to_crs(epsg='4326') could be up one level to avoid repetition

No, otherwise, the Exception 'Unable to transform this object to a GeoJSON.' will be replaced by Unknown method 'to_crs' (or something like this).

PS: geo_interface is a dict, right? Just making sure that we need the json.dumps.

__geo_interface__ is a dict. But we need to do a deep copy of the object here ; otherwise, we'll have unintended side effects : further folium computation (like styling) may modify the geopandas object...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, otherwise, the Exception 'Unable to transform this object to a GeoJSON.' will be replaced by Unknown method 'to_crs' (or something like this).

I was thinking of a copy like:

a = data.to_crs(epsg='4326')

and then

a.__geo_interface__ or a.to_json()

But that way is fine.

@ocefpaf
Copy link
Member

ocefpaf commented Dec 15, 2015

Some minor comments. Will merge as soon as you ping me back.

@BibMartin
Copy link
Contributor Author

@ocefpaf
Thanks for your feedback.

Answers are in the code.

Squashed and ready for merge if all is ok to you.

@ocefpaf
Copy link
Member

ocefpaf commented Dec 15, 2015

Awesome @BibMartin!

I always learn a lot by reviewing your PRs. Thanks!

ocefpaf added a commit that referenced this pull request Dec 15, 2015
@ocefpaf ocefpaf merged commit 7ec30b7 into python-visualization:master Dec 15, 2015
@ocefpaf ocefpaf added the enhancement Feature request or idea about how to make folium better label Feb 12, 2016
@ocefpaf ocefpaf added this to the v0.2.0 milestone Feb 12, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Feature request or idea about how to make folium better
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants