Last active
March 11, 2021 09:29
-
-
Save monkut/6547cfec029574c9d111edfe1b95b121 to your computer and use it in GitHub Desktop.
HLS Color Scaling for legends
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from colorsys import rgb_to_hls, hls_to_rgb | |
class ScaledFloatColorManager: | |
""" | |
Intended to provide a scaled legend between to colors in the HSL space | |
""" | |
def __init__(self, name, minimum_value, maximum_value, hex_color_min="66b219", hex_color_max="cc0000", display_band_count=6): | |
self.name = name | |
self.minimum_value = minimum_value | |
self.maximum_value = maximum_value | |
self.hex_min_color = hex_color_min | |
self.hex_max_color = hex_color_max | |
self.display_band_count = display_band_count | |
def get_rgb_tuple(self, color): | |
""" | |
:param color: hex string of RGB color, ex: 'FFFFFF' | |
:returns: tuple of RGB color as integers between 0-255, ex: (255, 255, 255) | |
Convert hex string value to tuple of ints | |
'FFFFFF' --> (255, 255, 255) | |
""" | |
assert len(self.hex_min_color) == 6 | |
assert len(self.hex_max_color) == 6 | |
color_tuple = [] | |
for idx in range(0, 6, 2): | |
color_tuple.append(int(color[idx: idx+2], 16)) | |
return tuple(color_tuple) | |
def value_to_hsl(self, value, as_str=False): | |
""" | |
:param value: float value to convert to HSL color | |
:param as_str: Toggle to force resulting HSL color as a string in the form 'hsl({}, {}%, {}%)' | |
:type as_str: bool | |
:returns: HSL color as tuple or string | |
Convert the given value to the appropriate color | |
resulting color is represented in HSL (not rgb) | |
""" | |
if value < self.minimum_value: | |
value = self.minimum_value | |
elif value > self.maximum_value: | |
value = self.maximum_value | |
if self.minimum_value < 0: | |
offset = abs(self.minimum_value) | |
minimum_value = self.minimum_value + offset | |
maximum_value = self.maximum_value + offset | |
value = value + offset | |
else: | |
minimum_value = self.minimum_value | |
maximum_value = self.maximum_value | |
if all(i == 0 for i in (value, minimum_value, maximum_value)): | |
scale = 1.0 | |
else: | |
scale = float(value - minimum_value) / float(maximum_value - minimum_value) | |
# scale all channels linearly between START_COLOR and END_COLOR | |
start_rgb = self.get_rgb_tuple(self.hex_min_color) | |
end_rgb = self.get_rgb_tuple(self.hex_max_color) | |
# convert rgb to hsl | |
# --> put rgb values on scale between 0, and 1 for usage with colorsys conversion functions | |
# results in values 0-1 for all (h,l,s) | |
start_hls = rgb_to_hls(*[v/255.0 for v in start_rgb]) | |
end_hls = rgb_to_hls(*[v/255.0 for v in end_rgb]) | |
h, l, s = [float(scale * (end-start) + start) for start, end in zip(start_hls, end_hls)] | |
# adjust to expected scales 0-360, 0-100, 0-100 | |
h *= 360 | |
l *= 100 | |
s *= 100 | |
assert 0 <= h <= 360 | |
assert 0 <= l <= 100 | |
assert 0 <= s <= 100 | |
hsl_color = (int(h), int(s), int(l)) | |
if as_str: | |
hsl_color = "hsl({}, {}%, {}%)".format(*hsl_color) | |
return hsl_color | |
def value_to_rgb(self, value, htmlhex=False, max_rgb_value=255): | |
""" | |
:param value: float value to convert to RGB color | |
:param htmlhex: toggle to return value as html formatted hex, ex: '#FFFFFF' | |
:returns: RGB color as tuple or string | |
convert the given float value to rgb color | |
""" | |
# flooring value to the limits of the legend | |
if value < self.minimum_value: | |
value = self.minimum_value | |
elif value > self.maximum_value: | |
value = self.maximum_value | |
# hsl is used because it is easier to 'rotate' evenly to another color on the spectrum | |
h, s, l = self.value_to_hsl(value) | |
# adjust range to be from 0 to 1 change to hls for use with hls_to_rgb() | |
hls = (h/360.0, l/100.0, s/100.0) | |
# covert to rgb | |
if max_rgb_value == 255: | |
# --> adjust values from 0 to 1, to 0 to 255 | |
rgb = [int(i * 255) for i in hls_to_rgb(*hls)] | |
else: | |
rgb= hls_to_rgb(*hls) | |
if htmlhex: | |
rgb = "#" + "".join("{:02x}".format(i) for i in rgb) | |
return rgb | |
def get_color_str(self, model_instance, model_value_fieldname="value"): | |
""" | |
Method for supporting tmstiler Django view | |
:param model_instance: Model containting a value to 'colorize' | |
:param model_value_fieldname: fieldname in the model that holds the target 'value" | |
:return: Color as 'hsl()' string. | |
""" | |
value = getattr(model_instance, model_value_fieldname) | |
return self.value_to_hsl(value, as_str=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment