Skip to content

Commit 2a59bc0

Browse files
Merge pull request #3136 from CarlAndersson/colorscale-sampling
Adds function to sample colorscales
2 parents db920c2 + 1b8ec25 commit 2a59bc0

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

Diff for: packages/python/plotly/_plotly_utils/colors/__init__.py

+45
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,48 @@ def get_colorscale(name):
833833
if should_reverse:
834834
return colorscale[::-1]
835835
return colorscale
836+
837+
838+
def sample_colorscale(colorscale, samplepoints, low=0.0, high=1.0, colortype="rgb"):
839+
"""
840+
Samples a colorscale at specific points.
841+
842+
Interpolates between colors in a colorscale to find the specific colors
843+
corresponding to the specified sample values. The colorscale can be specified
844+
as a list of `[scale, color]` pairs, as a list of colors, or as a named
845+
plotly colorscale. The samplepoints can be specefied as an iterable of specific
846+
points in the range [0.0, 1.0], or as an integer number of points which will
847+
be spaced equally between the low value (default 0.0) and the high value
848+
(default 1.0). The output is a list of colors, formatted according to the
849+
specified colortype.
850+
"""
851+
from bisect import bisect_left
852+
853+
try:
854+
validate_colorscale(colorscale)
855+
except exceptions.PlotlyError:
856+
if isinstance(colorscale, str):
857+
colorscale = get_colorscale(colorscale)
858+
else:
859+
colorscale = make_colorscale(colorscale)
860+
861+
scale = colorscale_to_scale(colorscale)
862+
validate_scale_values(scale)
863+
colors = colorscale_to_colors(colorscale)
864+
colors = validate_colors(colors, colortype="tuple")
865+
866+
if isinstance(samplepoints, int):
867+
samplepoints = [
868+
low + idx / (samplepoints - 1) * (high - low) for idx in range(samplepoints)
869+
]
870+
elif isinstance(samplepoints, float):
871+
samplepoints = [samplepoints]
872+
873+
sampled_colors = []
874+
for point in samplepoints:
875+
high = bisect_left(scale, point)
876+
low = high - 1
877+
interpolant = (point - scale[low]) / (scale[high] - scale[low])
878+
sampled_color = find_intermediate_color(colors[low], colors[high], interpolant)
879+
sampled_colors.append(sampled_color)
880+
return validate_colors(sampled_colors, colortype=colortype)

Diff for: packages/python/plotly/plotly/colors/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"label_rgb",
3737
"make_colorscale",
3838
"n_colors",
39+
"sample_colorscale",
3940
"unconvert_from_RGB_255",
4041
"unlabel_rgb",
4142
"validate_colors",

Diff for: packages/python/plotly/plotly/tests/test_core/test_colors/test_colors.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ def test_get_colorscale(self):
149149
# test for non-existing colorscale
150150
pattern = r"Colorscale \S+ is not a built-in scale."
151151
name = "foo"
152-
153152
self.assertRaisesRegex(PlotlyError, pattern, colors.get_colorscale, name)
154153

155154
# test non-capitalised access
@@ -164,3 +163,34 @@ def test_get_colorscale(self):
164163
self.assertEqual(
165164
colors.diverging.Portland_r, colors.get_colorscale("portland_r")
166165
)
166+
167+
def test_sample_colorscale(self):
168+
169+
# test that sampling a colorscale at the defined points returns the same
170+
defined_colors = colors.sequential.Inferno
171+
sampled_colors = colors.sample_colorscale(
172+
defined_colors, len(defined_colors), colortype="rgb"
173+
)
174+
defined_colors_rgb = colors.convert_colors_to_same_type(
175+
defined_colors, colortype="rgb"
176+
)[0]
177+
self.assertEqual(sampled_colors, defined_colors_rgb)
178+
179+
# test sampling an easy colorscale that goes [red, green, blue]
180+
defined_colors = ["rgb(255,0,0)", "rgb(0,255,0)", "rgb(0,0,255)"]
181+
samplepoints = [0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0]
182+
expected_output = [
183+
(1.0, 0.0, 0.0),
184+
(0.75, 0.25, 0.0),
185+
(0.5, 0.5, 0.0),
186+
(0.25, 0.75, 0.0),
187+
(0.0, 1.0, 0.0),
188+
(0.0, 0.75, 0.25),
189+
(0.0, 0.5, 0.5),
190+
(0.0, 0.25, 0.75),
191+
(0.0, 0.0, 1.0),
192+
]
193+
output = colors.sample_colorscale(
194+
defined_colors, samplepoints, colortype="tuple"
195+
)
196+
self.assertEqual(expected_output, output)

0 commit comments

Comments
 (0)