Skip to content

Fix reversed color scale issues in annotated heatmap figure factory #1251

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 5 commits into from
Oct 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions plotly/figure_factory/_annotated_heatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def create_annotated_heatmap(z, x=None, y=None, annotation_text=None,
defined, the colors are defined logically as black or white
depending on the heatmap's colorscale.
:param (bool) showscale: Display colorscale. Default = False
:param (bool) reversescale: Reverse colorscale. Default = False
:param kwargs: kwargs passed through plotly.graph_objs.Heatmap.
These kwargs describe other attributes about the annotated Heatmap
trace such as the colorscale. For more information on valid kwargs
Expand Down Expand Up @@ -98,14 +99,14 @@ def create_annotated_heatmap(z, x=None, y=None, annotation_text=None,

if x or y:
trace = dict(type='heatmap', z=z, x=x, y=y, colorscale=colorscale,
showscale=showscale, **kwargs)
showscale=showscale, reversescale=reversescale, **kwargs)
layout = dict(annotations=annotations,
xaxis=dict(ticks='', dtick=1, side='top',
gridcolor='rgb(0, 0, 0)'),
yaxis=dict(ticks='', dtick=1, ticksuffix=' '))
else:
trace = dict(type='heatmap', z=z, colorscale=colorscale,
showscale=showscale, **kwargs)
showscale=showscale, reversescale=reversescale, **kwargs)
layout = dict(annotations=annotations,
xaxis=dict(ticks='', side='top',
gridcolor='rgb(0, 0, 0)',
Expand All @@ -127,6 +128,12 @@ def to_rgb_color_list(color_str, default):
return default


def should_use_black_text(background_color):
return (background_color[0] * 0.299 +
background_color[1] * 0.587 +
background_color[2] * 0.114) > 186


class _AnnotatedHeatmap(object):
"""
Refer to TraceFactory.create_annotated_heatmap() for docstring
Expand Down Expand Up @@ -173,39 +180,47 @@ def get_text_color(self):
'Earth', 'Electric', 'Viridis', 'Cividis']
# Plotly colorscales ranging from a darker shade to a lighter shade
colorscales_reverse = ['Reds']

white = '#FFFFFF'
black = '#000000'
if self.font_colors:
min_text_color = self.font_colors[0]
max_text_color = self.font_colors[-1]
elif self.colorscale in colorscales and self.reversescale:
min_text_color = '#000000'
max_text_color = '#FFFFFF'
min_text_color = black
max_text_color = white
elif self.colorscale in colorscales:
min_text_color = '#FFFFFF'
max_text_color = '#000000'
min_text_color = white
max_text_color = black
elif self.colorscale in colorscales_reverse and self.reversescale:
min_text_color = '#FFFFFF'
max_text_color = '#000000'
min_text_color = white
max_text_color = black
elif self.colorscale in colorscales_reverse:
min_text_color = '#000000'
max_text_color = '#FFFFFF'
min_text_color = black
max_text_color = white
elif isinstance(self.colorscale, list):

min_col = to_rgb_color_list(self.colorscale[0][1],
[255, 255, 255])
max_col = to_rgb_color_list(self.colorscale[-1][1],
[255, 255, 255])

if (min_col[0]*0.299 + min_col[1]*0.587 + min_col[2]*0.114) > 186:
min_text_color = '#000000'
# swap min/max colors if reverse scale
if self.reversescale:
min_col, max_col = max_col, min_col

if should_use_black_text(min_col):
min_text_color = black
else:
min_text_color = '#FFFFFF'
if (max_col[0]*0.299 + max_col[1]*0.587 + max_col[2]*0.114) > 186:
max_text_color = '#000000'
min_text_color = white

if should_use_black_text(max_col):
max_text_color = black
else:
max_text_color = '#FFFFFF'
max_text_color = white
else:
min_text_color = '#000000'
max_text_color = '#000000'
min_text_color = black
max_text_color = black
return min_text_color, max_text_color

def get_z_mid(self):
Expand Down
81 changes: 81 additions & 0 deletions plotly/tests/test_optional/test_tools/test_figure_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ def test_simple_annotated_heatmap(self):
expected_a_heat = {
'data': [{'colorscale': 'RdBu',
'showscale': False,
'reversescale': False,
'type': 'heatmap',
'z': [[1, 0, 0.5], [0.25, 0.75, 0.45]]}],
'layout': {'annotations': [{'font': {'color': '#000000'},
Expand Down Expand Up @@ -831,6 +832,7 @@ def test_annotated_heatmap_kwargs(self):
expected_a = {'data': [{'colorscale':
[[0, 'rgb(255,255,255)'], [1, '#e6005a']],
'showscale': False,
'reversescale': False,
'type': 'heatmap',
'x': ['A', 'B'],
'y': ['One', 'Two', 'Three'],
Expand Down Expand Up @@ -891,6 +893,85 @@ def test_annotated_heatmap_kwargs(self):
self.assert_fig_equal(a['layout'],
expected_a['layout'])

def test_annotated_heatmap_reversescale(self):

# we should be able to create an annotated heatmap with x and y axes
# lables, a defined colorscale, and supplied text.

z = [[1, 0], [.25, .75], [.45, .5]]
text = [['first', 'second'], ['third', 'fourth'], ['fifth', 'sixth']]
a = ff.create_annotated_heatmap(z,
x=['A', 'B'],
y=['One', 'Two', 'Three'],
annotation_text=text,
reversescale=True,
colorscale=[[0, 'rgb(255,255,255)'],
[1, '#e6005a']])
expected_a = {'data': [{'colorscale':
[[0, 'rgb(255,255,255)'], [1, '#e6005a']],
'showscale': False,
'reversescale': True,
'type': 'heatmap',
'x': ['A', 'B'],
'y': ['One', 'Two', 'Three'],
'z': [[1, 0], [0.25, 0.75], [0.45, 0.5]]}],
'layout': {'annotations': [
{'font': {'color': '#000000'},
'showarrow': False,
'text': 'first',
'x': 'A',
'xref': 'x',
'y': 'One',
'yref': 'y'},
{'font': {'color': '#FFFFFF'},
'showarrow': False,
'text': 'second',
'x': 'B',
'xref': 'x',
'y': 'One',
'yref': 'y'},
{'font': {'color': '#FFFFFF'},
'showarrow': False,
'text': 'third',
'x': 'A',
'xref': 'x',
'y': 'Two',
'yref': 'y'},
{'font': {'color': '#000000'},
'showarrow': False,
'text': 'fourth',
'x': 'B',
'xref': 'x',
'y': 'Two',
'yref': 'y'},
{'font': {'color': '#FFFFFF'},
'showarrow': False,
'text': 'fifth',
'x': 'A',
'xref': 'x',
'y': 'Three',
'yref': 'y'},
{'font': {'color': '#FFFFFF'},
'showarrow': False,
'text': 'sixth',
'x': 'B',
'xref': 'x',
'y': 'Three',
'yref': 'y'}],
'xaxis': {'dtick': 1,
'gridcolor': 'rgb(0, 0, 0)',
'side': 'top',
'ticks': ''},
'yaxis': {'dtick': 1, 'ticks': '',
'ticksuffix': ' '}}}
self.assert_fig_equal(
a['data'][0],
expected_a['data'][0],
)

self.assert_fig_equal(a['layout'],
expected_a['layout'])


class TestTable(TestCase, NumpyTestUtilsMixin):

Expand Down