Skip to content

Commit

Permalink
v4 colorscale updates (#1647)
Browse files Browse the repository at this point in the history
* Added 'rebeccapurple' named css color
* Support color lists in colorscale validator
* Support strings of named colors from plotly.colors as colorscale
* Add all legacy plotly.js colorscale definitions to plotly.colors and always coerce color string to colorscale definitions.
* Rename Plotly sequential colorscale to Plotly3 since this was only
the theme default for version 3
* Added sequence colorscale tests
* Change default colorscale for annotated heatmap figure factory to Plasma
  • Loading branch information
jonmmease committed Jul 1, 2019
1 parent bdd5a3a commit de02fca
Show file tree
Hide file tree
Showing 285 changed files with 5,239 additions and 4,312 deletions.
135 changes: 84 additions & 51 deletions packages/python/plotly/_plotly_utils/basevalidators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,7 @@ class ColorValidator(BaseValidator):
"red",
"rosybrown",
"royalblue",
"rebeccapurple",
"saddlebrown",
"salmon",
"sandybrown",
Expand Down Expand Up @@ -1503,86 +1504,118 @@ class ColorscaleValidator(BaseValidator):
},
"""

named_colorscales = [
"Greys",
"YlGnBu",
"Greens",
"YlOrRd",
"Bluered",
"RdBu",
"Reds",
"Blues",
"Picnic",
"Rainbow",
"Portland",
"Jet",
"Hot",
"Blackbody",
"Earth",
"Electric",
"Viridis",
"Cividis",
]

def __init__(self, plotly_name, parent_name, **kwargs):
super(ColorscaleValidator, self).__init__(
plotly_name=plotly_name, parent_name=parent_name, **kwargs
)

# named colorscales initialized on first use
self._named_colorscales = None

@property
def named_colorscales(self):
if self._named_colorscales is None:
import inspect
import itertools
from plotly import colors

colorscale_members = itertools.chain(
inspect.getmembers(colors.sequential),
inspect.getmembers(colors.diverging),
inspect.getmembers(colors.cyclical),
)

self._named_colorscales = {
c[0].lower(): c[1]
for c in colorscale_members
if isinstance(c, tuple)
and len(c) == 2
and isinstance(c[0], str)
and isinstance(c[1], list)
}

return self._named_colorscales

def description(self):
colorscales_str = "\n".join(
textwrap.wrap(
repr(sorted(list(self.named_colorscales))),
initial_indent=" " * 12,
subsequent_indent=" " * 13,
break_on_hyphens=False,
width=80,
)
)

desc = """\
The '{plotly_name}' property is a colorscale and may be
specified as:
- A list of colors that will be spaced evenly to create the colorscale.
Many predefined colorscale lists are included in the sequential, diverging,
and cyclical modules in the plotly.colors package.
- A list of 2-element lists where the first element is the
normalized color level value (starting at 0 and ending at 1),
and the second item is a valid color string.
(e.g. [[0, 'green'], [0.5, 'red'], [1.0, 'rgb(0, 0, 255)']])
- One of the following named colorscales:
['Greys', 'YlGnBu', 'Greens', 'YlOrRd', 'Bluered', 'RdBu',
'Reds', 'Blues', 'Picnic', 'Rainbow', 'Portland', 'Jet',
'Hot', 'Blackbody', 'Earth', 'Electric', 'Viridis', 'Cividis']
""".format(
plotly_name=self.plotly_name
{colorscales_str}
""".format(
plotly_name=self.plotly_name, colorscales_str=colorscales_str
)

return desc

def validate_coerce(self, v):
v_valid = False

if v is None:
# Pass None through
pass
if v is None:
v_valid = True
elif isinstance(v, string_types):
v_match = [
el
for el in ColorscaleValidator.named_colorscales
if el.lower() == v.lower()
]
if v_match:
v_lower = v.lower()
if v_lower in self.named_colorscales:
# Convert to color list
v = self.named_colorscales[v_lower]

# Convert to list of lists colorscale
d = len(v) - 1
v = [[(1.0 * i) / (1.0 * d), x] for i, x in enumerate(v)]

v_valid = True

elif is_array(v) and len(v) > 0:
invalid_els = [
e
for e in v
if (
not is_array(e)
or len(e) != 2
or not isinstance(e[0], numbers.Number)
or not (0 <= e[0] <= 1)
or not isinstance(e[1], string_types)
or ColorValidator.perform_validate_coerce(e[1]) is None
)
]
# If firset element is a string, treat as colorsequence
if isinstance(v[0], string_types):
invalid_els = [
e for e in v if ColorValidator.perform_validate_coerce(e) is None
]

if len(invalid_els) == 0:
v_valid = True

# Convert to list of lists colorscale
d = len(v) - 1
v = [[(1.0 * i) / (1.0 * d), x] for i, x in enumerate(v)]
else:
invalid_els = [
e
for e in v
if (
not is_array(e)
or len(e) != 2
or not isinstance(e[0], numbers.Number)
or not (0 <= e[0] <= 1)
or not isinstance(e[1], string_types)
or ColorValidator.perform_validate_coerce(e[1]) is None
)
]

if len(invalid_els) == 0:
v_valid = True
if len(invalid_els) == 0:
v_valid = True

# Convert to list of lists
v = [[e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v]
# Convert to list of lists
v = [
[e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v
]

if not v_valid:
self.raise_invalid_val(v)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
from numbers import Number
import six

from plotly import exceptions
from _plotly_utils import exceptions


# Built-in qualitative color sequences and sequential,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .cmocean import balance, delta, curl # noqa: F401
from .carto import Armyrose, Fall, Geyser, Temps, Tealrose, Tropic, Earth # noqa: F401

from .plotlyjs import Picnic, Portland # noqa: F401

from ._swatches import _swatches

Expand Down
181 changes: 181 additions & 0 deletions packages/python/plotly/_plotly_utils/colors/plotlyjs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Copied from
# https://github.com/plotly/plotly.js/blob/master/src/components/colorscale/scales.js
_plotlyjs_scales = {
"Greys": [[0, "rgb(0,0,0)"], [1, "rgb(255,255,255)"]],
"YlGnBu": [
[0, "rgb(8,29,88)"],
[0.125, "rgb(37,52,148)"],
[0.25, "rgb(34,94,168)"],
[0.375, "rgb(29,145,192)"],
[0.5, "rgb(65,182,196)"],
[0.625, "rgb(127,205,187)"],
[0.75, "rgb(199,233,180)"],
[0.875, "rgb(237,248,217)"],
[1, "rgb(255,255,217)"],
],
"Greens": [
[0, "rgb(0,68,27)"],
[0.125, "rgb(0,109,44)"],
[0.25, "rgb(35,139,69)"],
[0.375, "rgb(65,171,93)"],
[0.5, "rgb(116,196,118)"],
[0.625, "rgb(161,217,155)"],
[0.75, "rgb(199,233,192)"],
[0.875, "rgb(229,245,224)"],
[1, "rgb(247,252,245)"],
],
"YlOrRd": [
[0, "rgb(128,0,38)"],
[0.125, "rgb(189,0,38)"],
[0.25, "rgb(227,26,28)"],
[0.375, "rgb(252,78,42)"],
[0.5, "rgb(253,141,60)"],
[0.625, "rgb(254,178,76)"],
[0.75, "rgb(254,217,118)"],
[0.875, "rgb(255,237,160)"],
[1, "rgb(255,255,204)"],
],
"Bluered": [[0, "rgb(0,0,255)"], [1, "rgb(255,0,0)"]],
# modified RdBu based on
# http:#www.kennethmoreland.com/color-maps/
"RdBu": [
[0, "rgb(5,10,172)"],
[0.35, "rgb(106,137,247)"],
[0.5, "rgb(190,190,190)"],
[0.6, "rgb(220,170,132)"],
[0.7, "rgb(230,145,90)"],
[1, "rgb(178,10,28)"],
],
# Scale for non-negative numeric values
"Reds": [
[0, "rgb(220,220,220)"],
[0.2, "rgb(245,195,157)"],
[0.4, "rgb(245,160,105)"],
[1, "rgb(178,10,28)"],
],
# Scale for non-positive numeric values
"Blues": [
[0, "rgb(5,10,172)"],
[0.35, "rgb(40,60,190)"],
[0.5, "rgb(70,100,245)"],
[0.6, "rgb(90,120,245)"],
[0.7, "rgb(106,137,247)"],
[1, "rgb(220,220,220)"],
],
"Picnic": [
[0, "rgb(0,0,255)"],
[0.1, "rgb(51,153,255)"],
[0.2, "rgb(102,204,255)"],
[0.3, "rgb(153,204,255)"],
[0.4, "rgb(204,204,255)"],
[0.5, "rgb(255,255,255)"],
[0.6, "rgb(255,204,255)"],
[0.7, "rgb(255,153,255)"],
[0.8, "rgb(255,102,204)"],
[0.9, "rgb(255,102,102)"],
[1, "rgb(255,0,0)"],
],
"Rainbow": [
[0, "rgb(150,0,90)"],
[0.125, "rgb(0,0,200)"],
[0.25, "rgb(0,25,255)"],
[0.375, "rgb(0,152,255)"],
[0.5, "rgb(44,255,150)"],
[0.625, "rgb(151,255,0)"],
[0.75, "rgb(255,234,0)"],
[0.875, "rgb(255,111,0)"],
[1, "rgb(255,0,0)"],
],
"Portland": [
[0, "rgb(12,51,131)"],
[0.25, "rgb(10,136,186)"],
[0.5, "rgb(242,211,56)"],
[0.75, "rgb(242,143,56)"],
[1, "rgb(217,30,30)"],
],
"Jet": [
[0, "rgb(0,0,131)"],
[0.125, "rgb(0,60,170)"],
[0.375, "rgb(5,255,255)"],
[0.625, "rgb(255,255,0)"],
[0.875, "rgb(250,0,0)"],
[1, "rgb(128,0,0)"],
],
"Hot": [
[0, "rgb(0,0,0)"],
[0.3, "rgb(230,0,0)"],
[0.6, "rgb(255,210,0)"],
[1, "rgb(255,255,255)"],
],
"Blackbody": [
[0, "rgb(0,0,0)"],
[0.2, "rgb(230,0,0)"],
[0.4, "rgb(230,210,0)"],
[0.7, "rgb(255,255,255)"],
[1, "rgb(160,200,255)"],
],
"Earth": [
[0, "rgb(0,0,130)"],
[0.1, "rgb(0,180,180)"],
[0.2, "rgb(40,210,40)"],
[0.4, "rgb(230,230,50)"],
[0.6, "rgb(120,70,20)"],
[1, "rgb(255,255,255)"],
],
"Electric": [
[0, "rgb(0,0,0)"],
[0.15, "rgb(30,0,100)"],
[0.4, "rgb(120,0,100)"],
[0.6, "rgb(160,90,0)"],
[0.8, "rgb(230,200,0)"],
[1, "rgb(255,250,220)"],
],
"Viridis": [
[0, "#440154"],
[0.06274509803921569, "#48186a"],
[0.12549019607843137, "#472d7b"],
[0.18823529411764706, "#424086"],
[0.25098039215686274, "#3b528b"],
[0.3137254901960784, "#33638d"],
[0.3764705882352941, "#2c728e"],
[0.4392156862745098, "#26828e"],
[0.5019607843137255, "#21918c"],
[0.5647058823529412, "#1fa088"],
[0.6274509803921569, "#28ae80"],
[0.6901960784313725, "#3fbc73"],
[0.7529411764705882, "#5ec962"],
[0.8156862745098039, "#84d44b"],
[0.8784313725490196, "#addc30"],
[0.9411764705882353, "#d8e219"],
[1, "#fde725"],
],
"Cividis": [
[0.000000, "rgb(0,32,76)"],
[0.058824, "rgb(0,42,102)"],
[0.117647, "rgb(0,52,110)"],
[0.176471, "rgb(39,63,108)"],
[0.235294, "rgb(60,74,107)"],
[0.294118, "rgb(76,85,107)"],
[0.352941, "rgb(91,95,109)"],
[0.411765, "rgb(104,106,112)"],
[0.470588, "rgb(117,117,117)"],
[0.529412, "rgb(131,129,120)"],
[0.588235, "rgb(146,140,120)"],
[0.647059, "rgb(161,152,118)"],
[0.705882, "rgb(176,165,114)"],
[0.764706, "rgb(192,177,109)"],
[0.823529, "rgb(209,191,102)"],
[0.882353, "rgb(225,204,92)"],
[0.941176, "rgb(243,219,79)"],
[1.000000, "rgb(255,233,69)"],
],
}

# Create variable named after each scale that contains the sequence of colors only
for scale_name, scale_pairs in _plotlyjs_scales.items():
scale_sequence = [c[1] for c in scale_pairs]
exec (
"{scale_name} = {scale_sequence}".format(
scale_name=scale_name, scale_sequence=scale_sequence
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def swatches():

swatches.__doc__ = _swatches.__doc__

Plotly = [
Plotly3 = [
"#0508b8",
"#1910d8",
"#3c19f0",
Expand Down Expand Up @@ -92,6 +92,8 @@ def swatches():
"#f0f921",
]

from .plotlyjs import Blackbody, Bluered, Electric, Hot, Jet, Rainbow # noqa: F401

from .colorbrewer import ( # noqa: F401
Blues,
BuGn,
Expand Down
Loading

0 comments on commit de02fca

Please sign in to comment.