def test_set_colors(): mc = MapCanvas((400, 300)) colors = [('blue', (0, 0, 255)), ('red', (255, 0, 0))] mc.add_colors(colors) assert mc.fore_image.get_color_names() == mc.back_image.get_color_names() colors = mc.get_color_names() assert colors == ['transparent', 'black', 'white', 'blue', 'red']
def __init__(self, ice_movers=None, image_size=(800, 600), projection=None, viewport=None, **kwargs): ''' :param ice_movers: ice_movers associated with this outputter. :type ice_movers: An ice_mover object or sequence of ice_mover objects. Use super to pass optional \*\*kwargs to base class __init__ method ''' # this is a place where we store our gradient color infomration self.gradient_lu = {} self.map_canvas = MapCanvas(image_size, projection=projection, viewport=viewport, preset_colors='transparent') self.map_canvas.add_colors([('black', (0, 0, 0))]) self.set_gradient_colors( 'thickness', color_range=( (0, 0, 0x7f, 0x7f), # dark blue (0, 0, 0x7f, 0x3f), # dark blue (0, 0, 0x7f, 0x00), # dark blue (0xff, 0, 0, 0x00)), # red scale=(0.0, 6.0), num_colors=64) self.set_gradient_colors( 'concentration', color_range=( (0x80, 0xc0, 0xd0, 0x7f), # sky blue (0, 0x40, 0x60, 0x00)), # dark blue scale=(0.0, 1.0), num_colors=64) super(IceImageOutput, self).__init__(**kwargs) if (isinstance(ice_movers, collections.Iterable) and not isinstance(ice_movers, str)): self.ice_movers = ice_movers elif ice_movers is not None: self.ice_movers = (ice_movers, ) else: self.ice_movers = tuple()
def test_foreground_poly(output_dir): """ test drawing polygons to the foreground """ mc = MapCanvas((400, 300), preset_colors='web') mc.background_color = 'transparent' mc.clear_background() mc.draw_polygon(((-30, 30), (30, 30), (30, -20), (-30, -20)), fill_color='white', line_color='black', background=False) mc.save_foreground(os.path.join(output_dir, "foreground.png"))
def test_foreground_polyline(output_dir): """ test drawing polygons to the background """ mc = MapCanvas((400, 300), preset_colors='web') mc.background_color = 'transparent' mc.clear_background() mc.draw_polyline(((-30, 30), (30, 30), (30, -20), (-30, -20)), line_color='red', line_width=5, background=False) mc.save_foreground(os.path.join(output_dir, "foreground_polyline.png"))
def test_background_poly(output_dir): """ test drawing polygons to the background """ mc = MapCanvas((400, 300), preset_colors='web') mc.background_color = 'transparent' mc.clear_background() mc.draw_polygon(((-30, 30), (30, 30), (30, -20), (-30, -20)), fill_color='blue', line_color='black', background=True) mc.save_background(os.path.join(output_dir, "background.png"))
def __init__(self, ice_movers=None, image_size=(800, 600), projection=None, viewport=None, **kwargs): ''' :param ice_movers: ice_movers associated with this outputter. :type ice_movers: An ice_mover object or sequence of ice_mover objects. Use super to pass optional \*\*kwargs to base class __init__ method ''' # this is a place where we store our gradient color infomration self.gradient_lu = {} self.map_canvas = MapCanvas(image_size, projection=projection, viewport=viewport, preset_colors='transparent') self.map_canvas.add_colors([('black', (0, 0, 0))]) self.set_gradient_colors('thickness', color_range=((0, 0, 0x7f, 0x7f), # dark blue (0, 0, 0x7f, 0x3f), # dark blue (0, 0, 0x7f, 0x00), # dark blue (0xff, 0, 0, 0x00)), # red scale=(0.0, 6.0), num_colors=64) self.set_gradient_colors('concentration', color_range=((0x80, 0xc0, 0xd0, 0x7f), # sky blue (0, 0x40, 0x60, 0x00)), # dark blue scale=(0.0, 1.0), num_colors=64) super(IceImageOutput, self).__init__(**kwargs) if (isinstance(ice_movers, collections.Iterable) and not isinstance(ice_movers, str)): self.ice_movers = ice_movers elif ice_movers is not None: self.ice_movers = (ice_movers,) else: self.ice_movers = tuple()
def test_copy_back_to_fore(output_dir): mc = MapCanvas((400, 300), preset_colors='web') mc.background_color = 'white' mc.clear_background() mc.clear_foreground() mc.draw_polyline(((-30, 30), (30, 30), (30, -20), (-30, -20)), line_color='red', line_width=5, background=True) mc.copy_back_to_fore() mc.draw_polyline(((-20, 20), (20, 20), (20, -10), (-20, -10)), line_color='blue', line_width=5, background=False) mc.save_foreground(os.path.join(output_dir, "copy_back_to_fore.png"))
class IceImageOutput(Outputter): ''' Class that outputs ice data as an image for each ice mover. The image is PNG encoded, then Base64 encoded to include in a JSON response. ''' _schema = IceImageSchema def __init__(self, ice_movers=None, image_size=(800, 600), projection=None, viewport=None, **kwargs): ''' :param ice_movers: ice_movers associated with this outputter. :type ice_movers: An ice_mover object or sequence of ice_mover objects. Use super to pass optional \*\*kwargs to base class __init__ method ''' # this is a place where we store our gradient color infomration self.gradient_lu = {} self.map_canvas = MapCanvas(image_size, projection=projection, viewport=viewport, preset_colors='transparent') self.map_canvas.add_colors([('black', (0, 0, 0))]) self.set_gradient_colors( 'thickness', color_range=( (0, 0, 0x7f, 0x7f), # dark blue (0, 0, 0x7f, 0x3f), # dark blue (0, 0, 0x7f, 0x00), # dark blue (0xff, 0, 0, 0x00)), # red scale=(0.0, 6.0), num_colors=64) self.set_gradient_colors( 'concentration', color_range=( (0x80, 0xc0, 0xd0, 0x7f), # sky blue (0, 0x40, 0x60, 0x00)), # dark blue scale=(0.0, 1.0), num_colors=64) super(IceImageOutput, self).__init__(**kwargs) if (isinstance(ice_movers, collections.Iterable) and not isinstance(ice_movers, str)): self.ice_movers = ice_movers elif ice_movers is not None: self.ice_movers = (ice_movers, ) else: self.ice_movers = tuple() def set_gradient_colors( self, gradient_name, color_range=( (0, 0, 0x7f), # dark blue (0, 0xff, 0xff)), # cyan scale=(0.0, 10.0), num_colors=16): ''' Add a color gradient to our palette representing the colors we will use for our ice thickness :param gradient_name: The name of the gradient. :type gradient_name: str :param color_range: The colors we will build our gradient with. :type color_range: A 2 element sequence of 3-tuples containing 8-bit RGB values. :param scale: A range of values representing the low and high end of our gradient. :type scale: A 2 element sequence of float :param num_colors: The number of colors to use for the gradient. :type num_colors: Number ''' color_names = self.add_gradient_to_canvas(color_range, gradient_name, num_colors) self.gradient_lu[gradient_name] = (scale, np.array(color_names)) def add_gradient_to_canvas(self, color_range, color_prefix, num_colors): ''' Add a color gradient to our palette NOTE: Probably not the most efficient way to do this. :param color_range: The colors that we would like to use to generate our gradient :type color_range: A sequence of 2 or more 3-tuples :param color_prefix: The prefix that will be used in the naming of the colors in the gradient :type color_prefix: str :param num_colors: The number of gradient colors to generate :type num_colors: Number ''' color_range_idx = range(len(color_range)) color_space = np.linspace(color_range_idx[0], color_range_idx[-1], num=num_colors) r_grad = np.interp(color_space, color_range_idx, [c[0] for c in color_range]) g_grad = np.interp(color_space, color_range_idx, [c[1] for c in color_range]) b_grad = np.interp(color_space, color_range_idx, [c[2] for c in color_range]) if all([len(c) >= 4 for c in color_range]): a_grad = np.interp(color_space, color_range_idx, [c[3] for c in color_range]) else: a_grad = np.array([0.] * num_colors) new_colors = [] for i, (r, g, b, a) in enumerate(zip(r_grad, g_grad, b_grad, a_grad)): new_colors.append(('{}{}'.format(color_prefix, i), (r, g, b, a))) self.map_canvas.add_colors(new_colors) return [c[0] for c in new_colors] def lookup_gradient_color(self, gradient_name, values): try: (low_val, high_val), color_names = self.gradient_lu[gradient_name] except IndexError: return None scale_range = high_val - low_val q_step_range = scale_range / len(color_names) idx = (np.floor(values / q_step_range).astype(int).clip( 0, len(color_names) - 1)) return color_names[idx] def write_output(self, step_num, islast_step=False): """ Generate image from data """ # I don't think we need this for this outputter: # - it does stuff with cache initialization super(IceImageOutput, self).write_output(step_num, islast_step) if (self.on is False or not self._write_step or len(self.ice_movers) == 0): return None # fixme -- doing all this cache stuff just to get the timestep.. # maybe timestep should be passed in. for sc in self.cache.load_timestep(step_num).items(): model_time = date_to_sec(sc.current_time_stamp) iso_time = sc.current_time_stamp.isoformat() thick_image, conc_image, bb = self.render_images(model_time) # web_mercator = 'EPSG:3857' equirectangular = 'EPSG:32662' # info to return to the caller output_dict = { 'step_num': step_num, 'time_stamp': iso_time, 'thickness_image': thick_image, 'concentration_image': conc_image, 'bounding_box': bb, 'projection': equirectangular, } return output_dict def get_sample_image(self): """ This returns a base 64 encoded PNG image for testing, just so we have something This should be removed when we have real functionality """ # hard-coding the base64 really confused my editor.. image_file_file_path = os.path.join( os.path.split(__file__)[0], 'sample.b64') return open(image_file_file_path).read() def render_images(self, model_time): """ render the actual images This uses the MapCanvas code to do the actual rendering returns: thickness_image, concentration_image """ canvas = self.map_canvas # We kinda need to figure our our bounding box before doing the # rendering. We will try to be efficient about it mainly by not # grabbing our grid data twice. mover_grid_bb = None mover_grids = [] for mover in self.ice_movers: mover_grids.append(mover.get_grid_data()) mover_grid_bb = mover.get_grid_bounding_box( mover_grids[-1], mover_grid_bb) canvas.viewport = mover_grid_bb canvas.clear_background() # Here is where we draw our grid data.... for mover, mover_grid in zip(self.ice_movers, mover_grids): mover_grid_bb = mover.get_grid_bounding_box( mover_grid, mover_grid_bb) concentration, thickness = mover.get_ice_fields(model_time) thickness_colors = self.lookup_gradient_color( 'thickness', thickness) concentration_colors = self.lookup_gradient_color( 'concentration', concentration) dtype = mover_grid.dtype.descr unstructured_type = dtype[0][1] new_shape = mover_grid.shape + (len(dtype), ) mover_grid = (mover_grid.view(dtype=unstructured_type).reshape( *new_shape)) for poly, tc, cc in zip(mover_grid, thickness_colors, concentration_colors): canvas.draw_polygon(poly, fill_color=tc) canvas.draw_polygon(poly, fill_color=cc, background=True) # py_gd does not currently have the capability to generate a .png # formatted buffer in memory. (libgd can be made to do this, but # the wrapper is yet to be written) # So we will just write to a tempfile and then read it back. # If we ever have to do this anywhere else, a context manger would be good. tempdir = tempfile.mkdtemp() tempfilename = os.path.join(tempdir, "gnome_temp_image_file.png") canvas.save_foreground(tempfilename) thickness_image = open(tempfilename, 'rb').read().encode('base64') canvas.save_background(tempfilename) coverage_image = open(tempfilename, 'rb').read().encode('base64') os.remove(tempfilename) os.rmdir(tempdir) return ("data:image/png;base64,{}".format(thickness_image), "data:image/png;base64,{}".format(coverage_image), mover_grid_bb) def ice_movers_to_dict(self): ''' a dict containing 'obj_type' and 'id' for each object in list/collection ''' return self._collection_to_dict(self.ice_movers)
def __init__( self, filename, images_dir, image_size=(800, 600), cache=None, output_timestep=None, output_zero_step=True, output_last_step=True, draw_ontop='forecast', **kwargs ): """ Init the image renderer. Following args are passed to base class Outputter's init: :param cache: sets the cache object from which to read data. The model will automatically set this param :param output_timestep: default is None in which case everytime the write_output is called, output is written. If set, then output is written every output_timestep starting from model_start_time. :type output_timestep: timedelta object :param output_zero_step: default is True. If True then output for initial step (showing initial release conditions) is written regardless of output_timestep :type output_zero_step: boolean :param output_last_step: default is True. If True then output for final step is written regardless of output_timestep :type output_last_step: boolean :param draw_ontop: draw 'forecast' or 'uncertain' LEs on top. Default is to draw 'forecast' LEs, which are in black on top :type draw_ontop: str Remaining kwargs are passed onto baseclass's __init__ with a direct call: MapCanvas.__init__(..) Optional parameters (kwargs) :param projection_class: gnome.utilities.projections class to use. Default is gnome.utilities.projections.FlatEarthProjection :param map_BB: map bounding box. Default is to use land_polygons.bounding_box. If land_polygons is None, then this is the whole world, defined by ((-180,-90),(180, 90)) :param viewport: viewport of map -- what gets drawn and on what scale. Default is to set viewport = map_BB :param image_mode: Image mode ('P' for palette or 'L' for Black and White image). BW_MapCanvas inherits from MapCanvas and sets the mode to 'L'. Default image_mode is 'P'. :param id: unique identifier for a instance of this class (UUID given as a string). This is used when loading an object from a persisted model. """ # set up the canvas self._filename = filename polygons = haz_files.ReadBNA(filename, 'PolygonSet') Outputter.__init__(self, cache, output_timestep, output_zero_step, output_last_step) MapCanvas.__init__(self, image_size, land_polygons=polygons, **kwargs) self.images_dir = images_dir self.last_filename = '' self.draw_ontop = draw_ontop
def test_projection(output_dir): """ draw the "same sized" rectangle at three latitudes to see how the look """ mc = MapCanvas((400, 400), preset_colors='web') mc.viewport = ((-20, 25), (20, 65)) mc.draw_polygon(((-15, 60), (15, 60), (15, 30), (-15, 30)), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_north.png")) mc.viewport = ((-20, -20), (20, 20)) mc.draw_polygon(((-15, 15), (15, 15), (15, -15), (-15, -15)), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_equator.png")) mc.viewport = ((-20, -45), (20, -90)) mc.draw_polygon(((-15, -80), (15, -80), (15, -50), (-15, -50)), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_south.png"))
def test_init(): """ can we even initialize one """ mc = MapCanvas((400, 300))
class IceImageOutput(Outputter): ''' Class that outputs ice data as an image for each ice mover. The image is PNG encoded, then Base64 encoded to include in a JSON response. ''' _state = copy.deepcopy(Outputter._state) # need a schema and also need to override save so output_dir # is saved correctly - maybe point it to saveloc _state.add_field(Field('ice_movers', save=True, update=True, iscollection=True)) _schema = IceImageSchema def __init__(self, ice_movers=None, image_size=(800, 600), projection=None, viewport=None, **kwargs): ''' :param ice_movers: ice_movers associated with this outputter. :type ice_movers: An ice_mover object or sequence of ice_mover objects. Use super to pass optional \*\*kwargs to base class __init__ method ''' # this is a place where we store our gradient color infomration self.gradient_lu = {} self.map_canvas = MapCanvas(image_size, projection=projection, viewport=viewport, preset_colors='transparent') self.map_canvas.add_colors([('black', (0, 0, 0))]) self.set_gradient_colors('thickness', color_range=((0, 0, 0x7f, 0x7f), # dark blue (0, 0, 0x7f, 0x3f), # dark blue (0, 0, 0x7f, 0x00), # dark blue (0xff, 0, 0, 0x00)), # red scale=(0.0, 6.0), num_colors=64) self.set_gradient_colors('concentration', color_range=((0x80, 0xc0, 0xd0, 0x7f), # sky blue (0, 0x40, 0x60, 0x00)), # dark blue scale=(0.0, 1.0), num_colors=64) super(IceImageOutput, self).__init__(**kwargs) if (isinstance(ice_movers, collections.Iterable) and not isinstance(ice_movers, str)): self.ice_movers = ice_movers elif ice_movers is not None: self.ice_movers = (ice_movers,) else: self.ice_movers = tuple() def set_gradient_colors(self, gradient_name, color_range=((0, 0, 0x7f), # dark blue (0, 0xff, 0xff)), # cyan scale=(0.0, 10.0), num_colors=16): ''' Add a color gradient to our palette representing the colors we will use for our ice thickness :param gradient_name: The name of the gradient. :type gradient_name: str :param color_range: The colors we will build our gradient with. :type color_range: A 2 element sequence of 3-tuples containing 8-bit RGB values. :param scale: A range of values representing the low and high end of our gradient. :type scale: A 2 element sequence of float :param num_colors: The number of colors to use for the gradient. :type num_colors: Number ''' color_names = self.add_gradient_to_canvas(color_range, gradient_name, num_colors) self.gradient_lu[gradient_name] = (scale, np.array(color_names)) def add_gradient_to_canvas(self, color_range, color_prefix, num_colors): ''' Add a color gradient to our palette NOTE: Probably not the most efficient way to do this. :param color_range: The colors that we would like to use to generate our gradient :type color_range: A sequence of 2 or more 3-tuples :param color_prefix: The prefix that will be used in the naming of the colors in the gradient :type color_prefix: str :param num_colors: The number of gradient colors to generate :type num_colors: Number ''' color_range_idx = range(len(color_range)) color_space = np.linspace(color_range_idx[0], color_range_idx[-1], num=num_colors) r_grad = np.interp(color_space, color_range_idx, [c[0] for c in color_range]) g_grad = np.interp(color_space, color_range_idx, [c[1] for c in color_range]) b_grad = np.interp(color_space, color_range_idx, [c[2] for c in color_range]) if all([len(c) >= 4 for c in color_range]): a_grad = np.interp(color_space, color_range_idx, [c[3] for c in color_range]) else: a_grad = np.array([0.] * num_colors) new_colors = [] for i, (r, g, b, a) in enumerate(zip(r_grad, g_grad, b_grad, a_grad)): new_colors.append(('{}{}'.format(color_prefix, i), (r, g, b, a))) self.map_canvas.add_colors(new_colors) return [c[0] for c in new_colors] def lookup_gradient_color(self, gradient_name, values): try: (low_val, high_val), color_names = self.gradient_lu[gradient_name] except IndexError: return None scale_range = high_val - low_val q_step_range = scale_range / len(color_names) idx = (np.floor(values / q_step_range) .astype(int) .clip(0, len(color_names) - 1)) return color_names[idx] def write_output(self, step_num, islast_step=False): """ Generate image from data """ # I don't think we need this for this outputter: # - it does stuff with cache initialization super(IceImageOutput, self).write_output(step_num, islast_step) if (self.on is False or not self._write_step or len(self.ice_movers) == 0): return None # fixme -- doing all this cache stuff just to get the timestep.. # maybe timestep should be passed in. for sc in self.cache.load_timestep(step_num).items(): model_time = date_to_sec(sc.current_time_stamp) iso_time = sc.current_time_stamp.isoformat() thick_image, conc_image, bb = self.render_images(model_time) # info to return to the caller web_mercator = 'EPSG:3857' equirectangular = 'EPSG:32662' output_dict = {'step_num': step_num, 'time_stamp': iso_time, 'thickness_image': thick_image, 'concentration_image': conc_image, 'bounding_box': bb, 'projection': equirectangular, } return output_dict def get_sample_image(self): """ This returns a base 64 encoded PNG image for testing, just so we have something This should be removed when we have real functionality """ # hard-coding the base64 really confused my editor.. image_file_file_path = os.path.join(os.path.split(__file__)[0], 'sample.b64') return open(image_file_file_path).read() def render_images(self, model_time): """ render the actual images This uses the MapCanvas code to do the actual rendering returns: thickness_image, concentration_image """ canvas = self.map_canvas # We kinda need to figure our our bounding box before doing the # rendering. We will try to be efficient about it mainly by not # grabbing our grid data twice. mover_grid_bb = None mover_grids = [] for mover in self.ice_movers: mover_grids.append(mover.get_grid_data()) mover_grid_bb = mover.get_grid_bounding_box(mover_grids[-1], mover_grid_bb) canvas.viewport = mover_grid_bb canvas.clear_background() # Here is where we draw our grid data.... for mover, mover_grid in zip(self.ice_movers, mover_grids): mover_grid_bb = mover.get_grid_bounding_box(mover_grid, mover_grid_bb) concentration, thickness = mover.get_ice_fields(model_time) thickness_colors = self.lookup_gradient_color('thickness', thickness) concentration_colors = self.lookup_gradient_color('concentration', concentration) dtype = mover_grid.dtype.descr unstructured_type = dtype[0][1] new_shape = mover_grid.shape + (len(dtype),) mover_grid = (mover_grid .view(dtype=unstructured_type) .reshape(*new_shape)) for poly, tc, cc in zip(mover_grid, thickness_colors, concentration_colors): canvas.draw_polygon(poly, fill_color=tc) canvas.draw_polygon(poly, fill_color=cc, background=True) # diagnostic so we can see what we have rendered. # print '\ndrawing reference objects...' # canvas.draw_graticule(False) # canvas.draw_tags(False) # canvas.save_background('background.png') # canvas.save_foreground('foreground.png') # py_gd does not currently have the capability to generate a .png # formatted buffer in memory. (libgd can be made to do this, but # the wrapper is yet to be written) # So we will just write to a tempfile and then read it back. with NamedTemporaryFile() as fp: canvas.save_foreground(fp.name) fp.seek(0) thickness_image = fp.read().encode('base64') with NamedTemporaryFile() as fp: canvas.save_background(fp.name) fp.seek(0) coverage_image = fp.read().encode('base64') return ("data:image/png;base64,{}".format(thickness_image), "data:image/png;base64,{}".format(coverage_image), mover_grid_bb) def rewind(self): 'remove previously written files' super(IceImageOutput, self).rewind() def ice_movers_to_dict(self): ''' a dict containing 'obj_type' and 'id' for each object in list/collection ''' return self._collection_to_dict(self.ice_movers) @classmethod def deserialize(cls, json_): """ append correct schema for current mover """ schema = cls._schema() _to_dict = schema.deserialize(json_) if 'ice_movers' in json_: _to_dict['ice_movers'] = [] for i, cm in enumerate(json_['ice_movers']): cm_cls = class_from_objtype(cm['obj_type']) cm_dict = cm_cls.deserialize(json_['ice_movers'][i]) _to_dict['ice_movers'].append(cm_dict) return _to_dict
def __init__(self, map_filename=None, output_dir='./', image_size=(800, 600), projection=None, viewport=None, map_BB=None, land_polygons=None, draw_back_to_fore=True, draw_map_bounds=False, draw_spillable_area=False, formats=['png', 'gif'], draw_ontop='forecast', cache=None, output_timestep=None, output_zero_step=True, output_last_step=True, output_start_time=None, on=True, timestamp_attrib={}, **kwargs): """ Init the image renderer. :param str map_filename=None: name of file for basemap (BNA) :type map_filename: str :param str output_dir='./': directory to output the images :param 2-tuple image_size=(800, 600): size of images to output :param projection=None: projection instance to use: If None, set to projections.FlatEarthProjection() :type projection: a gnome.utilities.projection.Projection instance :param viewport: viewport of map -- what gets drawn and on what scale. Default is full globe: (((-180, -90), (180, 90))) If not specifies, it will be set to the map's bounds. :type viewport: pair of (lon, lat) tuples ( lower_left, upper right ) :param map_BB=None: bounding box of map if None, it will use the bounding box of the mapfile. :param draw_back_to_fore=True: draw the background (map) to the foregound image when outputting the images each time step. :type draw_back_to_fore: boolean :param formats=['gif']: list of formats to output. :type formats: string or list of strings. Options are: ['bmp', 'jpg', 'jpeg', 'gif', 'png'] :param draw_ontop: draw 'forecast' or 'uncertain' LEs on top. Default is to draw 'forecast' LEs, which are in black on top :type draw_ontop: str Following args are passed to base class Outputter's init: :param cache: sets the cache object from which to read prop. The model will automatically set this param :param output_timestep: default is None in which case everytime the write_output is called, output is written. If set, then output is written every output_timestep starting from model_start_time. :type output_timestep: timedelta object :param output_zero_step: default is True. If True then output for initial step (showing initial release conditions) is written regardless of output_timestep :type output_zero_step: boolean :param output_last_step: default is True. If True then output for final step is written regardless of output_timestep :type output_last_step: boolean Remaining kwargs are passed onto baseclass's __init__ with a direct call: Outputter.__init__(..) """ projection = (projections.FlatEarthProjection() if projection is None else projection) # set up the canvas self.map_filename = map_filename self.output_dir = output_dir if map_filename is not None and land_polygons is None: self.land_polygons = haz_files.ReadBNA(map_filename, 'PolygonSet') elif land_polygons is not None: self.land_polygons = land_polygons else: self.land_polygons = [] # empty list so we can loop thru it self.last_filename = '' self.draw_ontop = draw_ontop self.draw_back_to_fore = draw_back_to_fore Outputter.__init__(self, cache, on, output_timestep, output_zero_step, output_last_step, output_start_time, output_dir, **kwargs) if map_BB is None: if not self.land_polygons: map_BB = ((-180, -90), (180, 90)) else: map_BB = self.land_polygons.bounding_box self.map_BB = map_BB viewport = self.map_BB if viewport is None else viewport MapCanvas.__init__(self, image_size, projection=projection, viewport=viewport) # assorted rendering flags: self.draw_map_bounds = draw_map_bounds self.draw_spillable_area = draw_spillable_area self.raster_map = None self.raster_map_fill = True self.raster_map_outline = False # initilize the images: self.add_colors(self.map_colors) self.background_color = 'background' if self.map_filename is not None: file_prefix = os.path.splitext(self.map_filename)[0] sep = '_' else: file_prefix = sep = '' fn = '{}{}anim.gif'.format(file_prefix, sep) self.anim_filename = os.path.join(output_dir, fn) self.formats = formats self.delay = 50 self.repeat = True self.timestamp_attribs = {} self.set_timestamp_attrib(**timestamp_attrib) self.grids = [] self.props = []
def __init__( self, filename=None, images_dir="./", image_size=(800, 600), cache=None, output_timestep=None, output_zero_step=True, output_last_step=True, draw_ontop="forecast", draw_back_to_fore=True, **kwargs ): """ Init the image renderer. Following args are passed to base class Outputter's init: :param filename: the name of the image file :param images_dir: the folder in which to write the image :param image_size: the width and height of the image :param cache: sets the cache object from which to read data. The model will automatically set this param :param output_timestep: default is None in which case everytime the write_output is called, output is written. If set, then output is written every output_timestep starting from model_start_time. :type output_timestep: timedelta object :param output_zero_step: default is True. If True then output for initial step (showing initial release conditions) is written regardless of output_timestep :type output_zero_step: boolean :param output_last_step: default is True. If True then output for final step is written regardless of output_timestep :type output_last_step: boolean :param draw_ontop: draw 'forecast' or 'uncertain' LEs on top. Default is to draw 'forecast' LEs, which are in black on top :type draw_ontop: str :param draw_back_to_fore=True: draw the background (map) to the foregound image when drawing Elements. :type draw_ontop: boolean Remaining kwargs are passed onto baseclass's __init__ with a direct call: MapCanvas.__init__(..) Optional parameters (kwargs) :param projection_class: gnome.utilities.projections class to use. Default is gnome.utilities.projections.FlatEarthProjection :param map_BB: map bounding box. Default is to use land_polygons.bounding_box. If land_polygons is None, then this is the whole world, defined by ((-180,-90),(180, 90)) :param viewport: viewport of map -- what gets drawn and on what scale. Default is to set viewport = map_BB :param image_mode: Image mode ('P' for palette or 'L' for Black and White image). BW_MapCanvas inherits from MapCanvas and sets the mode to 'L'. Default image_mode is 'P'. """ # set up the canvas self._filename = filename if filename is not None: polygons = haz_files.ReadBNA(filename, "PolygonSet") else: polygons = None self.images_dir = images_dir self.last_filename = "" self.draw_ontop = draw_ontop self.draw_back_to_fore = draw_back_to_fore Outputter.__init__( self, cache, kwargs.pop("on", True), output_timestep, output_zero_step, output_last_step, kwargs.pop("name", None), ) MapCanvas.__init__(self, image_size, land_polygons=polygons, **kwargs)
def test_projection(output_dir): """ draw the "same sized" rectangle at three latitudes to see how the look """ mc = MapCanvas((400, 400), preset_colors='web') mc.viewport = ((-20, 25), (20, 65)) mc.draw_polygon(((-15, 60), (15, 60), (15, 30), (-15, 30)), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_north.png")) mc.viewport = ((-20, -20), (20, 20)) mc.draw_polygon(((-15, 15), (15, 15), (15, -15), (-15, -15) ), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_equator.png")) mc.viewport = ((-20, -45), (20, -90)) mc.draw_polygon(((-15, -80), (15, -80), (15, -50), (-15, -50)), fill_color='maroon', line_color='black') mc.save_foreground(os.path.join(output_dir, "image_projection_south.png"))
def __init__(self, map_filename=None, output_dir='./', image_size=(800, 600), projection=None, viewport=None, map_BB=None, land_polygons=None, draw_back_to_fore=True, draw_map_bounds=False, draw_spillable_area=False, formats=['png', 'gif'], draw_ontop='forecast', cache=None, output_timestep=None, output_zero_step=True, output_last_step=True, output_start_time=None, on=True, timestamp_attrib={}, **kwargs ): """ Init the image renderer. :param str map_filename=None: name of file for basemap (BNA) :type map_filename: str :param str output_dir='./': directory to output the images :param 2-tuple image_size=(800, 600): size of images to output :param projection=None: projection instance to use: If None, set to projections.FlatEarthProjection() :type projection: a gnome.utilities.projection.Projection instance :param viewport: viewport of map -- what gets drawn and on what scale. Default is full globe: (((-180, -90), (180, 90))) If not specifies, it will be set to the map's bounds. :type viewport: pair of (lon, lat) tuples ( lower_left, upper right ) :param map_BB=None: bounding box of map if None, it will use the bounding box of the mapfile. :param draw_back_to_fore=True: draw the background (map) to the foregound image when outputting the images each time step. :type draw_back_to_fore: boolean :param formats=['gif']: list of formats to output. :type formats: string or list of strings. Options are: ['bmp', 'jpg', 'jpeg', 'gif', 'png'] :param draw_ontop: draw 'forecast' or 'uncertain' LEs on top. Default is to draw 'forecast' LEs, which are in black on top :type draw_ontop: str Following args are passed to base class Outputter's init: :param cache: sets the cache object from which to read prop. The model will automatically set this param :param output_timestep: default is None in which case everytime the write_output is called, output is written. If set, then output is written every output_timestep starting from model_start_time. :type output_timestep: timedelta object :param output_zero_step: default is True. If True then output for initial step (showing initial release conditions) is written regardless of output_timestep :type output_zero_step: boolean :param output_last_step: default is True. If True then output for final step is written regardless of output_timestep :type output_last_step: boolean Remaining kwargs are passed onto baseclass's __init__ with a direct call: Outputter.__init__(..) """ projection = (projections.FlatEarthProjection() if projection is None else projection) # set up the canvas self.map_filename = map_filename self.output_dir = output_dir if map_filename is not None and land_polygons is None: self.land_polygons = haz_files.ReadBNA(map_filename, 'PolygonSet') elif land_polygons is not None: self.land_polygons = land_polygons else: self.land_polygons = [] # empty list so we can loop thru it self.last_filename = '' self.draw_ontop = draw_ontop self.draw_back_to_fore = draw_back_to_fore Outputter.__init__(self, cache, on, output_timestep, output_zero_step, output_last_step, output_start_time, output_dir, **kwargs) if map_BB is None: if not self.land_polygons: map_BB = ((-180, -90), (180, 90)) else: map_BB = self.land_polygons.bounding_box self.map_BB = map_BB viewport = self.map_BB if viewport is None else viewport MapCanvas.__init__(self, image_size, projection=projection, viewport=viewport) # assorted rendering flags: self.draw_map_bounds = draw_map_bounds self.draw_spillable_area = draw_spillable_area self.raster_map = None self.raster_map_fill = True self.raster_map_outline = False # initilize the images: self.add_colors(self.map_colors) self.background_color = 'background' if self.map_filename is not None: file_prefix = os.path.splitext(self.map_filename)[0] sep = '_' else: file_prefix = sep = '' fn = '{}{}anim.gif'.format(file_prefix, sep) self.anim_filename = os.path.join(output_dir, fn) self.formats = formats self.delay = 50 self.repeat = True self.timestamp_attribs = {} self.set_timestamp_attrib(**timestamp_attrib) self.grids = [] self.props = []
def __init__(self, filename=None, images_dir='./', image_size=(800, 600), cache=None, output_timestep=None, output_zero_step=True, output_last_step=True, draw_ontop='forecast', draw_back_to_fore=True, **kwargs): """ Init the image renderer. Following args are passed to base class Outputter's init: :param filename: the name of the image file :param images_dir: the folder in which to write the image :param image_size: the width and height of the image :param cache: sets the cache object from which to read data. The model will automatically set this param :param output_timestep: default is None in which case everytime the write_output is called, output is written. If set, then output is written every output_timestep starting from model_start_time. :type output_timestep: timedelta object :param output_zero_step: default is True. If True then output for initial step (showing initial release conditions) is written regardless of output_timestep :type output_zero_step: boolean :param output_last_step: default is True. If True then output for final step is written regardless of output_timestep :type output_last_step: boolean :param draw_ontop: draw 'forecast' or 'uncertain' LEs on top. Default is to draw 'forecast' LEs, which are in black on top :type draw_ontop: str :param draw_back_to_fore=True: draw the background (map) to the foregound image when drawing Elements. :type draw_ontop: boolean Remaining kwargs are passed onto baseclass's __init__ with a direct call: MapCanvas.__init__(..) Optional parameters (kwargs) :param projection_class: gnome.utilities.projections class to use. Default is gnome.utilities.projections.FlatEarthProjection :param map_BB: map bounding box. Default is to use land_polygons.bounding_box. If land_polygons is None, then this is the whole world, defined by ((-180,-90),(180, 90)) :param viewport: viewport of map -- what gets drawn and on what scale. Default is to set viewport = map_BB :param image_mode: Image mode ('P' for palette or 'L' for Black and White image). BW_MapCanvas inherits from MapCanvas and sets the mode to 'L'. Default image_mode is 'P'. """ # set up the canvas self._filename = filename if filename is not None: polygons = haz_files.ReadBNA(filename, 'PolygonSet') else: polygons = None self.images_dir = images_dir self.last_filename = '' self.draw_ontop = draw_ontop self.draw_back_to_fore = draw_back_to_fore Outputter.__init__(self, cache, kwargs.pop('on', True), output_timestep, output_zero_step, output_last_step, kwargs.pop('name', None)) MapCanvas.__init__(self, image_size, land_polygons=polygons, **kwargs)