Skip to content

Tooltip classes (simple and geojson/topojson) #883

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 175 commits into from
Aug 8, 2018

Conversation

jtbaker
Copy link
Contributor

@jtbaker jtbaker commented Jun 15, 2018

Added a new Tooltip class to reference GeoJson feature properties, with tests, documentation, and examples. It's flexible, and can take field names, aliases, that can be turned on/off with the 'labels' boolean, a 'sticky' property, and can incorporate Javascript's .toLocaleString() functionality if desired. The same string can be passed for each object with the 'text' variable as well if you'd rather not use the 'fields'.

Also updated the GeoJson JS template to reference this Tooltip object, and added a test to make sure that the values passed exist in the properties.

Use case at this nbviewer

Added a new Tooltip class to reference GeoJson feature properties, with tests, documentation, and examples. It's flexible, and can take field names, aliases, that can be turned on/off with the 'labels' boolean, a 'sticky' property, and can incorporate Javascript's .toLocaleString() functionality if desired. The same string can be passed for each object with the 'text' variable as well if you'd rather not use the 'fields'.

Also updated the GeoJson JS template to reference this Tooltip object, and added a test to make sure that the values passed exist in the properties.
@@ -416,7 +431,14 @@ def __init__(self, data, style_function=None, name=None,
self.highlight = highlight_function is not None

self.highlight_function = highlight_function or (lambda x: {})

Choose a reason for hiding this comment

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

W293 blank line contains whitespace

if self.tooltip:
if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()), f"The value {value} is not in the available properties. For your review, they are {tuple(self.data['features'][0]['properties'].keys())}"

Choose a reason for hiding this comment

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

E501 line too long (227 > 120 characters)
E999 SyntaxError: invalid syntax



Choose a reason for hiding this comment

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

W293 blank line contains whitespace

@@ -652,6 +674,63 @@ def __init__(self, html=None, icon_size=None, icon_anchor=None,
self.className = class_name


class Tooltip:
"""
Creates a Tooltip object for adding to features to display text as a property a Map by executing a javascript function when hovering the cursor over each feature.

Choose a reason for hiding this comment

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

E501 line too long (162 > 120 characters)

----------
fields: list or tuple.
list or tuple of labels of the GeoJson 'properties' or GeoPandas GeodataFrame columns you'd like to display.
aliases: list or tuple

Choose a reason for hiding this comment

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

W291 trailing whitespace

assert not all((fields,text)), "Please choose either fields or text."
assert any((fields,text)), "Please choose either fields or text."
assert isinstance(toLocaleString,bool), "toLocaleString must be either True or False"
self.fields=fields

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

self.fields=fields
self.aliases = aliases
self.text = text
self.labels=labels

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

self.aliases = aliases
self.text = text
self.labels=labels
self.sticky=sticky

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

self.text = text
self.labels=labels
self.sticky=sticky
self.toLocaleString=toLocaleString

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

self.result = self.fields
else:
self.result = self.text

Choose a reason for hiding this comment

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

W293 blank line contains whitespace

@jtbaker jtbaker changed the title Update features.py Update Features to add more flexible Tooltip options with GeoJson. Jun 15, 2018
@jtbaker jtbaker mentioned this pull request Jun 15, 2018
@ocefpaf
Copy link
Member

ocefpaf commented Jun 15, 2018

@jtbaker god start! Can you please add a test and, if possible, a notebook example?

PS: it seems you have some linter issue there.

if self.tooltip:
if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),
(f"The value {value} is not in the available properties."+

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),
(f"The value {value} is not in the available properties."+
f"For your review, they are {tuple(self.data['features'][0]['properties'].keys())}")

Choose a reason for hiding this comment

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

E128 continuation line under-indented for visual indent

@@ -651,6 +674,69 @@ def __init__(self, html=None, icon_size=None, icon_anchor=None,
self.html = html
self.className = class_name

class Tooltip:

Choose a reason for hiding this comment

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

E302 expected 2 blank lines, found 1

fields: list or tuple.
list or tuple of labels of the GeoJson 'properties' or GeoPandas GeodataFrame columns you'd like to display.
aliases: list or tuple
list or tuple of optional 'aliases' you'd like to display the each field name as, to describe the data in

Choose a reason for hiding this comment

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

W291 trailing whitespace

if fields:
assert isinstance(fields, (list, tuple)), "Please pass a list or tuple to Fields."
if bool(fields) & bool(aliases):
assert isinstance(aliases, (list,tuple))

Choose a reason for hiding this comment

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

E231 missing whitespace after ','

assert isinstance(fields, (list, tuple)), "Please pass a list or tuple to Fields."
if bool(fields) & bool(aliases):
assert isinstance(aliases, (list,tuple))
assert len(fields)==len(aliases), "Fields and Aliases must have the same length."

Choose a reason for hiding this comment

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

E225 missing whitespace around operator

Further linting.
if self.tooltip:
if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),(

Choose a reason for hiding this comment

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

E231 missing whitespace after ','

if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),(
f"The value {value} is not in the available properties.\n"+

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax
E225 missing whitespace around operator

for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()),(
f"The value {value} is not in the available properties.\n"+
f"For your review, they are {tuple(self.data['features'][0]['properties'].keys())}"

Choose a reason for hiding this comment

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

E101 indentation contains mixed spaces and tabs
W191 indentation contains tabs

assert value in tuple(self.data['features'][0]['properties'].keys()),(
f"The value {value} is not in the available properties.\n"+
f"For your review, they are {tuple(self.data['features'][0]['properties'].keys())}"
)

Choose a reason for hiding this comment

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

E101 indentation contains mixed spaces and tabs
W191 indentation contains tabs

if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()), (
f"The value {value} is not in the available properties.\n" +

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax
W291 trailing whitespace

for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()), (
f"The value {value} is not in the available properties.\n" +
f"For your review, they are {tuple(self.data['features'][0]['properties'].keys())}"

Choose a reason for hiding this comment

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

E101 indentation contains mixed spaces and tabs
W191 indentation contains tabs

if self.tooltip:
if self.tooltip.fields:
for value in self.tooltip.fields:
assert value in tuple(self.data['features'][0]['properties'].keys()), f"The value {value} is not in the available properties.\nFor your review, they are {tuple(self.data['features'][0]['properties'].keys())}"

Choose a reason for hiding this comment

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

E501 line too long (228 > 120 characters)
E999 SyntaxError: invalid syntax

@@ -651,6 +672,70 @@ def __init__(self, html=None, icon_size=None, icon_anchor=None,
self.html = html
self.className = class_name


Choose a reason for hiding this comment

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

W293 blank line contains whitespace

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, f"The value {value} is not in the available properties.\nFor your review, they are {keys}"

Choose a reason for hiding this comment

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

E501 line too long (132 > 120 characters)
E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, f"The value {value} is not available.\nFor your review, they are {keys}"

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, f"The value {value} is not available. For your review, they are {keys}"

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, f"{value} is not available. For your review, they are {keys}"

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, f"{value} is not available." +

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, (f"{value} is not available. " +

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

if self.tooltip.fields:
keys = tuple(self.data['features'][0]['properties'].keys())
for value in self.tooltip.fields:
assert value in keys, (f"{value} is not available in {keys}")

Choose a reason for hiding this comment

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

E999 SyntaxError: invalid syntax

folium/map.py Outdated
@@ -186,21 +187,29 @@ class Icon(MacroElement):
iconColor: '{{this.icon_color}}',
markerColor: '{{this.color}}',
prefix: '{{this.prefix}}',
extraClasses: 'fa-rotate-{{this.angle}}'
extraClasses: 'fa-rotate-{{this.angle}}',
Copy link
Member

@Conengmo Conengmo Aug 3, 2018

Choose a reason for hiding this comment

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

You're adding an option to Icon, which is fine in itself but please do this in a separate PR.

folium/map.py Outdated
super(Icon, self).__init__()
self._name = 'Icon'
self._name = 'AwesomeMarkers.icon'
Copy link
Member

Choose a reason for hiding this comment

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

Changing the name could have unintended consequences so please do this in a separate PR so it doesn't go unnoticed.

folium/map.py Outdated
self.color = color
self.icon = icon
self.icon_color = icon_color
self.angle = angle
self.prefix = prefix
self.spin = spin
self.options = {'icon': icon,
Copy link
Member

Choose a reason for hiding this comment

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

Changing the way options work here doesn't belong in this PR.

folium/map.py Outdated

def __init__(self, location, popup=None, tooltip=None, icon=None):
def __init__(self, location=None, popup=None, tooltip=None, icon=None):
Copy link
Member

Choose a reason for hiding this comment

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

Changing the default of location here doesn't have to do with tooltip, please remove it here.

folium/map.py Outdated
self.tooltip = tooltip
self.location = _validate_coordinates(location)
self._name = 'marker'
if location:
Copy link
Member

Choose a reason for hiding this comment

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

This check fails with Numpy arrays. Also adding it doesn't belong in this PR.

@@ -0,0 +1,12 @@
from __future__ import (absolute_import, division, print_function)

import pytest

Choose a reason for hiding this comment

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

F401 'pytest' imported but unused

@Conengmo
Copy link
Member

Conengmo commented Aug 7, 2018

I removed the stowaway xml files, @jtbaker maybe add them to your .gitignore file so you don't accidentally commit them again?

I think we're good to go!

@Conengmo Conengmo changed the title Update Features to add more flexible Tooltip options with GeoJson. Tooltip classes (simple and geojson/topojson) Aug 7, 2018
@jtbaker
Copy link
Contributor Author

jtbaker commented Aug 7, 2018

@Conengmo Awesome! Thanks for your help on this. This is my first real open source contribution, and I learned quite a bit in the process - have a decent grasp on JS and lower level Python, project best practices and scoping now. Looking forward to future PRs (with less errors). :-)

Since the changes have been approved, and I created this PR, am I good to close it out now? Is the merge onto the master complete, or does it need another review from @ocefpaf or someone else?

@ocefpaf
Copy link
Member

ocefpaf commented Aug 7, 2018

@ocefpaf or someone else?

LGTM but I'll leave to @Conengmo to do the honors and merge it.

This is my first real open source contribution

Awesome! And this is not an easy one!! Thanks for all the effort and for hanging in there while we reviewed it.

Thanks for the PR! Hope to see more 😉

@Conengmo Conengmo merged commit 05f8e81 into python-visualization:master Aug 8, 2018
@Conengmo
Copy link
Member

Conengmo commented Aug 8, 2018

Merged! Congrats @jtbaker and glad to hear you learned stuff. It took some time to figure out how to get this right, so I appreciate your patience on this PR.

Future PRs sounds good, let's do it!

@baskoes
Copy link

baskoes commented Aug 10, 2018

@jtbaker thank you for your efforts. I would really appreciate an example that combines chloropleth and tooltip into one map from the same dataframe / geojson. The example in the GeoJsonTooltip class docstring just seems to call "Tooltip(" and I can't figure out where that class/function is coming from. If someone could point me in the right direction I would be happy to share a ipynb for the examples directory. thanks again.

@jtbaker
Copy link
Contributor Author

jtbaker commented Aug 10, 2018

@Conengmo Looks like we forgot to update the naming in the docstring after the class split. Whoops!

@baskoes, Thank you! It was fun, and I learned quite a bit about how the library works under the hood. Those references to Tooltip in the GeoJsonTooltip docstring should be to GeoJsonTooltip itself(we'll get these updated ASAP). Tooltip is a separate class for passing text of your choice, through the text argument - i.e. 'Click for more info'. GeoJsonTooltip allows you to reference the column names/properties keys, to display the associated values of each feature in the GeoJson dataset via the fields argument, with an option to relabel/describe the data in the visualization. This is completed on the fly for each feature using a JavaScript function.

I'll get a new notebook added to the examples directory to do a demo on how to use both classes.

The usage would looks something like this:

folium.GeoJson(gdf, tooltip=folium.GeoJsonTooltip(fields=['NAMELSAD',"POP2010"], aliases=['State Name','2010 Census Population'], sticky=True, style="font-family: courier new; color: steelblue;", opacity=0.8, direction='top'), )

@Conengmo
Copy link
Member

Looks like we forgot to update the naming in the docstring after the class split.

Good catch, didn't notice that.

A notebook with examples of GeoJsonTooltip would be great, looking forward to reviewing and merging it. We can change the docstring in the same PR I suppose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants