def _get_figure(map_region='default', map_par=None, fig_par=None, fig=None, ax=None, m=None, image=None): """Returns a matplotlib figure based on the parameters. The idea is that I create a :class:`Structure` that contains the figure, ax, and m instance. I also add a field for "indices". This is so as to be able to reuse figures. This saves a huge amount of time, as creating then basemap instance can be time consuming. USAGE:: >>> fig = _get_figure() or >>> fig = _get_figure(map_region='polarcat') Returns This will return the "FIG" object, which has attributes: `fig`, `ax`, `m`, and `indices`. The indices are used for deleting lines, texts, collections, etc. if and when we are reusing the figure instance. The indices basically give us a reference to the *empty* map, so we can delete lines without losing meridians or parallels for example. ============ ====================================== keys description ============ ====================================== fig A pyplot.fig instance, use plt.figure(FIG.fig.number) to make the fig active (for example to use plt.savefig('filename.png') m The basemap instance so you can do: x,y = FIG.m(lon,lat) ax The axes indices with index for texts, images, lines, and collections ============ ====================================== """ fig_par = fig_par or {} figure = Structure() if m is None: fig, m = mp.get_base_image(map_region, map_par, fig_par, image=image) elif fig is None: map_par, fig_par = mp.map_regions(map_region, map_par, fig_par) fig = plt.figure(**fig_par) figure.fig = fig figure.m = m figure.ax = ax if ax is not None else fig.gca() figure.indices = Structure( texts=len(figure.ax.texts), images=len(figure.ax.images), collections=len(figure.ax.collections), lines=len(figure.ax.lines), ) print("Using figure: %s" % figure.fig.number) return figure
def plot_curtain(H, data, nx=None, ny=None, data_range=None, units='ppbv', datainfo_str=None, asl=True, plottitle=None, log=True, figure=None, cax_title=None, method='contourf', fig_par=None): """ plot_sensitivity: core function for plotting FLEXPART output. Usage:: > FIG = plot_sensitivity(H,data,*kwargs) This returns the FIGURE object, and plots the sensitivity from the data contained in the "D" array. Inputs H = a :class:`Header` instance for a FLEXPART run. data = a 2d data array containing the sensitivity values to plot, this can be extracted from a grid instance (see :func:`readgridV8` and :func:`get_slabs`) Returns A "mapping.py" ``FIGURE`` object. Arguments .. tabularcolumns:: |l|L| ============= ================================================ keyword Description [default] ============= ================================================ data_range range of data for scale bars, if None will be taken from min/max of data asl [True] plot the units in m.a.s.l cax_title string to be passed as colorbar title (units will be passed to the format argument) units units for the scale bar datainfo_str A string for labeling the scale bar. plottitle Title for the plot. rel_i Release index to plot from the data array map_region A map_region specified in mapping.py projection [deprecated] use pre-defined map_regions. dropm Force creation of a new basemap instance coords Used with autofit option. An array of lat,lon values for the mapping module to autofit a basemap instance to. autofit Try to generate map_region automagically (flakey) overlay Force removal of previous figure elements. transform For use with imshow method, if your data is not in same coordinates as projection, try to transform the data to the basemap projection. log Create a logarithmic color scale. figure A FIGURE instance from mapping module get_FIGURE MapPar A Structure of paramters to be passed to the basemap class when creating an instance. method The method to use for plotting array data. May be one of: [pcolormesh], imshow, or contourf lsmask set to True to draw a grey landseamask [False] ============= ================================================ .. todo:: A lot!! There are some problems here and it is sensitive to options. lsmask = True seems to only work with certain projections (POLARCAT) .. note:: This is the primary main function for creating plots of flexpart output. Most the other routines are simply wrappers to this function, passing arguments in with some predefined settings. For more information on the mechanics of this function, see the mapping.py module and the matplotlib basemap toolkit. """ methods = ['imshow', 'pcolormesh', 'contourf', 'contour', 'None'] assert method in methods, "method keyword must be one of: %s" % methods if figure is None: fig_par = fig_par or {} fig = plt.figure(**fig_par) ax = fig.add_subplot(111) figure = Structure(fig=fig, ax=ax) else: fig = figure.fig ax = figure.ax # Make the figure current plt.figure(fig.number) plt.axes(ax) # Get min/max range #data = data.slabs[0] if data_range is not None: dat_min = data_range[0] dat_max = data_range[1] else: dat_min, dat_max = [data.min(), data.max()] if log: clevs = _log_clevs(dat_min, dat_max) else: clevs = [i for i in np.arange(dat_min, dat_max, (dat_max - dat_min) / 100)] # # Set up the IMAGE # # cmapnames = ['jet', 'hsv', 'gist_ncar', 'gist_rainbow', 'cool', 'spectral'] colmap = _gen_flexpart_colormap() colmap.set_over(color='k', alpha=0.8) topodat = data if method == 'imshow': im = plt.imshow(np.flipud(topodat), cmap=colmap, zorder=-1, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) if method == 'pcolormesh': im = plt.pcolormesh(nx, ny, topodat, cmap=colmap, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) if method == 'contourf': print('*******') print(nx) print(ny) print('*******') print(topodat) print('*******') im = plt.contourf(topodat) # im = plt.contourf(nx, ny, topodat, cmap=colmap, levels=clevs, # norm=mpl.colors.LogNorm(vmin=clevs[0], # vmax=clevs[-1])) if method == 'contour': im = plt.contour(nx, ny, topodat, cmap=colmap, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) # # Get the current axes, and properties for use later pos = ax.get_position() l, b, w, h = pos.bounds # # CREATE COLORBAR # # Note, with upgrades to matplotlib and basemap had to make some # # changes here... no more 'ghost' axes # # does a colorbar already exist? try: cb = figure.cb cax = figure.cax cb.update_normal(im) except: # # make a copy of the image object, change # # colormap to linear version of the precip colormap. # # create new axis for colorbar. h = 0.8 * h l = l + w + .02 b = 0.5 - (h / 2) w = 0.025 cax = plt.axes([l, b, w, h]) # # using im2, not im (hack to prevent colors from being # # too compressed at the low end on the colorbar - results # # from highly nonuniform colormap) cb = fig.colorbar(im, cax=cax) # , format='%3.2g') # draw colorbar figure.cax = cax figure.cb = cb # # set colorbar label and ticks p_cax = mpl.font_manager.FontProperties(size='6') clabels = list(clevs[::10]) # #clevs, by 10 steps clabels.append(clevs[-1]) # # add the last label # cax.set_yticks(np.linspace(clabels[0],clabels[-1],len(clabels))) cax.set_yticks(np.linspace(0, 1, len(clabels))) cax.set_yticklabels(['%3.2g' % cl for cl in clabels]) # fontproperties=p_cax) if cax_title: cax.set_title(cax_title.format(units), fontproperties=p_cax) else: cax.set_title('sensitivity\n({0})'.format(units), fontproperties=p_cax) # # make the original axes current again plt.axes(ax) plt.grid(True) figure.ax = ax figure.fig = fig if plottitle != None: figure.ax.set_title(plottitle, fontsize=10) return figure
def map_regions(map_region='default', map_par=None, fig_par=None): """Given a `map_region`, return the associated parameters in mapping DB. USAGE:: map_par, fig_par = map_regions(map_region="polarcat") The list of regions know by reflexible by default is located in the package file named 'mapping_db.yml'. If you want to create a new region, or override an existing one in reflexible itself, you can create it in your own YAML file. For example, suppose that you have the next 'myregions.yml' file:: northern_hemisphere: descr: Northern Hemisphere alias: my_own_nh map_par: projection: cyl llcrnrlat: 0.5 urcrnrlat: 90 llcrnrlon: -180.7 urcrnrlon: 180 resolution: c # anchor: W fig_par: figsize: [8, 3] # w,h tuple axlocs: [0.1, 0.1, .7, .8] For informing reflexible on where your mapping file is located, you just create the REFLEXIBLE_MAPDB environment variable with its path:: $ export REFLEXIBLE_MAPDB = $HOME/my_analysis/myregions.yml Since this moment on, the definitions in your file will be used (with highest priority) for finding the regions specified in `map_region`. Returns Two dictionaries, first a map_par dictionary with keywords that are the same as what is need to create a basemap instance of matplotlib: ============ ========================== keys description ============ ========================== llcrnrlat lower left latitude llcrnrlon lower left longitude urcrnrlat upper right latitude urcrnrlon upper right longitude area_thresh area threshold resolution resolution projection projection lat_1 lat_1 lon_0 lon_0 rsphere (6378137.00,6356752.3142) m you can pass an m object which is needed for some regions ============ ========================== Second, a fig_par dictionary that contains options that may be passed to the :mod:`matplotlib.pyplot` :func:`figure` function: ============ ========================== keys description ============ ========================== figsize size of the figure axlocs locations of the axes ============ ========================== .. note:: You can override the returned map_par and fig_par dicts by passing dicts through the optional `map_par` and `fig_par` parameters. """ # Set some default values map_par_ = Structure(anchor='C') fig_par_ = Structure( figsize=[8, 7], # w,h tuple axlocs=[0.05, 0.01, .8, .9], # rect = l,b,w,h ) # Get the database out of the system YAML file mapdb_file = os.path.join(os.path.dirname(__file__), 'mapping_db.yml') with open(mapdb_file) as mapdb: mapping_db = yaml.safe_load(mapdb) # and merge it with a possible one pointed by REFLEXIBLE_MAPDB env var if 'REFLEXIBLE_MAPDB' in os.environ: user_mapdb_file = os.environ['REFLEXIBLE_MAPDB'] with open(user_mapdb_file) as mapdb: mapping_db.update(yaml.safe_load(mapdb)) # Lookup the region and its aliases try: region = mapping_db[map_region] except KeyError: # Lookup aliases for key in mapping_db: if 'alias' in mapping_db[key]: alias = mapping_db[key]['alias'] if map_region in re.split(r',\s*', alias): region = mapping_db[key] break else: raise KeyError("region {} not found".format(map_region)) # Get the params map_par_.update(region['map_par']) # map_par should be always there fig_par_.update(region.get('fig_par', {})) # fig_par not always present # Override params if `map_par` or `fig_par` are passed map_par_.update(map_par or {}) fig_par_.update(fig_par or {}) # Just in case map_par_.pop('m', None) return map_par_, fig_par_
def _get_figure(map_region='default', map_par=None, fig_par=None, fig=None, ax=None, m=None, image=None): """Returns a matplotlib figure based on the parameters. The idea is that I create a :class:`Structure` that contains the figure, ax, and m instance. I also add a field for "indices". This is so as to be able to reuse figures. This saves a huge amount of time, as creating then basemap instance can be time consuming. USAGE:: >>> fig = _get_figure() or >>> fig = _get_figure(map_region='polarcat') Returns This will return the "FIG" object, which has attributes: `fig`, `ax`, `m`, and `indices`. The indices are used for deleting lines, texts, collections, etc. if and when we are reusing the figure instance. The indices basically give us a reference to the *empty* map, so we can delete lines without losing meridians or parallels for example. ============ ====================================== keys description ============ ====================================== fig A pyplot.fig instance, use plt.figure(FIG.fig.number) to make the fig active (for example to use plt.savefig('filename.png') m The basemap instance so you can do: x,y = FIG.m(lon,lat) ax The axes indices with index for texts, images, lines, and collections ============ ====================================== """ fig_par = fig_par or {} figure = Structure() if m is None: if image: fig, m = mp.get_base_image(image, map_region, map_par, fig_par) else: fig, m = mp.get_base1(map_region, map_par, fig_par, fig=fig) if fig is None: map_par, fig_par = mp.map_regions(map_region, map_par, fig_par) fig = plt.figure(**fig_par) figure.fig = fig figure.m = m figure.ax = ax if ax is not None else fig.gca() figure.indices = Structure( texts=len(figure.ax.texts), images=len(figure.ax.images), collections=len(figure.ax.collections), lines=len(figure.ax.lines), ) print("Using figure: %s" % figure.fig.number) return figure
def plot_curtain(H, data, data_range=None, nx=None, ny=None, units='ppbv', datainfo_str=None, asl=True, plottitle=None, log=True, figure=None, cax_title=None, method='contourf', fig_par=None): """ plot_sensitivity: core function for plotting FLEXPART output. Usage:: > FIG = plot_sensitivity(H,data,*kwargs) This returns the FIGURE object, and plots the sensitivity from the data contained in the "D" array. Inputs H = a :class:`Header` instance for a FLEXPART run. data = a 2d data array containing the sensitivity values to plot, this can be extracted from a grid instance (see :func:`readgridV8` and :func:`get_slabs`) Returns A "mapping.py" ``FIGURE`` object. Arguments .. tabularcolumns:: |l|L| ============= ================================================ keyword Description [default] ============= ================================================ data_range range of data for scale bars, if None will be taken from min/max of data asl [True] plot the units in m.a.s.l cax_title string to be passed as colorbar title (units will be passed to the format argument) units units for the scale bar datainfo_str A string for labeling the scale bar. plottitle Title for the plot. rel_i Release index to plot from the data array map_region A map_region specified in mapping.py projection [deprecated] use pre-defined map_regions. dropm Force creation of a new basemap instance coords Used with autofit option. An array of lat,lon values for the mapping module to autofit a basemap instance to. autofit Try to generate map_region automagically (flakey) overlay Force removal of previous figure elements. transform For use with imshow method, if your data is not in same coordinates as projection, try to transform the data to the basemap projection. log Create a logarithmic color scale. figure A FIGURE instance from mapping module get_FIGURE MapPar A Structure of paramters to be passed to the basemap class when creating an instance. method The method to use for plotting array data. May be one of: [pcolormesh], imshow, or contourf lsmask set to True to draw a grey landseamask [False] ============= ================================================ .. todo:: A lot!! There are some problems here and it is sensitive to options. lsmask = True seems to only work with certain projections (POLARCAT) .. note:: This is the primary main function for creating plots of flexpart output. Most the other routines are simply wrappers to this function, passing arguments in with some predefined settings. For more information on the mechanics of this function, see the mapping.py module and the matplotlib basemap toolkit. """ methods = ['imshow', 'pcolormesh', 'contourf', 'contour', 'None'] assert method in methods, "method keyword must be one of: %s" % methods if figure is None: fig_par = fig_par or {} fig = plt.figure(**fig_par) ax = fig.add_subplot(111) figure = Structure(fig=fig, ax=ax) else: fig = figure.fig ax = figure.ax # Make the figure current plt.figure(fig.number) plt.axes(ax) # Get min/max range #data = data.slabs[0] if data_range is not None: dat_min = data_range[0] dat_max = data_range[1] else: dat_min, dat_max = [data.min(), data.max()] if log: clevs = _log_clevs(dat_min, dat_max) else: clevs = [i for i in np.arange(dat_min, dat_max, (dat_max - dat_min) / 100)] # # Set up the IMAGE # # cmapnames = ['jet', 'hsv', 'gist_ncar', 'gist_rainbow', 'cool', 'spectral'] colmap = _gen_flexpart_colormap() colmap.set_over(color='k', alpha=0.8) topodat = data if method == 'imshow': im = plt.imshow(np.flipud(topodat), cmap=colmap, zorder=-1, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) if method == 'pcolormesh': im = plt.pcolormesh(nx, ny, topodat, cmap=colmap, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) if method == 'contourf': im = plt.contourf(topodat) # im = plt.contourf(nx, ny, topodat, cmap=colmap, levels=clevs, # norm=mpl.colors.LogNorm(vmin=clevs[0], # vmax=clevs[-1])) if method == 'contour': im = plt.contour(nx, ny, topodat, cmap=colmap, norm=mpl.colors.LogNorm(vmin=clevs[0], vmax=clevs[-1])) # # Get the current axes, and properties for use later pos = ax.get_position() l, b, w, h = pos.bounds # # CREATE COLORBAR # # Note, with upgrades to matplotlib and basemap had to make some # # changes here... no more 'ghost' axes # # does a colorbar already exist? try: cb = figure.cb cax = figure.cax cb.update_normal(im) except: # # make a copy of the image object, change # # colormap to linear version of the precip colormap. # # create new axis for colorbar. h = 0.8 * h l = l + w + .02 b = 0.5 - (h / 2) w = 0.025 cax = plt.axes([l, b, w, h]) # # using im2, not im (hack to prevent colors from being # # too compressed at the low end on the colorbar - results # # from highly nonuniform colormap) cb = fig.colorbar(im, cax=cax) # , format='%3.2g') # draw colorbar figure.cax = cax figure.cb = cb # # set colorbar label and ticks p_cax = mpl.font_manager.FontProperties(size='6') clabels = list(clevs[::10]) # #clevs, by 10 steps clabels.append(clevs[-1]) # # add the last label # cax.set_yticks(np.linspace(clabels[0],clabels[-1],len(clabels))) cax.set_yticks(np.linspace(0, 1, len(clabels))) cax.set_yticklabels(['%3.2g' % cl for cl in clabels]) # fontproperties=p_cax) if cax_title: cax.set_title(cax_title.format(units), fontproperties=p_cax) else: cax.set_title('sensitivity\n({0})'.format(units), fontproperties=p_cax) # # make the original axes current again plt.axes(ax) plt.grid(True) figure.ax = ax figure.fig = fig if plottitle != None: figure.ax.set_title(plottitle, fontsize=10) return figure