def test_stretched_renderer(tmpdir): data = numpy.zeros((100, 100)) for i in range(0, 100): data[i] = i colors = ( (data.min(), Color(255, 0, 0, 255)), (data.max(), Color(0, 0, 255, 255)) ) renderer = StretchedRenderer(colors) assert renderer.name == 'stretched' img = renderer.render_image(data) assert len(img.getpalette()) / 3 == 256 assert img.size == (100, 100) img.save(str(tmpdir.join("stretched.png"))) legend = renderer.get_legend(20, 20) assert len(legend) == 1 assert legend[0].image.size == (20, 20) legend[0].image.save(str(tmpdir.join("stretched_legend.png"))) legend = renderer.get_legend(20, 20, discrete_images=True) assert len(legend) == 2 assert legend[0].image.size == (20, 20) expected = { 'colors': [(0.0, '#F00'), (99.0, '#00F')], 'type': 'stretched', 'options': {'color_space': 'hsv'} } assert renderer.serialize() == expected
def test_classified_rendererer(tmpdir): data = numpy.zeros((100, 100)) for i in range(0, 100): data[i] = i colors = ( (10, Color(255, 0, 0, 255)), (50, Color(0, 255, 0, 255)), (data.max(), Color(0, 0, 255, 255)) ) renderer = ClassifiedRenderer(colors) assert renderer.name == 'classified' img = renderer.render_image(data) img.save(str(tmpdir.join("classified.png"))) assert img.palette.palette == b'\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00' assert img.size == (100, 100) legend = renderer.get_legend(20, 20) assert len(legend) == 3 for index, element in enumerate(legend): element.image.save(str(tmpdir.join("classified_legend_%i.png" % index))) expected = { 'colors': [(10, '#F00'), (50, '#0F0'), (99.0, '#00F')], 'type': 'classified' } assert renderer.serialize() == expected
def generate_unique_renderer(values, randomize_colors=False): if randomize_colors: r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) return UniqueValuesRenderer([(v[0], Color(r, g, b)) for v in values], labels=[v[1] for v in values]) else: return UniqueValuesRenderer( [(v[1], Color(*[int(c) for c in v[0].split(',')[1:]])) for v in values], labels=[v[2] for v in values])
def test_get_renderers_by_name(): data = numpy.zeros((100, 100)) for i in range(0, 100): data[i] = i colors = ( (10, Color(255, 0, 0, 255)), (50, Color(0, 255, 0, 255)), (data.max(), Color(0, 0, 255, 255)) ) renderer = get_renderer_by_name("classified")(colors) img = renderer.render_image(data) assert img.palette.palette == b'\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00' assert img.size == (100, 100)
def generate_stretched_renderer(info): v_min = None v_max = None for var in info['variables'].keys(): if var not in ['x', 'y', 'lat', 'lon', 'latitude', 'longitude']: variable = info['variables'][var] minimum = variable['min'] maximum = variable['max'] v_min = minimum if not v_min or minimum < v_min else v_min v_max = minimum if not v_max or maximum < v_max else v_max v_mid = (v_max - v_min) / 2 + v_min return StretchedRenderer([(v_min, Color(240, 59, 32)), (v_mid, Color(254, 178, 76)), (v_max, Color(255, 237, 160))])
def colormap_to_stretched_renderer(colormap, colorspace='hsv', filenames=None, variable=None, fill_value=None, mask=None): statistics = None if 'min:' in colormap or 'max:' in colormap or 'mean' in colormap: if not filenames and variable: raise ValueError( 'filenames and variable are required inputs to use colormap with statistics' ) statistics = collect_statistics(filenames, (variable, ), mask=mask)[variable] colors = [] for entry in colormap.split(','): value, color = entry.split(':') # TODO: add proportions of statistics if value in ('min', 'max', 'mean'): value = statistics[value] else: value = float(value) colors.append((value, Color.from_hex(color))) return StretchedRenderer(colors, colorspace=colorspace, fill_value=fill_value)
def palette_to_stretched_renderer(palette_path, values, filenames=None, variable=None, fill_value=None, mask=None): palette = get_palette(palette_path) values = values.split(',') if not len(values) > 1: raise ValueError( 'Must provide at least 2 values for palette-based stretched renderer' ) if 'min' in values or 'max' in values: if not filenames and variable: raise ValueError( 'filenames and variable are required inputs to use palette with statistics' ) statistics = collect_statistics(filenames, (variable, ), mask=mask)[variable] for statistic in ('min', 'max'): if statistic in values: values[values.index(statistic)] = statistics[statistic] values = [float(v) for v in values] # in case any are still strings hex_colors = palette.hex_colors # TODO: this only works cleanly for min:max or 2 endpoint values. Otherwise require that the number of palette colors match the number of values colors = [(values[0], Color.from_hex(hex_colors[0]))] intermediate_colors = hex_colors[1:-1] if intermediate_colors: interval = (values[-1] - values[0]) / (len(intermediate_colors) + 1) for i, color in enumerate(intermediate_colors): colors.append( (values[0] + (i + 1) * interval, Color.from_hex(color))) colors.append((values[-1], Color.from_hex(hex_colors[-1]))) return StretchedRenderer( colors, colorspace='rgb', fill_value=fill_value ) # I think all palettable palettes are in RGB ramps
def _palette_to_stretched_renderer(palette_path, values, filenames=None, variable=None): index = palette_path.rindex('.') palette = getattr( importlib.import_module('palettable.' + palette_path[:index]), palette_path[index + 1:]) values = values.split(',') if not len(values) > 1: raise ValueError( 'Must provide at least 2 values for palette-based stretched renderer' ) statistics = None if 'min' in values or 'max' in values: if not filenames and variable: raise ValueError( 'filenames and variable are required inputs to use palette with statistics' ) statistics = collect_statistics(filenames, (variable, ))[variable] for statistic in ('min', 'max'): if statistic in values: values[values.index(statistic)] = statistics[statistic] hex_colors = palette.hex_colors # TODO: this only works cleanly for min:max or 2 endpoint values. Otherwise require that the number of palette colors match the number of values colors = [(values[0], Color.from_hex(hex_colors[0]))] intermediate_colors = hex_colors[1:-1] if intermediate_colors: interval = (values[-1] - values[0]) / (len(intermediate_colors) + 1) for i, color in enumerate(intermediate_colors): colors.append( (values[0] + (i + 1) * interval, Color.from_hex(color))) colors.append((values[-1], Color.from_hex(hex_colors[-1]))) return StretchedRenderer( colors, colorspace='rgb') # I think all palettable palettes are in RGB ramps
def test_color(): color_tuple = (0, 0, 0) c = Color(*color_tuple) assert c.to_tuple() == color_tuple assert c.to_hex() == "#000" c2 = Color.from_hsv(*c.to_hsv()) assert c2.to_tuple() == color_tuple assert Color.from_hex("#000000", alpha=100)
def test_uniquevalues_renderer(tmpdir): data = numpy.zeros((100, 100)) data[10:25] = 10 data[35:50] = 25 data[50:75] = 50 data[85:100] = 100 colors = ( (10, Color(255, 0, 0, 255)), (25, Color(255, 255, 255, 255)), (50, Color(0, 255, 0, 255)), (100, Color(0, 0, 255, 255)) ) labels = ('A', 'B', 'C', 'D') renderer = UniqueValuesRenderer(colors, labels=labels) assert renderer.name == 'unique' img = renderer.render_image(data) img.save(str(tmpdir.join("unique.png"))) assert img.palette.palette == b'\xff\x00\x00\xff\xff\xff\x00\xff\x00\x00\x00\xff\x00\x00\x00' assert img.size == (100, 100) legend = renderer.get_legend(20, 20) assert len(legend) == 4 for index, element in enumerate(legend): element.image.save( str(tmpdir.join("uniquevalues_legend_%i.png" % index))) expected = { 'colors': [ (10, '#F00'), (25, '#FFF'), (50, '#0F0'), (100, '#00F')], 'type': 'unique', 'options': { 'labels': ('A', 'B', 'C', 'D') } } assert renderer.serialize() == expected
def get_polygon_image(self, polygons): im = Image.new('RGBA', self.image_size) if self._basemap_image is not None: im.paste(Image.blend(im, self._basemap_image, 1), (0, 0), self._basemap_image) for p in polygons: color = Color.from_hex( p['properties']['color']).to_tuple() if hasattr( p['properties'], 'color') else (0, 0, 255) self.draw_geometry(im, Geometry(str(p['geometry']))[0], color, 3) return self.crop_image(im)
def renderer_from_dict(renderer_dict): """Returns a renderer object from a dictionary object""" options = renderer_dict.get('options', {}) try: renderer_type = renderer_dict['type'] renderer_colors = [(float(x[0]), Color.from_hex(x[1])) for x in renderer_dict['colors']] fill_value = options.get('fill_value') if fill_value is not None: fill_value = float(fill_value) except KeyError: raise ValueError("Missing required keys from renderer renderer_dicturation") renderer_kwargs = { 'colormap': renderer_colors, 'fill_value': fill_value, 'background_color': Color(255, 255, 255, 0) } if renderer_type == "stretched": color_space = options.get('color_space', 'hsv').lower().strip() if not color_space in ('rgb', 'hsv'): raise ValueError("Invalid color space: {}".format(color_space)) renderer = StretchedRenderer(colorspace=color_space, **renderer_kwargs) elif renderer_type == "classified": renderer = ClassifiedRenderer(**renderer_kwargs) elif renderer_type == "unique": try: labels = [six.text_type(x) for x in options.get('labels', [])] except TypeError: raise ValueError("Labels option must be an array") renderer = UniqueValuesRenderer(labels=labels, **renderer_kwargs) return renderer
def palette_to_classified_renderer(palette_path, filenames, variable, method='equal', fill_value=None, mask=None): palette = get_palette(palette_path) num_breaks = palette.number colors = [Color(r, g, b) for (r, g, b) in palette.colors] if method == 'equal': statistics = collect_statistics(filenames, (variable, ), mask=mask)[variable] step = (statistics['max'] - statistics['min']) / num_breaks breaks = numpy.linspace(statistics['min'] + step, statistics['max'], num_breaks) return ClassifiedRenderer(zip(breaks, colors), fill_value=fill_value)
def __init__(self, colormap, fill_value, background_color): """ Construct a new renderer. :param colormap: [(value or class break, Color object)...] :param fill_value: value to fill with background color (if provided) or transparent :param background_color: the background color to apply to all areas not specifically handled by colormap, including areas with fill_value or masked out. """ if background_color is not None: assert isinstance(background_color, Color) else: background_color = Color(0, 0, 0, 0) self.colormap = list(colormap) self.fill_value = fill_value self.background_color = background_color self.colormap.sort(key=lambda x: x[0]) self.values = numpy.array([entry[0] for entry in self.colormap]) self._generate_palette()
import json from PIL import Image import numpy from clover.utilities.color import Color LEGEND_ELEMENT_BORDER_COLOR = Color(150, 150, 150, 0) LEGEND_ELEMENT_BORDER_WIDTH = 1 class RasterRenderer(object): def __init__(self, colormap, fill_value, background_color): """ Construct a new renderer. :param colormap: [(value or class break, Color object)...] :param fill_value: value to fill with background color (if provided) or transparent :param background_color: the background color to apply to all areas not specifically handled by colormap, including areas with fill_value or masked out. """ if background_color is not None: assert isinstance(background_color, Color) else: background_color = Color(0, 0, 0, 0) self.colormap = list(colormap) self.fill_value = fill_value self.background_color = background_color self.colormap.sort(key=lambda x: x[0]) self.values = numpy.array([entry[0] for entry in self.colormap])
import json import mercantile from clover.geometry.bbox import BBox from clover.utilities.color import Color from ncdjango.config import RenderConfiguration, ImageConfiguration, LegendConfiguration from ncdjango.views import GetImageViewBase, LegendViewBase from pyproj import Proj TILE_SIZE = (256, 256) TRANSPARENT_BACKGROUND_COLOR = Color(255, 255, 255, 0) class GetImageView(GetImageViewBase): def get_service_name(self, request, *args, **kwargs): return kwargs['service_name'] def get_render_configurations(self, request, **kwargs): tile_bounds = list( mercantile.bounds(int(self.kwargs['x']), int(self.kwargs['y']), int(self.kwargs['z']))) extent = BBox( tile_bounds, projection=Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs') ).project( Proj( '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs' )) base_config = ImageConfiguration( extent=extent,
def _parse_colormap(colormap_str): colormap = [] for entry in colormap_str.split(','): value, color = entry.split(':') colormap.append((float(value), Color.from_hex(color))) return colormap