From 41d495cea9073d1a53d8e66e879bde9e5d7b68be Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 13 Nov 2019 15:39:40 +0100 Subject: [PATCH 01/27] Agg backend added --- mapgenerator/plotting/plotmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index a4ab4c2..d38128b 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -6,7 +6,7 @@ import matplotlib as mpl -mpl.use('Qt5Agg') +mpl.use('Agg') import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap -- GitLab From 020abb6d047f632a1a2dde3012e0205d28577352 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Thu, 30 Jul 2020 17:18:46 +0200 Subject: [PATCH 02/27] Add subplots support --- mapgenerator/plotting/definitions.py | 5 ++ mapgenerator/plotting/plotmap.py | 124 +++++++++++++-------------- 2 files changed, 67 insertions(+), 62 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index d391cb3..1670148 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -617,6 +617,11 @@ class MapDrawOptions(object): self.coastsopts = ".5,grey" self.countropts = ".3,grey" + self.save = True + self.subplot = None + + self.boundinglat = None + ## FILLED COUNTOUR OPTIONS ## AIR QUALITY OPTIONS diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index d38128b..aa7e3b6 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -337,14 +337,15 @@ class PlotMap(MapCross, MapDrawOptions): # overwrite option if os.path.exists(figName + ".png") and not self.overwrite: print(figName, " already exists.") - plt.clf() + if self.subplot is None: + plt.clf() return figName map_data = data.getMapData() # FIXME scatter_data = data.getScatterData() or cur_scatter_data - - plt.clf() + if self.subplot is None: + plt.clf() # params = { # 'font.size': 14, # 'text.fontsize': 28, @@ -361,14 +362,26 @@ class PlotMap(MapCross, MapDrawOptions): #print("-----------", lon_0, lat_0, "-----------") else: lon_0, lat_0 = None, None - self.map = Basemap( - projection=self.projection, resolution=self.resolution, - llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], - urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], - lon_0=lon_0, lat_0=lat_0, - fix_aspect=self.keep_aspect, - area_thresh=self.area_thresh - ) + if self.subplot: + self.map = Basemap( + ax=self.mgaxis, + projection=self.projection, resolution=self.resolution, + llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], + urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], + lon_0=lon_0, lat_0=lat_0, + fix_aspect=self.keep_aspect, + area_thresh=self.area_thresh + ) + else: + self.map = Basemap( + projection=self.projection, resolution=self.resolution, + llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], + urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], + lon_0=lon_0, lat_0=lat_0, + fix_aspect=self.keep_aspect, + area_thresh=self.area_thresh, + boundinglat=self.boundinglat, + ) glon, glat = grid #log.info("GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) @@ -704,51 +717,35 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi log.warning("printing {}".format(fullname)) if self.kml or self.kmz: #self.mgplot.axes(frameon=0) - self.mgplot.savefig(fullname, - bbox_inches='tight', - frameon=0, - pad_inches=0, dpi=self.dpi, transparent=True) + if self.save: + self.mgplot.savefig(fullname, + bbox_inches='tight', + frameon=0, + pad_inches=0, dpi=self.dpi, transparent=True) else: #a = plt.axes([0.0, 0.0, 1.0, 1.0]) #plt.axis('off') - plt.setp(plt.gca().spines.values(), linewidth=.1) + plt.setp(self.mgaxis.spines.values(), linewidth=.1) xpos = self.xsize/2 if not self.keep_aspect: ypos = ystart_norm + ysize_norm + 0.05 else: ypos = self.ysize*0.95 - self.mgplot.text( - xpos, - ypos, - imgTitle, - horizontalalignment='center', - verticalalignment='top', - fontsize=self.fontsize, - zorder=0, - ) - self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) - #self.mgplot.show() - - #plt.tight_layout() - #self.printTime("saving") - -# if(self.first_image): -# #if first image of the batch, produce a image "color map" -# print "First image of the batch: creating image color map" -# self.mapName = "%s.map.png" % figName -# # Get the colorbar to generate the image color map -# self.runCommand("convert -crop 70x600+730+0 -colors 160 %s.png %s" % (figName, self.mapName)) -# self.first_image = False - - #convert from png to gif - #print "printing ", figName -# self.runCommand("convert -quality 80 +dither -remap %s %s.png %s.anim.png" % (self.mapName, figName, figName)) -# self.runCommand("convert +dither %s.png %s.gif" % (figName, figName)) -# self.runCommand("rm %s.png" % figName) - - #plt.clf() - #self.printTime("convert") + if self.subplot is None: + self.mgplot.text( + xpos, + ypos, + imgTitle, + horizontalalignment='center', + verticalalignment='top', + fontsize=self.fontsize, + zorder=0, + ) + else: + self.mgaxis.set_title(imgTitle, fontsize=self.fontsize, zorder=0,) + if self.save: + self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) return figName @@ -956,9 +953,12 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi else: fName = 'mg_aplot' figName = "%s/%s" % (self.outdir, fName) - - plt.clf() - self.mgplot, self.mgaxis = plt.subplots() + if self.subplot is None: + plt.clf() + self.mgplot, self.mgaxis = plt.subplots() + else: + self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2]) + self.mgplot = plt.gcf() fn = self.genImageMap( figName, @@ -1088,12 +1088,12 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi START = 0 for nTime in self.timesteps: #range(START,int(TOTAL),INTERVAL): + if self.subplot is None: + plt.clf() + self.mgplot, self.mgaxis = plt.subplots() + else: + self.mgplot, self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[3]) - plt.clf() - -# self.mgplot = plt.figure() -# self.mgaxis = plt.gca() - self.mgplot, self.mgaxis = plt.subplots() valid_tmp = dims[nTime] currDate = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], valid_tmp[-9:-7], valid_tmp[0:2]), "%Y %b %d %H" ) @@ -1197,13 +1197,13 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi os.mkdir("%s-%s" % (runDate, self.srcvars[0])) except: pass - - fig.savefig( - "%s/%s-%s/%s-colorbar.png" % (self.outdir, runDate, self.srcvars[0], runDate), - bbox_inches='tight', - pad_inches=0, dpi=self.dpi, - transparent=True - ) + if self.save: + fig.savefig( + "%s/%s-%s/%s-colorbar.png" % (self.outdir, runDate, self.srcvars[0], runDate), + bbox_inches='tight', + pad_inches=0, dpi=self.dpi, + transparent=True + ) #generate KMZ - Offline if self.kmz: -- GitLab From 8e160f2097e09c229a0e3d994cbc70c7ad6b54ef Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Fri, 21 Aug 2020 09:17:25 +0200 Subject: [PATCH 03/27] Add boundinglat option --- mapgenerator/plotting/plotmap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index aa7e3b6..4532e18 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -370,7 +370,8 @@ class PlotMap(MapCross, MapDrawOptions): urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], lon_0=lon_0, lat_0=lat_0, fix_aspect=self.keep_aspect, - area_thresh=self.area_thresh + area_thresh=self.area_thresh, + boundinglat=self.boundinglat, ) else: self.map = Basemap( -- GitLab From 8daf3239f9ad6b9b45417d261ebf88869d423084 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Thu, 8 Oct 2020 15:42:40 +0200 Subject: [PATCH 04/27] Add timeseries plotting --- bin/map_main.py | 4 +- mapgenerator/plotting/definitions.py | 4 +- mapgenerator/plotting/plotmap.py | 17 --- mapgenerator/plotting/timeseries.py | 215 +++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 21 deletions(-) create mode 100644 mapgenerator/plotting/timeseries.py diff --git a/bin/map_main.py b/bin/map_main.py index b174da7..a4c204a 100755 --- a/bin/map_main.py +++ b/bin/map_main.py @@ -29,7 +29,7 @@ mpl_logger = logging.getLogger('matplotlib') mpl_logger.setLevel('CRITICAL') from mapgenerator.plotting.config import ArgumentParser -from mapgenerator.plotting import plotmap +from mapgenerator.plotting import plotmap, timeseries import sys from datetime import datetime, timedelta @@ -76,7 +76,7 @@ class MapGenerator: if res['pltype'] == 'cross': plotmap.PlotCross(loglevel).plot(**res) elif res['pltype'] == 'timeseries': - plotmap.PlotSeries(loglevel).plot(**res) + timeseries.PlotSeries(loglevel).plot(**res) elif res['pltype'] == 'map': plotmap.PlotMap(loglevel).plot(**res) else: diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 1670148..0394c3c 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -611,8 +611,7 @@ class MapDrawOptions(object): self.area_thresh = None # Default area threshold for maps self.resolution = None # Default resolution is intermediate self.xsize = '1.' # (when keep_aspect = False) Default horizontal size [value between 0 and 1] - self.ysize = '1.' # (when keep_aspect = False) Default vertical size [value between 0 and 1] - self.dpi = '200' # Default DPI + self.ysize = '1.' # (when keep_aspect = False) Default vertical size [value between 0 and 1] # Default DPI self.coordsopts = ".3,8,grey" self.coastsopts = ".5,grey" self.countropts = ".3,grey" @@ -699,6 +698,7 @@ class MapGenerator(object): self.debug = kwargs.get('debug', False) self.alpha = kwargs.get('alpha', None) self.shapefiles = kwargs.get('shapefiles', None) + self.dpi = kwargs.get('dpi', 200) class MapCross(MapGenerator): diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 4532e18..c362460 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -45,13 +45,6 @@ import logging log = logging.getLogger(__name__) -class PlotSeries(): - """ Main class for plotting time series """ - - def __init__(self, loglevel='WARNING', **kwargs): - pass - - class PlotCross(): """ Main class for plotting cross sections """ @@ -404,16 +397,6 @@ class PlotMap(MapCross, MapDrawOptions): x, y = glon, glat log.info("3. GLON: %s, GLAT: %s" % (str(x.shape), str(y.shape))) -# print "map_data", map_data[0].shape - - #self.printTime("meshgrid") - #log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) - -# # nomap option -# if self.nomap and os.path.exists("%s-%s/%s.png" % (runDate, varName, figName)) and not self.overwrite: -# print figName, " already exists." -# plt.clf() -# return figName if self.nomap: self.mgplot.frameon = False diff --git a/mapgenerator/plotting/timeseries.py b/mapgenerator/plotting/timeseries.py new file mode 100644 index 0000000..e935ace --- /dev/null +++ b/mapgenerator/plotting/timeseries.py @@ -0,0 +1,215 @@ +from ast import parse +import math +import os +import logging +from matplotlib.dates import date2num, num2date + +import matplotlib.pyplot as plt +from matplotlib.gridspec import GridSpec +import matplotlib.dates as mdates +import netCDF4 + +from mapgenerator.plotting.definitions import MapGenerator + +logger = logging.getLogger(__name__) + +class PlotSeries(MapGenerator): + """ Main class for plotting time series """ + + def __init__(self, loglevel='WARNING', **kwargs): + """ Initialize class with attributes """ + super().__init__(loglevel=loglevel, kwargs=kwargs) + self._current_fig = None + self.scalex = kwargs.get('scalex', None) + self.scaley = kwargs.get('scaley', None) + self.suptitle_fontsize = kwargs.get('suptitle_fontsize', 24) + self.title_fontsize = kwargs.get('title_fontsize', 16) + self.axis_fontsize = kwargs.get('axis_fontsize', 12) + self.plot_size = kwargs.get("plot_size", (8.0, 6.0)) + + def _close(self): + plt.close(self._current_fig) + self._current_fig = None + + def plot_cube(self, cube, coord=None, **kwargs): + if coord: + coord = cube.coord(coord) + if not coord and cube.dim_coords: + coord = cube.dim_coords[0] + if not coord and cube.dim_coords: + coord = cube.aux_coords[0] + + if coord.units.calendar: + points = date2num(coord.units.num2date(coord.points)) + else: + points = coord.points + + if 'xlabel' not in kwargs: + kwargs['xlabel'] = self._get_default_title(cube) + self._current_fig = plt.figure(figsize=self.plot_size) + self._plot_array( + points, + cube.data, + title=self._get_default_title(cube), + **kwargs, + ) + self._set_time_axis(coord) + self._current_fig.tight_layout() + suptitle = kwargs.pop('suptitle', None) + if suptitle: + plt.suptitle(suptitle, y=1.08, fontsize=self.suptitle_fontsize) + self._save_fig(self.img_template) + self._close() + + def _plot_array(self, x, y, **kwargs): + invertx = kwargs.pop('invertx', False) + inverty = kwargs.pop('inverty', False) + xlabel = kwargs.pop('xlabel', None) + ylabel = kwargs.pop('ylabel', None) + xlimits = kwargs.pop('xlimits', None) + ylimits = kwargs.pop('ylimits', None) + title = kwargs.pop('title', None) + plt.plot(x, y) + ax = plt.gca() + if xlabel: + ax.set_xlabel(xlabel, fontsize=self.axis_fontsize) + if ylabel: + ax.set_ylabel(ylabel, fontsize=self.axis_fontsize) + if title: + ax.set_title(title, fontsize=self.title_fontsize, y=1.04) + if invertx: + logger.debug('Invert x axis') + ax.invert_xaxis() + if inverty: + logger.debug('Invert y axis') + ax.invert_yaxis() + if self.scalex: + ax.set_xscale(self.scalex) + if self.scaley: + ax.set_yscale(self.scaley) + if xlimits: + if xlimits == 'tight': + ax.set_xlim(left=x.min(), right=x.max()) + elif xlimits == 'auto': + ax.set_autoscalex_on(True) + else: + ax.set_xlim(left=xlimits[0], right=xlimits[1]) + if ylimits: + if ylimits == 'tight': + ax.set_ylim(left=y.min(), right=y.max()) + elif ylimits == 'auto': + ax.set_autoscaley_on(True) + else: + ax.set_ylim(left=ylimits[0], right=ylimits[1]) + ax.grid(b=True, which='major', axis='both', alpha=0.6) + ax.grid(b=True, which='minor', axis='both', alpha=0.3) + ax.tick_params(axis='both', which='major', labelsize=self.axis_fontsize) + + + def multiplot_cube(self, cube, coord, multi_coord, ncols=2, invert=False, **kwargs): + coord = cube.coord(coord) + multi_coord = cube.coord(multi_coord) + if multi_coord.shape[0] == 1: + self.plot_cube(cube, coord, **kwargs) + return + sharex = kwargs.get('sharex', False) + sharey = kwargs.get('sharey', False) + + self._current_fig = plt.figure( + figsize=( + ncols * self.plot_size[0], + math.ceil(multi_coord.shape[0] / ncols) * self.plot_size[1]) + ) + suptitle = kwargs.pop('suptitle', '') + if suptitle: + suptitle = f"{suptitle}\n\n{self._get_default_title(cube)}" + else: + suptitle = self._get_default_title(cube) + self._current_fig.suptitle( + suptitle, y=1.0, fontsize=self.suptitle_fontsize + ) + gs = GridSpec(math.ceil(multi_coord.shape[0] / ncols), ncols) + for i, plot_cube in enumerate(cube.slices_over(multi_coord)): + if i == 0 or not (sharex or sharey): + self._current_fig.add_subplot(gs[i]) + elif sharex: + if sharey: + self._current_fig.add_subplot(gs[i], sharex=plt.gca(), sharey=plt.gca()) + kwargs.pop('invertx', None) + kwargs.pop('inverty', None) + else: + self._current_fig.add_subplot(gs[i], sharex=plt.gca()) + kwargs.pop('invertx', None) + elif sharey: + self._current_fig.add_subplot(gs[i], sharey=plt.gca()) + kwargs.pop('inverty', None) + title = plot_cube.coord(multi_coord).cell(0).point + if isinstance(title, bytes): + title = title.decode() + plt.title( + title, + fontsize=self.title_fontsize + ) + if coord.units.calendar: + points = date2num(coord.units.num2date(coord.points)) + else: + points = coord.points + if invert: + x = plot_cube.data + y = points + if 'ylabel' not in kwargs: + kwargs['ylabel'] = self._get_default_title(coord) + else: + x = points + y = plot_cube.data + if 'xlabel' not in kwargs: + kwargs['xlabel'] = self._get_default_title(coord) + + self._plot_array( + x, y, **kwargs, + ) + self._set_time_axis(coord) + + self._current_fig.tight_layout(pad=2.0) + self._save_fig(self.img_template) + self._close() + + def _save_fig(self, name): + fullname = os.path.join(self.outdir ,f"{name}.{self.filefmt}") + self._current_fig.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) + + @staticmethod + def _get_default_title(cube): + if cube.attributes.get('plot_name', None): + show_name = cube.attributes['plot_name'] + elif cube.long_name and len(cube.long_name) < 45: + show_name = cube.long_name + elif cube.standard_name and len(cube.standard_name) < 45: + show_name = cube.standard_name + else: + show_name = cube.var_name + return f"{show_name} ({cube.units})" + + @staticmethod + def _set_time_axis(coord): + if not coord.units.calendar: + return + axis = plt.gca().xaxis + years = coord.cell(-1).point.year - coord.cell(0).point.year + if years < 10: + major_locator = 1 + minor_locator = None + elif years < 50: + major_locator = 5 + minor_locator = 1 + elif years < 100: + major_locator = 10 + minor_locator = 2 + else: + major_locator = 20 + minor_locator = 5 + axis.set_major_locator(mdates.YearLocator(major_locator)) + axis.set_minor_locator(mdates.YearLocator(minor_locator)) + axis.set_major_formatter(mdates.DateFormatter('%Y')) + axis.label._text = f"{coord.name()} (years)" + -- GitLab From 1f213afa230d9257e3a72909b588b962c486ae3c Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Fri, 16 Oct 2020 15:21:42 +0200 Subject: [PATCH 05/27] Add timeseries --- mapgenerator/plotting/timeseries.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mapgenerator/plotting/timeseries.py b/mapgenerator/plotting/timeseries.py index e935ace..d9e8fd3 100644 --- a/mapgenerator/plotting/timeseries.py +++ b/mapgenerator/plotting/timeseries.py @@ -209,7 +209,8 @@ class PlotSeries(MapGenerator): major_locator = 20 minor_locator = 5 axis.set_major_locator(mdates.YearLocator(major_locator)) - axis.set_minor_locator(mdates.YearLocator(minor_locator)) + if minor_locator: + axis.set_minor_locator(mdates.YearLocator(minor_locator)) axis.set_major_formatter(mdates.DateFormatter('%Y')) axis.label._text = f"{coord.name()} (years)" -- GitLab From 915d8bfef674e2fd6cf0cc5ee228446b747d49fe Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 22 Oct 2020 12:54:31 +0200 Subject: [PATCH 06/27] Fixed naming --- .pre-commit-config.yaml | 9 + mapgenerator/plotting/config.py | 246 ++++++------- mapgenerator/plotting/definitions.py | 306 ++++++++-------- mapgenerator/plotting/plotmap.py | 522 ++++++++++++++------------- 4 files changed, 569 insertions(+), 514 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0da760f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +repos: +- repo: local + hooks: + - id: wily + name: wily + entry: wily diff + verbose: true + language: python + additional_dependencies: [wily] diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index 261bb8a..35cf6c3 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -7,8 +7,9 @@ import os import configargparse as argparse import configparser -from optparse import OptionParser -import mapgenerator +# from optparse import OptionParser +from mapgenerator import __version__ +from mapgenerator import mg_exceptions import logging @@ -34,187 +35,187 @@ class ArgumentParser(object): try: self.parser = argparse.ArgumentParser(description='') self.parser.add_argument('-V', '--version', action='version', - version=mapgenerator.__version__, + version=__version__, help="returns MapGenerator version number and exit") self.parser.add_argument('--config', #is_config_file=True, - dest="config", + dest="config", help='specifies the config file to read' ) #required=False) self.parser.add_argument("--section", - dest="section", - help="config file section to read") + dest="section", + help="config file section to read") # main options self.parser.add_argument("--srcfiles", - dest="srcfiles", - help="main source netCDF file") + dest="srcfiles", + help="main source netCDF file") self.parser.add_argument("--srcvars", - dest="srcvars", - help="main variable") + dest="srcvars", + help="main variable") self.parser.add_argument("--plottype", - dest="pltype", - choices=['map', 'cross', 'timeseries'], - help="type of plots to generate") + dest="pltype", + choices=['map', 'cross', 'timeseries'], + help="type of plots to generate") # self.parser.add_argument("--loglevel", - dest="loglevel", - choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', - 'CRITICAL'], - help="logging levelbug (default=WARNING)") + dest="loglevel", + choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', + 'CRITICAL'], + help="logging levelbug (default=WARNING)") self.parser.add_argument("--indir", - dest="indir", - help="directory where is/are stored the input file(s)") + dest="indir", + help="directory where is/are stored the input file(s)") self.parser.add_argument("--outdir", - dest="outdir", - help="directory where is/are to be stored the output file(s)") + dest="outdir", + help="directory where is/are to be stored the output file(s)") self.parser.add_argument("--file_format", - dest="filefmt", - help="format of the output file(s) (default=png)") + dest="filefmt", + help="format of the output file(s) (default=png)") self.parser.add_argument("--timesteps", - dest="timesteps", - nargs="+", - help="timesteps to plot (list or 'all' - default=[0])") + dest="timesteps", + nargs="+", + help="timesteps to plot (list or 'all' - default=[0])") self.parser.add_argument("--bounds", - dest="bounds", - nargs="+", - help="values bounds list (default=None)") + dest="bounds", + nargs="+", + help="values bounds list (default=None)") self.parser.add_argument("--ticks", - dest="ticks", - help="colorbar ticks list (default=None)") + dest="ticks", + help="colorbar ticks list (default=None)") self.parser.add_argument("--colors", - dest="colors", - help="colors list or colormap name (default='jet'), for a list check here: https://matplotlib.org/users/colormaps.html") + dest="colors", + help="colors list or colormap name (default='jet'), for a list check here: https://matplotlib.org/users/colormaps.html") self.parser.add_argument("--lon", - dest="lon", - nargs="+", - help="longitude list") + dest="lon", + nargs="+", + help="longitude list") self.parser.add_argument("--lat", - dest="lat", - nargs="+", - help="latitude list") + dest="lat", + nargs="+", + help="latitude list") self.parser.add_argument("--smooth", - dest="smooth", - help="plot without smoothing (default=True)") + dest="smooth", + help="plot without smoothing (default=True)") self.parser.add_argument("--subsetting", - dest="subsetting", - help="subset according to given lat/lon (default=True)") + dest="subsetting", + help="subset according to given lat/lon (default=True)") self.parser.add_argument("--nocontourf", - dest="nocontourf", - help="no contourf") + dest="nocontourf", + help="no contourf") self.parser.add_argument("--colorbar", - dest="colorbar", - help="toggle colorbar (default=True)") + dest="colorbar", + help="toggle colorbar (default=True)") self.parser.add_argument("--formats", - dest="formats", - help="colorbar labels format (default=auto)") + dest="formats", + help="colorbar labels format (default=auto)") self.parser.add_argument("--extend", - dest="extend", - choices=['both','max','min'], - help="extend colormap extremes (default='neither')") + dest="extend", + choices=['both', 'max', 'min'], + help="extend colormap extremes (default='neither')") self.parser.add_argument("--resolution", - dest="resolution", - choices=['c','l','i','h','f'], - help="details level of coastlines/coutries") + dest="resolution", + choices=['c', 'l', 'i', 'h', 'f'], + help="details level of coastlines/coutries") self.parser.add_argument("--dpi", - dest="dpi", - help="output image dpi (default=200)") + dest="dpi", + help="output image dpi (default=200)") self.parser.add_argument("--fontsize", - dest="fontsize", - help="title fontsize (default=12)") + dest="fontsize", + help="title fontsize (default=12)") self.parser.add_argument("--continents", - dest="continents", - help="color to fill continents (default=None)") + dest="continents", + help="color to fill continents (default=None)") self.parser.add_argument("--coordsopts", - dest="coordsopts", - help="coordinates linewidth, fontsize, color (default=0.3,10,grey)") + dest="coordsopts", + help="coordinates linewidth, fontsize, color (default=0.3,10,grey)") self.parser.add_argument("--coastsopts", - dest="coastsopts", - help="coastlines linewidth, color (default=0.5,grey)") + dest="coastsopts", + help="coastlines linewidth, color (default=0.5,grey)") self.parser.add_argument("--countropts", - dest="countropts", - help="countries linewidth, color (default=0.3,grey)") + dest="countropts", + help="countries linewidth, color (default=0.3,grey)") self.parser.add_argument("--anim", - dest="anim", - help="Toggle Animation (default=False)") + dest="anim", + help="Toggle Animation (default=False)") self.parser.add_argument("--scatter", - dest="scatter", - help="data file for additional scatter plot") + dest="scatter", + help="data file for additional scatter plot") self.parser.add_argument("--contours", - dest="contours", - help="additional contour variable") + dest="contours", + help="additional contour variable") self.parser.add_argument("--keep_aspect", - dest="keep_aspect", - help="preserve map aspect ratio when drawing (default=False)") + dest="keep_aspect", + help="preserve map aspect ratio when drawing (default=False)") self.parser.add_argument("--shapefiles", - dest="shapefiles", - help="additional shape files") + dest="shapefiles", + help="additional shape files") self.parser.add_argument("--wind", - dest="wind", - help="wind source file") + dest="wind", + help="wind source file") self.parser.add_argument("--windopts", - dest="windopts", - help="wind options") + dest="windopts", + help="wind options") self.parser.add_argument("--xsize", - dest="xsize", - help="proportion of X dimension (between 0 and 1)") + dest="xsize", + help="proportion of X dimension (between 0 and 1)") self.parser.add_argument("--ysize", - dest="ysize", - help="proportion of Y dimension (between 0 and 1)") + dest="ysize", + help="proportion of Y dimension (between 0 and 1)") self.parser.add_argument("--projection", - dest="projection", - help="map projection to draw (default=cyl)") + dest="projection", + help="map projection to draw (default=cyl)") self.parser.add_argument("--area_thresh", - dest="area_thresh", - help="area threshold in surface drawing (including lakes)") + dest="area_thresh", + help="area threshold in surface drawing (including lakes)") self.parser.add_argument("--title", - dest="title", - help="title of the figure") + dest="title", + help="title of the figure") self.parser.add_argument("--contours_int", - dest="contours_int", - help="interval between contours") + dest="contours_int", + help="interval between contours") self.parser.add_argument("--contours_color", - dest="contours_color", - help="contours color") + dest="contours_color", + help="contours color") self.parser.add_argument("--contours_label", - dest="contours_label", - help="contours label") + dest="contours_label", + help="contours label") # self.parser.add_argument("--varconds", # dest="varconds", # help="varconds") self.parser.add_argument("--dimension", - dest="dimension", - help="dimension name and operation in the form DIMENSION,OPERATION (mean, max, min, ...) as documented in the numpy reference guide. If OPERATION is a number it will extract the correspondent element") + dest="dimension", + help="dimension name and operation in the form DIMENSION,OPERATION (mean, max, min, ...) as documented in the numpy reference guide. If OPERATION is a number it will extract the correspondent element") self.parser.add_argument("--alpha", - dest="alpha", - help="alpha value for colormap netween 0 and 1 (default=1)") + dest="alpha", + help="alpha value for colormap netween 0 and 1 (default=1)") ############ self.parser.add_argument("--img_template", - dest="img_template", - help="Image template") + dest="img_template", + help="Image template") self.parser.add_argument("--joint_template", - dest="joint_template", - help="Joint template") + dest="joint_template", + help="Joint template") self.parser.add_argument("--nomap", - dest="nomap", - help="hide map") + dest="nomap", + help="hide map") # self.parser.add_argument("--kml", -# dest="kml", -# help="generate KML. String with base url (default='')") +# dest="kml", +# help="generate KML. String with base url (default='')") self.parser.add_argument("--kmz", - dest="kmz", - help="generate KMZ (default=False)") + dest="kmz", + help="generate KMZ (default=False)") self.parser.add_argument("--overwrite", - dest="overwrite", - help="overwrite outputs (default=True)") + dest="overwrite", + help="overwrite outputs (default=True)") self.parser.add_argument("--background", - dest="background", - choices=['bluemarble','shadedrelief','etopo','GIS'], - help="background orography (default=None)") + dest="background", + choices=['bluemarble', 'shadedrelief', 'etopo', 'GIS'], + help="background orography (default=None)") self.parser.add_argument("--logo", - dest="logo", - help="put a logo (image path, x, y - default=None)") + dest="logo", + help="put a logo (image path, x, y - default=None)") # self.parser.add_argument("--noruntime", # dest="noruntime", # help="no runtime") @@ -281,12 +282,12 @@ class ArgumentParser(object): raise mg_exceptions.ArgumentParserException -def readConf(section=None, fpath=None): +def read_conf(section=None, fpath=None): """ Read configuration """ config = configparser.RawConfigParser() config.read(fpath) - if section == None: + if section is None: return config.sections() res = {} @@ -298,10 +299,10 @@ def readConf(section=None, fpath=None): return res -def writeConf(section, fpath, opts): +def write_conf(section, fpath, opts): """ Write configurations on file. """ - config = ConfigParser.RawConfigParser() + config = configparser.RawConfigParser() # check if file exists if os.path.exists(fpath): @@ -319,4 +320,3 @@ def writeConf(section, fpath, opts): # write configuration with open(fpath, 'wb') as configfile: config.write(configfile) - diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 1670148..efcc398 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -32,7 +32,7 @@ def is_grid_regular(lon, lat): return False #print("Checking if lat is strictly_increasing ...") - if not np.array([strictly_increasing(lat[:,y]) \ + if not np.array([strictly_increasing(lat[:, y]) \ for y in np.arange(lat.shape[1])]).all(): return False @@ -41,20 +41,23 @@ def is_grid_regular(lon, lat): for x in np.arange(lon.shape[0]-1)]).all() print(is_x_reg) - if not is_x_reg: return is_x_reg + if not is_x_reg: + return is_x_reg #print("Checking lat spacing ...") - return np.array([(np.spacing(lat[:,y]) == np.spacing(lat[:,y+1])).all() \ + return np.array([(np.spacing(lat[:, y]) == np.spacing(lat[:, y+1])).all() \ for y in np.arange(lat.shape[1]-1)]).all() -def strictly_increasing(L): +def strictly_increasing(vector): """ Check if vector values are strictly increasing """ - return all(x= cur_coord[0]) & \ @@ -110,7 +114,8 @@ def extract_coords(self, var, coord='lons'): def parse_parameters_list(plist): - """ Parser for list parameter to be able to enter intervals and number of steps (examples: 0,1,2 0-6,2 ...""" + """ Parser for list parameter to be able to enter intervals and number of + steps (examples: 0,1,2 0-6,2 ...""" if isinstance(plist, list) or isinstance(plist, tuple) or isinstance(plist, np.ndarray): if len(plist) == 1: plist = plist[0] @@ -119,30 +124,31 @@ def parse_parameters_list(plist): if str(plist).find('-') > 0: tmp = plist.strip('[]') try: - rg, steps = tmp.split(',') + rng, steps = tmp.split(',') except: - rg = tmp + rng = tmp steps = None - start, end = [float(i.replace('m','-')) if i.find('.')>=0 else - int(i.replace('m','-')) for i in rg.split('-')] + start, end = [float(i.replace('m', '-')) if i.find('.') >= 0 else + int(i.replace('m', '-')) for i in rng.split('-')] if not steps: steps = (end-start)/10 else: steps = float(steps) return list(np.arange(start, end+int(steps), steps)) #, endpoint=True)) else: - res = eval(str(plist).replace('m','-')) + res = eval(str(plist).replace('m', '-')) if isinstance(res, int) or isinstance(res, float): return [res,] return list(res) def parse_parameter(var, val): + """ Parameter parser """ # parse numeric list arguments if var in ('lon', 'lat', 'timesteps', 'bounds'): - if var=='timesteps' and val in ('all',['all']): + if var == 'timesteps' and val in ('all', ['all']): return 'all' - if val!=None: + if val != None: return parse_parameters_list(val) # parse float arguments @@ -152,7 +158,7 @@ def parse_parameter(var, val): # parse simple list arguments elif var in ('coordsopts', 'coastsopts', 'countropts'): - if val and type(val) == str: + if val and isinstance(val, str): #log.info(val, type(val)) return val.split(',') @@ -170,13 +176,14 @@ def parse_parameters(params): return ret -def parse_path(dir, f): +def parse_path(directory, dfile): + """ Path parser """ #print "Opening data file", f - if os.path.isabs(f): - return f + if os.path.isabs(dfile): + return dfile else: #log.info("Input: %s", f) - return os.path.join(dir, f) + return os.path.join(directory, dfile) class MapData(object): @@ -184,11 +191,11 @@ class MapData(object): def __init__(self, loglevel='WARNING', **kwargs): log.setLevel(loglevel) - log.info("mapData initializer") + log.info("map_data initializer") self.reset() - self.maxReset() + self.max_reset() # Process optional arguments - for dtype in ('map_data','wind_data','contour_data','scatter_data'): + for dtype in ('map_data', 'wind_data', 'contour_data', 'scatter_data'): if dtype in kwargs: setattr(self, dtype, kwargs[dtype]) @@ -198,60 +205,60 @@ class MapData(object): self.contour_data = None self.scatter_data = None - def maxReset(self): + def max_reset(self): self.max_data = None - def setGridData(self, data): + def set_grid_data(self, data): #print "Setting grid data" self.grid_data = data - def setMapData(self, data): + def set_map_data(self, data): #print "Setting map data" self.map_data = data - def setMaxData(self, data): + def set_max_data(self, data): self.max_data = data - def setWindData(self, data): + def set_wind_data(self, data): self.wind_data = data - def setContourData(self, data): + def set_contour_data(self, data): self.contour_data = data - def setScatterData(self, data): + def set_scatter_data(self, data): self.scatter_data = data - def getGridData(self): + def get_grid_data(self): return self.grid_data - def getMapData(self): + def get_map_data(self): return self.map_data - def getWindData(self): + def get_wind_data(self): return self.wind_data - def getContourData(self): + def get_contour_data(self): return self.contour_data - def getMaxData(self): + def get_max_data(self): return self.max_data - def getScatterData(self): + def get_scatter_data(self): return self.scatter_data - def hasMapData(self): + def has_map_data(self): return (self.map_data != None) - def hasWindData(self): + def has_wind_data(self): return (self.wind_data != None) - def hasContourData(self): + def has_contour_data(self): return self.contour_data is not None - def hasMaxData(self): + def has_max_data(self): return (self.max_data != None) - def hasScatterData(self): + def has_scatter_data(self): return (self.scatter_data != None) @@ -268,9 +275,10 @@ class MapDataHandler(object): # max = None # transf = False - def __init__(self, sources, srcvars, indir, srcgaps, windsource, windopts, lats, lons, transf, subsetting, dimension, **kwargs): + def __init__(self, sources, srcvars, indir, srcgaps, windsource, windopts, + lats, lons, transf, subsetting, dimension, **kwargs): #self.galab = GaLab(Bin='grads', Window=False, Echo=False) - self.ncdf = [[],[]] # list of lists, srcfiles and windfiles + self.ncdf = [[], []] # list of lists, srcfiles and windfiles self.srcs = sources self.srcvars = srcvars self.srcgaps = srcgaps @@ -280,7 +288,7 @@ class MapDataHandler(object): self.lats = lats self.lons = lons self.maxdata = None - self.lastTstep = None + self.last_tstep = None self.transf = transf self.data = MapData() self.do_winds = False @@ -296,33 +304,33 @@ class MapDataHandler(object): and (kwargs['winds']['opts']): log.info("Will do winds") self.do_winds = True - self.windsrc = kwargs['winds']['src'] + self.windsrc = kwargs['winds']['src'] self.windopts = kwargs['winds']['opts'] if ('contours' in kwargs) \ and (kwargs['contours']['var']): log.info("Will do contours") self.do_contours = True - self.contoursrc = self.srcs[0] - self.contourvar = kwargs['contours']['var'] + self.contoursrc = self.srcs[0] + self.contourvar = kwargs['contours']['var'] if ('varconds' in kwargs): - vc = kwargs['varconds'] - if vc: - self.varconds = eval(vc) + vcond = kwargs['varconds'] + if vcond: + self.varconds = eval(vcond) #self.galab.cmd("reinit") # Open sources - for f in self.srcs: - fpath = parse_path(self.indir, f) + for sfile in self.srcs: + fpath = parse_path(self.indir, sfile) fin = nc(fpath) self.ncdf[0].append(fin) - for v in fin.variables: - if v in ('longitude','lon','nav_lon'): - x, x_idxs = extract_coords(self, fin.variables[v]) - elif v in ('latitude','lat','nav_lat'): - y, y_idxs = extract_coords(self, fin.variables[v], coord='lats') + for vname in fin.variables: + if vname in ('longitude', 'lon', 'nav_lon'): + x, x_idxs = extract_coords(self, fin.variables[vname]) + elif vname in ('latitude', 'lat', 'nav_lat'): + y, y_idxs = extract_coords(self, fin.variables[vname], coord='lats') self.grid = (x, y) if x_idxs is not None and y_idxs is not None: @@ -338,7 +346,8 @@ class MapDataHandler(object): # if self.transf: # self.ncdf.transf = self.transf - def ncdfReset(self): + def ncdf_reset(self): + """ Closes netcdfs """ # close src files and winds for f in self.ncdf[0]: f.close() @@ -346,7 +355,8 @@ class MapDataHandler(object): w.close() #print "Resetting GraDs engine" - def dimOperation(self, srcvar, srcfile): + def dim_operation(self, srcvar, srcfile): + """ Operations over dimensions """ dim, oper = self.dimension.split(',') log.info("Dimension %s, operation or index/value: %s" % (dim, oper)) dimidx = srcvar.dimensions.index(dim) @@ -364,39 +374,41 @@ class MapDataHandler(object): oper = np.abs(dimvar-oper).argmin() return np.take(srcvar, oper, axis=dimidx) - def getDataForTstep(self, step): + def get_data_for_tstep(self, step): + """ Get data per timestep """ self.data.reset() #print "Getting map data for step ", step - self.data.setMapData(self.getMapDataForTstep(step)) + self.data.set_map_data(self.get_map_data_for_tstep(step)) if self.do_winds: log.info("Getting wind data for step '%s'" % step) - self.data.setWindData(self.getWindDataForTstep(step)) + self.data.set_wind_data(self.get_wind_data_for_tstep(step)) if self.do_contours: log.info("Getting contour data for step '%s'" % step) - self.data.setContourData(self.getContourDataForTstep(step)) + self.data.set_contour_data(self.get_contour_data_for_tstep(step)) # Calculate maximum over data # FIXME should this step be optional? -# if (self.data.hasMaxData()): -# self.data.setMaxData(maximum(self.data.getMaxData(), self.data.getMapData())) +# if (self.data.has_max_data()): +# self.data.set_max_data(maximum(self.data.get_max_data(), self.data.get_map_data())) # else: -# self.data.setMaxData(self.data.getMapData()) -# self.lastTstep = step +# self.data.set_max_data(self.data.get_map_data()) +# self.last_tstep = step return self.data - def getCurrentMaxData(self): + def get_current_max_data(self): '''Get current max data. NOTE: A call to this method will reset current max data, so it can be called only once! ''' - log.info("Retrieving max (last calculated tstep: %d); this will reset current MAX data!!" % self.lastTstep) - max_data = MapData(map_data=self.data.getMaxData()) - self.data.maxReset() + log.info("Retrieving max (last calculated tstep: %d); this will reset current MAX data!!" % self.last_tstep) + max_data = MapData(map_data=self.data.get_max_data()) + self.data.max_reset() return max_data - def getMapDataForTstep(self, step): - mapData = [] + def get_map_data_for_tstep(self, step): + """ Get map data for tstep """ + map_data = [] for src_n in range(len(self.srcs)): tstep = step + int(self.srcgaps[src_n]) srcfile = self.ncdf[0][src_n] @@ -428,131 +440,141 @@ class MapDataHandler(object): if expvar in srcfile.variables: srcvar = srcfile.variables[expvar] if self.dimension: - srcvar = self.dimOperation(srcvar, srcfile) + srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: # if no expression #log.info("SRCVAR: %s - GRID_IDXS: %s" % (str(srcvar.shape), str(self.grid_idxs))) - values = srcvar[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] + values = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] else: - values = srcvar[tstep,:,:] + values = srcvar[tstep, :, :] log.info("1. SRCVAR: %s - GRID_IDXS: %s" % (str(values.shape), str(self.grid_idxs))) else: # otherwise search for a variable of the dataset into the # expression given - for v in srcfile.variables: - if v in expvar: - expvar = expvar.replace(v, 'newvar') - srcvar = srcfile.variables[v] + for vname in srcfile.variables: + if vname in expvar: + expvar = expvar.replace(vname, 'newvar') + srcvar = srcfile.variables[vname] if self.dimension: - srcvar = self.dimOperation(srcvar, srcfile) + srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - newvar = srcvar[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] + newvar = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] else: - newvar = srcvar[tstep,:,:] + newvar = srcvar[tstep, :, :] values = eval(expvar) break log.info("2. SRCVAR: %s - GRID_IDXS: %s" % (str(values.shape), str(self.grid_idxs))) - if mapData: + if map_data: #exp_res = self.ncdf.exp(EXPVAR) - mapData.append(values) + map_data.append(values) else: - mapData = [values] + map_data = [values] - return mapData + return map_data - def getWindDataForTstep(self, step): - windData = {'u': None, 'v': None} + def get_wind_data_for_tstep(self, tstep): + """ Get wind data for tstep """ + wind_data = {'u': None, 'v': None} log.info("WINDOPTS: %s" % str(self.windopts)) srcfile = self.ncdf[1][0] srcvar_u = srcfile.variables[self.windopts[0]] srcvar_v = srcfile.variables[self.windopts[1]] if self.dimension: - srcvar_u = self.dimOperation(srcvar_u, srcfile) - srcvar_v = self.dimOperation(srcvar_v, srcfile) + srcvar_u = self.dim_operation(srcvar_u, srcfile) + srcvar_v = self.dim_operation(srcvar_v, srcfile) if self.grid_idxs: - windData['u'] = srcvar_u[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] - windData['v'] = srcvar_v[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] + wind_data['u'] = srcvar_u[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + wind_data['v'] = srcvar_v[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] else: - windData['u'] = srcvar_u[tstep,:,:] - windData['v'] = srcvar_v[tstep,:,:] + wind_data['u'] = srcvar_u[tstep, :, :] + wind_data['v'] = srcvar_v[tstep, :, :] if len(self.windopts) == 4: - windData['barbs'] = self.windopts[3] - return windData + wind_data['barbs'] = self.windopts[3] + return wind_data - def getContourDataForTstep(self, step): + def get_contour_data_for_tstep(self, tstep): + """ Get contour data for tstep """ cdata = None log.info("Contours variable: %s" % self.contourvar) srcfile = self.ncdf[0][0] srcvar = srcfile.variables[self.contourvar] if self.dimension: - srcvar = self.dimOperation(srcvar, srcfile) + srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - cdata = srcvar[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] + cdata = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] else: - cdata = srcvar[tstep,:,:] + cdata = srcvar[tstep, :, :] return cdata - def getScatterDataForTstep(self, step): - sdata = None + def get_scatter_data_for_tstep(self, tstep): + """ """ log.info("Scatter variable: %s" % self.scatter) srcfile = self.ncdf[0][0] srcvar = srcfile.variables[self.contourvar] if self.dimension: - srcvar = self.dimOperation(srcvar, srcfile) + srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - cdata = srcvar[tstep,:,:][self.grid_idxs[1],:][:,self.grid_idxs[0]] + cdata = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] else: - cdata = srcvar[tstep,:,:] + cdata = srcvar[tstep, :, :] return cdata - def getDims(self): + def get_dims(self): log.info("Getting dimensions from file '%s'" % self.srcs[0]) #self.ncdf.cmd("set dfile 1") - for v in self.ncdf[0][0].variables: - if v in ('time', 'time_counter'): - tvar = v + for vname in self.ncdf[0][0].variables: + if vname in ('time', 'time_counter'): + tvar = vname time = self.ncdf[0][0].variables[tvar] try: - typ, since, dat, tim = time.units.split()[:4] + typ, _, dat, tim = time.units.split()[:4] except: - typ, since, dat = time.units.split()[:3] + typ, _, dat = time.units.split()[:3] tim = "00:00:00" - t = time[:] + tdata = time[:] tim = tim.split('.')[0] tfmt = '%HZ%d%b%Y' try: - dt = datetime.strptime("%s %s" % (dat,tim), '%Y-%m-%d %H:%M:%S') + dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M:%S') except: - dt = datetime.strptime("%s %s" % (dat,tim), '%Y-%m-%d %H:%M') + dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M') if typ == 'minutes': - d = {(np.where(t==i)[0][0], (dt + timedelta(minutes=int(i))).strftime(tfmt)) for i in t} + cdate = {(np.where(tdata == i)[0][0], (dtime + + timedelta(minutes=int(i))).strftime(tfmt)) + for i in tdata} elif typ == 'seconds': - d = {(np.where(t==i)[0][0], (dt + timedelta(seconds=int(i))).strftime(tfmt)) for i in t} + cdate = {(np.where(tdata == i)[0][0], (dtime + + timedelta(seconds=int(i))).strftime(tfmt)) + for i in tdata} else: #typ == 'hours': - d = {(np.where(t==i)[0][0], (dt + timedelta(hours=int(i))).strftime(tfmt)) for i in t} - return dict(d) + cdate = {(np.where(tdata == i)[0][0], (dtime + + timedelta(hours=int(i))).strftime(tfmt)) + for i in tdata} + return dict(cdate) class DataFrameHandler(object): + """ Clase to handle dataframes """ def __init__(self, dframe, separator=',', ): - if type(dframe) == str: + if isinstance(dframe, str): log.info("opening '%s'" % dframe) self.reader = pd.read_csv(dframe, sep=separator, parse_dates=True) else: self.reader = dframe def filter(self, **kwargs): + """ Filtering dataframes """ datecol = 'datecol' in kwargs and kwargs['datecol'] or 'date' - latcol = 'latcol' in kwargs and kwargs['latcol'] or 'lat' - loncol = 'loncol' in kwargs and kwargs['loncol'] or 'lon' - colcol = 'colcol' in kwargs and kwargs['colcol'] or 'color' + latcol = 'latcol' in kwargs and kwargs['latcol'] or 'lat' + loncol = 'loncol' in kwargs and kwargs['loncol'] or 'lon' + colcol = 'colcol' in kwargs and kwargs['colcol'] or 'color' sizecol = 'sizecol' in kwargs and kwargs['sizecol'] or 'size' ret = {} - if type(self.reader) == dict: + if isinstance(self.reader, dict): self.reader = pd.DataFrame(self.reader) self.reader = self.reader.dropna() @@ -561,17 +583,18 @@ class DataFrameHandler(object): return dict(self.reader) gdf = self.reader.groupby(datecol) - for g in gdf.groups: - gg = gdf.get_group(g) - dt = datetime.strptime(g, "%Y-%m-%d %H:%M:00") - ret[dt] = gg[[loncol,latcol,colcol,sizecol]] + for gname in gdf.groups: + group = gdf.get_group(gname) + dtime = datetime.strptime(gname, "%Y-%m-%d %H:%M:00") + ret[dtime] = group[[loncol, latcol, colcol, sizecol]] #log.info('filtering on date: %s and hour %s' % (date, hour)) return ret class DataTransform(object): + """ Data transform""" @staticmethod - def setColorsFromBuckets(colors, bounds, values): + def set_color_from_buckets(colors, bounds, values): '''Returns a vector of the same size as values, containing a value picked from colors when the current value falls in a bucket as specified in @@ -583,20 +606,20 @@ class DataTransform(object): log.info('colors: %s' % colors) log.info('values: %s' % values) - if (len(colors) != len(bounds) + 1): + if len(colors) != len(bounds) + 1: raise Exception("Wrong size for colors/bounds") out = [] for idx in range(0, len(values)): log.info("processing %s" % str(values[idx])) - if(values[idx] < bounds[0]): + if values[idx] < bounds[0]: out.append(colors[0]) - elif(values[idx] >= bounds[len(bounds) - 1]): + elif values[idx] >= bounds[len(bounds) - 1]: out.append(colors[len(colors) - 1]) else: for bidx in range(0, len(bounds) - 1): - if(values[idx] >= bounds[bidx] and values[idx] < bounds[bidx + 1] ): + if values[idx] >= bounds[bidx] and values[idx] < bounds[bidx + 1]: out.append(colors[bidx + 1]) log.info("data transform output: %s" % str(out)) @@ -604,9 +627,10 @@ class DataTransform(object): class MapDrawOptions(object): - """ """ + """ Map draw options """ def __init__(self, loglevel='WARNING', **kwargs): + log.setLevel(loglevel) ## General Options self.area_thresh = None # Default area threshold for maps self.resolution = None # Default resolution is intermediate @@ -631,7 +655,7 @@ class MapDrawOptions(object): self.wind_units = 'inches' # Wind arrow default unit self.wind_scale = 40 # Wind arrow scale default self.wind_head_length = 10 # Wind arrowhead length default - self.wind_head_width = 8 # Wind arrowhead width default + self.wind_head_width = 8 # Wind arrowhead width default self.wind_width = 0.008 # Wind arrow width default self.wind_minshaft = 0.25 # Wind arrow minimum shaft default self.wind_label_xpos = 0.8 # Wind label x position (relative to quiver plot) @@ -658,10 +682,10 @@ class MapDrawOptions(object): log.setLevel(loglevel) - def hasShapeFiles(self): + def has_shape_files(self): return (len(self.shapefiles) > 0) - def hasLimits(self): + def has_limits(self): return (len(self.limits) > 0) @@ -706,6 +730,7 @@ class MapCross(MapGenerator): def __init__(self, loglevel='WARNING', **kwargs): """ Initialize class with MapGenerator attributes plus some others """ + log.setLevel(loglevel) MapGenerator.__init__(self, **kwargs) self.bounds = kwargs.get('bounds', None) self.ticks = kwargs.get('ticks', None) @@ -727,4 +752,3 @@ class MapCross(MapGenerator): self.noruntime = kwargs.get('noruntime', False) self.timesteps = kwargs.get('timesteps', '0') self.limits = kwargs.get('limits', None) - diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 4532e18..c443c6c 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -45,14 +45,14 @@ import logging log = logging.getLogger(__name__) -class PlotSeries(): +class PlotSeries(object): """ Main class for plotting time series """ def __init__(self, loglevel='WARNING', **kwargs): pass -class PlotCross(): +class PlotCross(object): """ Main class for plotting cross sections """ def __init__(self, loglevel='WARNING', **kwargs): @@ -79,7 +79,7 @@ class PlotMap(MapCross, MapDrawOptions): #self.formats = None self.map = None #Map #self.first_image = True # Is is the first image of the batch? - self.mapName = None # name of the figure to map animation colors + self.map_name = None # name of the figure to map animation colors # self.drawopts = {'coastlines': # {'linewidth': 0.5, 'color': 'grey', }, # 'countries': @@ -91,7 +91,7 @@ class PlotMap(MapCross, MapDrawOptions): #log.info("SETATTR: {} - {}".format(key, value)) super(PlotMap, self).__setattr__(key, parse_parameter(key, value)) - def setResolution(self): + def set_resolution(self): """ 'c','l','i','h','f' """ # lats = 36, 72, 108, 144, 180 # lons = 72, 144, 216, 288, 360 @@ -112,12 +112,12 @@ class PlotMap(MapCross, MapDrawOptions): else: self.resolution = 'c' # default - def setColorMap(self): + def set_color_map(self): """ Create color map """ if not isinstance(self.colors, str) and isinstance(self.colors, Iterable): # adjust number of colors to number of bounds if self.bounds: - if self.extend in ('min','max'): + if self.extend in ('min', 'max'): if len(self.bounds) < len(self.colors): self.colors = self.colors[:len(self.bounds)] elif self.extend == 'both': @@ -148,9 +148,9 @@ class PlotMap(MapCross, MapDrawOptions): if self.background or self.kml or self.kmz: try: - self.cmap.colors = [(0,0,0,0),]+[c for c in self.cmap.colors[1:]] - except: #if self.extend in ('min','both'): - self.cmap.set_under(color=(0,0,0,0)) + self.cmap.colors = [(0, 0, 0, 0),]+[c for c in self.cmap.colors[1:]] + except: # if self.extend in ('min','both'): + self.cmap.set_under(color=(0, 0, 0, 0)) # else: # self.cmap.colors = [(0,0,0,0),]+[c for c in self.cmap.colors[1:]] @@ -168,7 +168,7 @@ class PlotMap(MapCross, MapDrawOptions): self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) - def setColorBar(self, mco, location='left', drawedges=False, cax=None): + def set_color_bar(self, mco, location='left', drawedges=False, cax=None): """ Create color bar """ # xs = self.xsize # nap = .8-((1-xs)*0.4) @@ -189,17 +189,21 @@ class PlotMap(MapCross, MapDrawOptions): log.info("***** {} *****".format(self.formats)) if self.map: - cb = self.map.colorbar(mco, ticks=self.ticks, format=self.formats, pad="2%", size="3%", extend=self.extend, drawedges=drawedges) + cb = self.map.colorbar(mco, ticks=self.ticks, format=self.formats, + pad="2%", size="3%", extend=self.extend, + drawedges=drawedges) else: - cb = plt.colorbar(mco, ticks=self.ticks, format=self.formats, pad=.06, extend=self.extend, drawedges=drawedges) + cb = plt.colorbar(mco, ticks=self.ticks, format=self.formats, + pad=.06, extend=self.extend, + drawedges=drawedges) cb.ax.tick_params(labelsize=float(self.coordsopts[1])) for lin in cb.ax.yaxis.get_ticklines(): lin.set_visible(False) - #self.printTime("colorbar") + #self.print_time("colorbar") ## If required, draw arrow pointing to the specified limits - if self.hasLimits(): + if self.has_limits(): # The following two values are absolute shrink = 0.75 upper = 0.863 - 0.45 * (0.8 - shrink) # formula seems to work ok @@ -223,14 +227,16 @@ class PlotMap(MapCross, MapDrawOptions): p = j add = (float(i - ticks[j])/float(ticks[j+1] - ticks[j]))*f pos = f*p + lower - plt.arrow(0.88, pos + add, 0.10, 0, length_includes_head=True, head_length=0.10, head_width=0.025, fill=True, color='k') + plt.arrow(0.88, pos + add, 0.10, 0, + length_includes_head=True, head_length=0.10, + head_width=0.025, fill=True, color='k') #plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) - def setTitle(self, title, sdate, cdate, step, stime): + def set_title(self, title, sdate, cdate, step, stime): """ Set the title of the current image to the one provided, substituting the patterns """ - titleDic = { + title_dic = { 'syear': sdate.strftime("%Y"), 'smonth': sdate.strftime("%m"), 'sMONTH': sdate.strftime("%b"), @@ -248,14 +254,15 @@ class PlotMap(MapCross, MapDrawOptions): } try: - pTitle = str(title) % titleDic #("%sh" % sTime, valid) + p_title = str(title) % title_dic #("%sh" % s_time, valid) except Exception as e: print("Title error: %s", str(e)) - pTitle = title % titleDic #("%sh" % sTime, valid) + p_title = title % title_dic #("%sh" % s_time, valid) - return pTitle + return p_title - def printTime(self, msg=None): + def print_time(self, msg=None): + """ Print time """ # initial time if 'start_t' not in globals(): global start_t, last_t @@ -265,36 +272,37 @@ class PlotMap(MapCross, MapDrawOptions): now_t = datetime.now() diff_t = now_t - last_t - if (msg != None): + if msg != None: print('TIME:', msg, ' done in ', diff_t.seconds, ' s') last_t = now_t - def runCommand(self, commStr, fatal=True): - print(commStr) - p = subprocess.Popen(commStr.split(), stderr=subprocess.PIPE) + def run_command(self, comm_str, fatal=True): + """ Run command """ + print(comm_str) + p = subprocess.Popen(comm_str.split(), stderr=subprocess.PIPE) output, err = p.communicate() - print(output,'-----',err) -# st, out = commands.getstatusoutput(commStr) + print(output, '-----', err) +# st, out = commands.getstatusoutput(comm_str) # if st != 0: # print "Error: %s" % str(out) # if (fatal == True): # sys.exit(1) - def genAnim(self, inpattern, outfile): + def gen_anim(self, inpattern, outfile): ''' Generate an animation starting from a set of images, specified by the inpattern parameter ''' #print "Animation: ", indir, inpattern, outdir, outfile # Create animation - self.runCommand( + self.run_command( "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" % \ (self.anim_delay, self.outdir, inpattern, self.outdir, outfile)) # Remove intermediate files - #self.runCommand("rm %s/%s %s" % (indir, inpattern, self.mapName), False) + #self.run_command("rm %s/%s %s" % (indir, inpattern, self.map_name), False) # ccc = [] # ccc.extend(self.colors) -# print "Filtering scatter data for date/hour: ", currDate.strftime("%Y%m%d"), sTime24 -# csv_lat, csv_lon, csv_val = csv_handler.filter(currDate.strftime("%Y%m%d"), sTime24) +# print "Filtering scatter data for date/hour: ", curr_date.strftime("%Y%m%d"), s_time24 +# csv_lat, csv_lon, csv_val = csv_handler.filter(curr_date.strftime("%Y%m%d"), s_time24) # #csv_col = self.dataTransform.setColorsFromBuckets(ccc, self.bounds, csv_con) # if(len(csv_lat) > 0): # scatter_data = [csv_lon, csv_lat, 40, csv_col] @@ -302,16 +310,18 @@ class PlotMap(MapCross, MapDrawOptions): # print "Filtered %d records for scatterplot" % len(scatter_data) - def initMap(self, grid): + def init_map(self, grid): """ Initialize a Basemap map. Initialization should be performed only once the beginning of a serie of images """ glon, glat = grid #print("***",self.lon, self.lat,"***") if not self.lat: # or len(self.lat) not in (2,3): - self.lat = "%s-%s" % (str(round(glat.min(), 1)).replace('-','m'), str(round(glat.max(), 1)).replace('-','m')) + self.lat = "%s-%s" % (str(round(glat.min(), 1)).replace('-', 'm'), + str(round(glat.max(), 1)).replace('-', 'm')) if not self.lon: # or len(self.lon) not in (2,3): - self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace('-','m'), str(round(glon.max(), 1)).replace('-','m')) + self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace('-', 'm'), + str(round(glon.max(), 1)).replace('-', 'm')) #print(self.lon, self.lat) # # Fix the printout of tick values to avoid .0 decimals in integers @@ -324,22 +334,22 @@ class PlotMap(MapCross, MapDrawOptions): # strs.append(str(b)) # # Set the colormap - self.setColorMap() + self.set_color_map() if not self.resolution: - self.setResolution() + self.set_resolution() self.first_image = True - def genImageMap(self, figName, grid, data, imgTitle, cur_scatter_data=None, **kwargs): + def gen_image_map(self, fig_name, grid, data, img_title, cur_scatter_data=None, **kwargs): """ Generate image map """ # overwrite option - if os.path.exists(figName + ".png") and not self.overwrite: - print(figName, " already exists.") + if os.path.exists(fig_name + ".png") and not self.overwrite: + print(fig_name, " already exists.") if self.subplot is None: plt.clf() - return figName + return fig_name map_data = data.getMapData() # FIXME @@ -386,7 +396,7 @@ class PlotMap(MapCross, MapDrawOptions): glon, glat = grid #log.info("GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) - if len(glon.shape)==1 and len(glat.shape)==1: + if len(glon.shape) == 1 and len(glat.shape) == 1: glon, glat = np.meshgrid(glon, glat) #print(glon, glat) @@ -406,14 +416,14 @@ class PlotMap(MapCross, MapDrawOptions): log.info("3. GLON: %s, GLAT: %s" % (str(x.shape), str(y.shape))) # print "map_data", map_data[0].shape - #self.printTime("meshgrid") + #self.print_time("meshgrid") #log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) # # nomap option -# if self.nomap and os.path.exists("%s-%s/%s.png" % (runDate, varName, figName)) and not self.overwrite: -# print figName, " already exists." +# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, varName, fig_name)) and not self.overwrite: +# print fig_name, " already exists." # plt.clf() -# return figName +# return fig_name if self.nomap: self.mgplot.frameon = False @@ -425,10 +435,10 @@ class PlotMap(MapCross, MapDrawOptions): # log.info("YSIZE: %s" % self.ysize) ysize_norm = self.ysize*0.8 xsize_norm = self.xsize*0.8 - rows = imgTitle.count("\n") + rows = img_title.count("\n") blines = max(rows - 2, 0) param = self.fontsize/400 - ystart_base_norm = 0.1 - param*(blines)#imgTitle + ystart_base_norm = 0.1 - param*(blines)#img_title xstart_base_norm = 0.1 ystart_norm = (0.8 - ysize_norm) / 2 + ystart_base_norm xstart_norm = (0.8 - xsize_norm) / 2 + xstart_base_norm @@ -441,8 +451,11 @@ class PlotMap(MapCross, MapDrawOptions): if self.extend in ('min', 'both'): map_data[0] = np.ma.masked_where(map_data[0] < self.bounds[0], map_data[0]) else: - map_data[0] = np.ma.masked_where((map_data[0] >= self.bounds[0]) & (map_data[0] < self.bounds[1]), -map_data[0]) + map_data[0] = np.ma.masked_where((map_data[0] >= + self.bounds[0]) & + (map_data[0] < + self.bounds[1]), + map_data[0]) mco = None @@ -495,13 +508,13 @@ map_data[0]) plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) # Draw shape files - if self.hasShapeFiles(): + if self.has_shape_files(): line_w = float(self.countropts[0]) for shapef in self.shapefiles: - log.info("Processing shape file: {} with line width: {}".format(shapef,line_w)) + log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) self.map.readshapefile(shapef, "%s" % os.path.basename(shapef), linewidth=line_w, color=self.countropts[1]) line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) - #self.printTime("contourf") + #self.print_time("contourf") ## FIXME Modify to use scatter inside DATA ## if DATA.hasScatterData()... etc... @@ -554,7 +567,7 @@ map_data[0]) if data.hasWindData(): winds = data.getWindData() - X,Y,U,V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) + X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) #print("Wind scale is", self.wind_units, self.wind_scale) if winds.has_key('barbs'): self.map.barbs(X, Y, U, V, #units=self.wind_units, @@ -564,24 +577,24 @@ map_data[0]) #minshaft=self.wind_minshaft, #scale=self.wind_scale, color='k') - #self.printTime("barbs") + #self.print_time("barbs") else: Q = self.map.quiver(X, Y, U, V, units=self.wind_units, - headlength=self.wind_head_length, - headwidth=self.wind_head_width, - width=self.wind_width, - minshaft=self.wind_minshaft, - scale=self.wind_scale, - color='gray') + headlength=self.wind_head_length, + headwidth=self.wind_head_width, + width=self.wind_width, + minshaft=self.wind_minshaft, + scale=self.wind_scale, + color='gray') # Draw the key plt.quiverkey(Q, - self.wind_label_xpos, - self.wind_label_ypos, - self.wind_label_scale, - label='%s m/s' % self.wind_label_scale, - coordinates='axes', - labelpos='S', labelsep=0.05) - #self.printTime("quivers") + self.wind_label_xpos, + self.wind_label_ypos, + self.wind_label_scale, + label='%s m/s' % self.wind_label_scale, + coordinates='axes', + labelpos='S', labelsep=0.05) + #self.print_time("quivers") if data.hasContourData(): interval = self.contours_int @@ -635,9 +648,9 @@ map_data[0]) if self.contours_label: self.mgplot.clabel(CS, inline=1, - fontsize=self.contours_lbl_fontsize, + fontsize=self.contours_label_fontsize, #backgroundcolor='r', - fmt=self.contours_lbl_format) + fmt=self.contours_label_format) # else: # print "Not drawing contours since levels has ", len(lvls), " elements" @@ -648,7 +661,7 @@ map_data[0]) if not self.nomap and not self.kml and not self.kmz: # self.map.drawparallels(np.arange(self.lat[0]+lat_offset, self.lat[1], self.lat[2]),labels=[1,0,0,0],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) # self.map.drawmeridians(np.arange(self.lon[0]+lon_offset, self.lon[1], self.lon[2]),labels=[0,0,0,1],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) - #self.printTime("coords") + #self.print_time("coords") self.map.drawparallels(self.lat, labels=[1,0,0,0],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) self.map.drawmeridians(self.lon, labels=[0,0,0,1],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) @@ -661,19 +674,20 @@ map_data[0]) # # Drawing countries # if 'countries' in drawopts: # self.map.drawcountries(**drawopts['countries']) -# #self.printTime("countries") +# #self.print_time("countries") # # Drawing coastlines (land/sea, lakes, etc...) # if 'coastlines' in drawopts: # self.map.drawcoastlines(**drawopts['coastlines']) -# #self.printTime("coastlines") +# #self.print_time("coastlines") ## # Drawing states (FIXME does it work outside the U.S.?) ## if 'states' in drawopts: ## self.map.drawstates(**drawopts['states']) -## #self.printTime("states") +## #self.print_time("states") # Change axes for colorbar - if self.colorbar and not self.kml and not self.kmz and (not self.nocontourf or scatter_data is not None): - self.setColorBar(mco) + if self.colorbar and not self.kml and not self.kmz and \ + (not self.nocontourf or scatter_data is not None): + self.set_color_bar(mco) #m.bluemarble() if self.background == 'shadedrelief': @@ -714,7 +728,7 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi self.mgplot.figimage(nim, self.logo[1], self.logo[2], zorder=10) # SAVEFIG - fullname = "{}.{}".format(figName, self.filefmt) + fullname = "{}.{}".format(fig_name, self.filefmt) log.warning("printing {}".format(fullname)) if self.kml or self.kmz: #self.mgplot.axes(frameon=0) @@ -737,32 +751,32 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi self.mgplot.text( xpos, ypos, - imgTitle, + img_title, horizontalalignment='center', verticalalignment='top', fontsize=self.fontsize, zorder=0, ) else: - self.mgaxis.set_title(imgTitle, fontsize=self.fontsize, zorder=0,) + self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0,) if self.save: self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) - return figName + return fig_name - def compareMaps(self, mapNames, outdir, date, steps, tpl=''): + def compare_maps(self, map_names, outdir, date, steps, tpl=''): """ Generate maps comparison """ os.chdir(outdir) - #print "MAPNAMES", mapNames - #for mapName in mapNames: -# newMapNames = mapNames[0] -# items = {} #[i[0] for i in mapNames] -# for i in range(0, len(mapNames)-1): -# items, newMapNames, newMapNameTpl = self.twoMapsCompare([newMapNames, mapNames[i+1]], items) + #print "MAPNAMES", map_names + #for map_name in map_names: +# newMapNames = map_names[0] +# items = {} #[i[0] for i in map_names] +# for i in range(0, len(map_names)-1): +# items, newMapNames, newMapNameTpl = self.twoMapsCompare([newMapNames, map_names[i+1]], items) - mn = np.array(mapNames) + mn = np.array(map_names) ks = mn.T idx = 0 @@ -807,7 +821,7 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi print("Error: %s" % str(out)) - def genKML(self, figNames, srcvars, dims, online): + def genKML(self, fig_names, srcvars, dims, online=True): """ """ from lxml import etree # from pykml.parser import Schema @@ -818,15 +832,15 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi lon = self.lon lat = self.lat - print(figNames) - #kmlName = '-'.join(os.path.basename(figNames[0]).split('-')[:-1]) - kmlName = os.path.basename(figNames[0])[:-3] - varName = srcvars[0] #figNames[0].split('-')[-2] - runDate0 = datetime.strptime(dims[0], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") - runDate1 = datetime.strptime(dims[1], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") - begindate = runDate0.strftime("%Y%m%d%H") + print(fig_names) + #kmlName = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) + kmlName = os.path.basename(fig_names[0])[:-3] + varName = srcvars[0] #fig_names[0].split('-')[-2] + run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") + run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") + begindate = run_date0.strftime("%Y%m%d%H") dirName = os.path.join(self.outdir, begindate + '-' + varName).replace('./','') - interval = runDate1 - runDate0 + interval = run_date1 - run_date0 datefmt = "%Y-%m-%dT%H:00:00Z" doc = KML.kml( @@ -862,10 +876,10 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi if not online: zf = zipfile.ZipFile("%s.kmz" % kmlName, 'w') - for figName, dat in zip(figNames, sorted(dims.keys())): + for fig_name, dat in zip(fig_names, sorted(dims.keys())): dt = dims[dat] - figName = figName + ".png" - figPath = os.path.join(dirName,os.path.basename(figName)) + fig_name = fig_name + ".png" + figPath = os.path.join(dirName,os.path.basename(fig_name)) startdate = datetime.strptime(dt, "%HZ%d%b%Y") begdate = startdate.strftime(datefmt) enddate = (startdate + interval).strftime(datefmt) @@ -891,8 +905,8 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi ), )) - if os.path.exists(figName): - os.rename(figName, figPath) + if os.path.exists(fig_name): + os.rename(fig_name, figPath) zf.write(figPath) outf = file("%s.kml" % kmlName, 'w') @@ -918,21 +932,21 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi localvars = copy.deepcopy(vars(self)) # update from config file if ('config' in kwargs) and ('section' in kwargs): - self.loadConf(kwargs['section'],kwargs['config']) + self.load_conf(kwargs['section'], kwargs['config']) # update from command line vars(self).update(parse_parameters(kwargs)) # arguments are only local # parse wind arguments - if (self.windopts and (len(self.windopts) == 4)): + if self.windopts and (len(self.windopts) == 4): self.wind_scale = int(self.windopts[3]) log.info("SHP {}".format(str(self.shapefiles))) if self.shapefiles: - self.shapefiles = [f.replace(".shp","") for f in glob(self.shapefiles)] + self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] log.info("SHP {}".format(str(self.shapefiles))) if scatter_data is not None: - if type(scatter_data) == str: + if isinstance(scatter_data, str): scatter_data = DataFrameHandler(parse_path(self.indir, scatter_data)).filter() else: scatter_data = DataFrameHandler(scatter_data).filter() @@ -941,19 +955,19 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi log.info("scatter data: {}".format(str(scatter_data))) data = MapData( - map_data=[map_data], #temporarily - wind_data=wind_data, - contour_data=contour_data, - scatter_data=scatter_data - ) + map_data=[map_data], #temporarily + wind_data=wind_data, + contour_data=contour_data, + scatter_data=scatter_data + ) grid = (x, y) - self.initMap(grid) + self.init_map(grid) if self.img_template: - fName = self.img_template + f_name = self.img_template else: - fName = 'mg_aplot' - figName = "%s/%s" % (self.outdir, fName) + f_name = 'mg_aplot' + fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() self.mgplot, self.mgaxis = plt.subplots() @@ -961,24 +975,24 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2]) self.mgplot = plt.gcf() - fn = self.genImageMap( - figName, - grid, - data, - self.title, - #scatter_data=scatter_data - ) + fn = self.gen_image_map( + fig_name, + grid, + data, + self.title, + #scatter_data=scatter_data + ) # # Create Max at required intervals -# if self.maxdata and nTime in self.maxdata: +# if self.maxdata and n_time in self.maxdata: # if (self.maxtitle != None): -# figName = "%s_%s" % (fName, 'MAXD%d' % (nTime/24)) -# pTitle = self.setTitle(self.maxtitle, sDate, currDate, sTime) -# self.genImageMap( -# figName, +# fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) +# p_title = self.set_title(self.maxtitle, s_date, curr_date, s_time) +# self.gen_image_map( +# fig_name, # grid, -# nc_handler.getCurrentMaxData(), -# pTitle +# nc_handler.get_current_max_data(), +# p_title # ) # else: # print "Missing MAXTITLE, thus not generating MAX images!" @@ -987,6 +1001,8 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi vars(self).update(localvars) def plot_cube(self, cube, **kwargs): + """ Plot cube """ + if 'title' not in kwargs: kwargs['title'] = '{0.long_name} ({0.units})'.format(cube) @@ -1004,7 +1020,7 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi localvars = copy.deepcopy(vars(self)) # update from config file if ('config' in kwargs) and ('section' in kwargs): - self.loadConf(kwargs['section'],kwargs['config']) + self.load_conf(kwargs['section'], kwargs['config']) # update from command line vars(self).update(parse_parameters(kwargs)) # arguments are only local @@ -1028,7 +1044,7 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi log.info("SHP {}".format(str(self.shapefiles))) if self.scatter is not None: - if type(self.scatter) == str: + if isinstance(self.scatter, str): scatter_data = DataFrameHandler(parse_path(self.indir, self.scatter)).filter() else: scatter_data = DataFrameHandler(self.scatter).filter() @@ -1038,134 +1054,140 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi #run = '' run_tmp = '' - mapNames = [] + map_names = [] nc_handler = MapDataHandler( - self.srcfiles, - self.srcvars, - self.indir, - self.srcgaps, - self.wind, - self.windopts, - self.lat, - self.lon, - self.transf, - self.subsetting, - self.dimension, - winds={'src': self.wind, - 'opts': self.windopts}, - contours={'var': self.contours}, - varconds=self.varconds, - ) - - figNames = [] - pltNames = [] - - dims = nc_handler.getDims() + self.srcfiles, + self.srcvars, + self.indir, + self.srcgaps, + self.wind, + self.windopts, + self.lat, + self.lon, + self.transf, + self.subsetting, + self.dimension, + winds={'src': self.wind, + 'opts': self.windopts}, + contours={'var': self.contours}, + varconds=self.varconds, + ) + + fig_names = [] + plt_names = [] + + dims = nc_handler.get_dims() if self.timesteps == 'all': self.timesteps = list(sorted(dims.keys())) run_tmp = dims[0] #12:30Z30NOV2010 - if run_tmp[:-10] == "01:30": run_tmp = run_tmp.replace("01:30", "00") + if run_tmp[:-10] == "01:30": + run_tmp = run_tmp.replace("01:30", "00") #run = "%sh %s %s %s" % (run_tmp[:-10], run_tmp[-9:-7], run_tmp[-7:-4], run_tmp[-4:]) # return grid grid = nc_handler.grid #if not NOMAP: - self.initMap(grid) + self.init_map(grid) - sDate = datetime.strptime("%s %s %s %s" % (run_tmp[-4:], run_tmp[-7:-4], run_tmp[-9:-7], run_tmp[0:2]), "%Y %b %d %H" ) + s_date = datetime.strptime("%s %s %s %s" % (run_tmp[-4:], + run_tmp[-7:-4], + run_tmp[-9:-7], + run_tmp[0:2]), "%Y %b %d %H") - ssDate = sDate.strftime("%Y%m%d") + ss_date = s_date.strftime("%Y%m%d") steps = [] START = self.timesteps[0] - #log.info("Start @ %s - sDate: %s - run_tmp: %s" % (START, sDate, run_tmp)) + #log.info("Start @ %s - s_date: %s - run_tmp: %s" % (START, s_date, run_tmp)) if START is None: START = 0 - for nTime in self.timesteps: #range(START,int(TOTAL),INTERVAL): + for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): if self.subplot is None: plt.clf() self.mgplot, self.mgaxis = plt.subplots() else: - self.mgplot, self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[3]) + self.mgplot, self.mgaxis = plt.subplot(self.subplot[0], + self.subplot[1], + self.subplot[3]) - valid_tmp = dims[nTime] - currDate = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], valid_tmp[-9:-7], valid_tmp[0:2]), "%Y %b %d %H" ) + valid_tmp = dims[n_time] + curr_date = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], valid_tmp[-9:-7], valid_tmp[0:2]), "%Y %b %d %H" ) - sTime = "%02d" % nTime - steps.append(sTime) + s_time = "%02d" % n_time + steps.append(s_time) - pTime = (currDate - sDate).total_seconds()/3600 - sTime24 = "%02d" % (pTime%24) - #sTime3d = "%03d" % pTime # Currently unused, only for Valentina who has more than 99 img in a set + p_time = (curr_date - s_date).total_seconds()/3600 + s_time24 = "%02d" % (p_time%24) + #s_time3d = "%03d" % p_time # Currently unused, only for Valentina who has more than 99 img in a set if self.img_template: - fName = self.img_template % {'date': ssDate, 'step': sTime} - figName = "%s/%s" % (self.outdir, fName) - loopName = self.img_template % {'date': ssDate, 'step': 'loop'} + f_name = self.img_template % {'date': ss_date, 'step': s_time} + fig_name = "%s/%s" % (self.outdir, f_name) + loop_name = self.img_template % {'date': ss_date, 'step': 'loop'} else: - fName = '.'.join(os.path.basename(self.srcfiles[0]).split('.')[:-1]) - figName = "%s/%s_%s" % (self.outdir, fName, sTime) - loopName = "%s_loop" % (fName) + f_name = '.'.join(os.path.basename(self.srcfiles[0]).split('.')[:-1]) + fig_name = "%s/%s_%s" % (self.outdir, f_name, s_time) + loop_name = "%s_loop" % (f_name) - #valid = "%02dUTC %s" % (pTime % 24, currDate.strftime("%d %b %Y")) + #valid = "%02dUTC %s" % (p_time % 24, curr_date.strftime("%d %b %Y")) - #stime = "%02d" % (int(run_tmp.split('Z')[0])+pTime) - stime = "%02d" % (pTime) - pTitle = self.setTitle(self.title, sDate, currDate, sTime, stime) + #stime = "%02d" % (int(run_tmp.split('Z')[0])+p_time) + stime = "%02d" % (p_time) + p_title = self.set_title(self.title, s_date, curr_date, s_time, stime) cur_scatter_data = None - if scatter_data != None and (currDate in scatter_data): - cur_scatter_data = scatter_data[currDate] - - fn = self.genImageMap( - figName, - grid, - nc_handler.getDataForTstep(nTime), - pTitle, - cur_scatter_data=cur_scatter_data - ) + if scatter_data != None and (curr_date in scatter_data): + cur_scatter_data = scatter_data[curr_date] + + fn = self.gen_image_map( + fig_name, + grid, + nc_handler.get_data_for_tstep(n_time), + p_title, + cur_scatter_data=cur_scatter_data + ) - figNames.append(fn) - pltNames.append((self.mgplot, self.mgaxis)) + fig_names.append(fn) + plt_names.append((self.mgplot, self.mgaxis)) # Create Max at required intervals - if self.maxdata and nTime in self.maxdata: - if (self.maxtitle != None): - figName = "%s_%s" % (fName, 'MAXD%d' % (nTime/24)) - pTitle = self.setTitle(self.maxtitle, sDate, currDate, sTime) - self.genImageMap( - figName, - grid, - nc_handler.getCurrentMaxData(), - pTitle - ) + if self.maxdata and n_time in self.maxdata: + if self.maxtitle != None: + fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) + p_title = self.set_title(self.maxtitle, s_date, curr_date, s_time, stime) + self.gen_image_map( + fig_name, + grid, + nc_handler.get_current_max_data(), + p_title + ) else: print("Missing MAXTITLE, thus not generating MAX images!") if self.kml or self.kmz: plt.clf() - runDate = sDate.strftime("%Y%m%d%H") + run_date = s_date.strftime("%Y%m%d%H") #separate colorbar mpl.rcParams['axes.linewidth'] = 0.1 - fig = plt.figure(figsize=(.1,12)) - ax = fig.add_axes([0.05, 0.80, 0.9, 0.15], axisbg=(1,1,1,0)) + fig = plt.figure(figsize=(.1, 12)) + ax = fig.add_axes([0.05, 0.80, 0.9, 0.15], axisbg=(1, 1, 1, 0)) print("...", self.bounds, "...") cb = mpl.colorbar.ColorbarBase( - ax, - cmap=self.cmap, - norm=self.norm, - values=self.bounds, - ticks=self.bounds, - extend=self.extend, - drawedges=False) + ax, + cmap=self.cmap, + norm=self.norm, + values=self.bounds, + ticks=self.bounds, + extend=self.extend, + drawedges=False) if self.bounds: cb.set_ticklabels(self.bounds) else: @@ -1179,83 +1201,83 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi for lin in cb.ax.yaxis.get_ticklines(): lin.set_visible(False) - tit = str(pTitle) + tit = str(p_title) idx1 = tit.find('(') idx2 = tit.find(')') - varUnit = tit[idx1:idx2+1] - if varUnit.find("%s") >= 0: - varUnit = '' + var_unit = tit[idx1:idx2+1] + if var_unit.find("%s") >= 0: + var_unit = '' plt.xlabel( - pTitle, - #"%s\n%s" % (self.srcvars[0], varUnit), + p_title, + #"%s\n%s" % (self.srcvars[0], var_unit), horizontalalignment='left', color='w', fontsize=6, ) - ax.xaxis.set_label_coords(-0.025,-0.025) + ax.xaxis.set_label_coords(-0.025, -0.025) try: - os.mkdir("%s-%s" % (runDate, self.srcvars[0])) + os.mkdir("%s-%s" % (run_date, self.srcvars[0])) except: pass if self.save: fig.savefig( - "%s/%s-%s/%s-colorbar.png" % (self.outdir, runDate, self.srcvars[0], runDate), - bbox_inches='tight', - pad_inches=0, dpi=self.dpi, - transparent=True - ) + "%s/%s-%s/%s-colorbar.png" % (self.outdir, run_date, self.srcvars[0], run_date), + bbox_inches='tight', + pad_inches=0, dpi=self.dpi, + transparent=True + ) #generate KMZ - Offline if self.kmz: print("Generating KMZ ...") - self.genKML(figNames, self.srcvars, dims, online=False) + self.genKML(fig_names, self.srcvars, dims, online=False) #generate KML - Online if self.kml: print("Generating KML ...") - self.genKML(figNames, self.srcvars, dims) + self.genKML(fig_names, self.srcvars, dims) #generate animation. if self.anim: - self.genAnim( - "%s.%s" % (loopName.replace('loop','*'), self.filefmt), - "%s.gif" % loopName - ) + self.gen_anim( + "%s.%s" % (loop_name.replace('loop', '*'), self.filefmt), + "%s.gif" % loop_name + ) # fulloutdir = os.path.join(self.outdir, os.path.dirname(self.img_template)) -# loopName = os.path.basename(loopName) -# self.genAnim( +# loop_name = os.path.basename(loop_name) +# self.gen_anim( # fulloutdir, -# "%s.%s" % (loopName.replace('loop','*'), self.filefmt), +# "%s.%s" % (loop_name.replace('loop','*'), self.filefmt), # fulloutdir, -# "%s.gif" % loopName +# "%s.gif" % loop_name # ) - mapNames.append(figNames) + map_names.append(fig_names) # restore options without local function parameters vars(self).update(localvars) - #print "Returning ", pltNames - #return pltNames + #print "Returning ", plt_names + #return plt_names -# num = len(mapNames) +# num = len(map_names) # if num > 1: -# mg.compareMaps(mapNames, OUTDIR, ssDate, steps, JOINT_TEMPLATE ) +# mg.compare_maps(map_names, OUTDIR, ss_date, steps, JOINT_TEMPLATE ) - def resetConf(self): + def reset_conf(self): """ Back to the initial conditions. """ self.__init__() - def loadConf(self, section=None, fpath=None, reset=False): + def load_conf(self, section=None, fpath=None, reset=False): """ Load existing configurations from file. """ - from .config import readConf + from .config import read_conf - if fpath == None: + if fpath is None: fpath = parse_path(self.config_dir, self.config_file) else: fpath = parse_path(self.indir, fpath) @@ -1264,21 +1286,21 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi print("Error %s" % fpath) return - opts = readConf(section, fpath) - if section == None: + opts = read_conf(section, fpath) + if section is None: return opts if reset: - self.resetConf() + self.reset_conf() vars(self).update(parse_parameters(opts)) - def writeConf(self, section, fpath=None): + def write_conf(self, section, fpath=None): """ Write configurations on file. """ - from .config import writeConf + from .config import write_conf - if fpath == None: + if fpath is None: fpath = parse_path(self.config_dir, self.config_file) else: fpath = parse_path(self.indir, fpath) @@ -1291,11 +1313,11 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi # save current conf curropts = copy.deepcopy(vars(self)) # load defaults - self.resetConf() + self.reset_conf() defaults = copy.deepcopy(vars(self)) # calculate diff - diff = {k : curropts[k] for k in curropts if curropts[k]!=defaults[k]} + diff = {k : curropts[k] for k in curropts if curropts[k] != defaults[k]} # reload diff vars(self).update(diff) # save diff - writeConf(section, fpath, diff) + write_conf(section, fpath, diff) -- GitLab From 0d977451762c8d8a319570ef80091fb937ed9688 Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 22 Oct 2020 15:27:24 +0200 Subject: [PATCH 07/27] Removed Basemap and replaced with Cartopy --- mapgenerator/plotting/plotmap.py | 325 ++++++++++++++++--------------- 1 file changed, 172 insertions(+), 153 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index c443c6c..6d62c20 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -9,9 +9,11 @@ import matplotlib as mpl mpl.use('Agg') import matplotlib.pyplot as plt -from mpl_toolkits.basemap import Basemap +# from mpl_toolkits.basemap import Basemap +import cartopy.crs as ccrs +# import cartopy.feature as cpf from matplotlib.cbook import delete_masked_points -from matplotlib.image import imread +# from matplotlib.image import imread import numpy as np from datetime import datetime #from datetime import timedelta @@ -35,7 +37,10 @@ from .definitions import parse_parameter from .definitions import parse_parameters from .definitions import parse_parameters_list from .definitions import parse_path -from urllib.request import urlopen +try: + from urllib2 import urlopen +except ImportError: + from urllib.request import urlopen import copy from glob import glob @@ -77,7 +82,7 @@ class PlotMap(MapCross, MapDrawOptions): self.norm = None self.cmap = None #self.formats = None - self.map = None #Map + # self.map = None #Map #self.first_image = True # Is is the first image of the batch? self.map_name = None # name of the figure to map animation colors # self.drawopts = {'coastlines': @@ -188,14 +193,14 @@ class PlotMap(MapCross, MapDrawOptions): pass log.info("***** {} *****".format(self.formats)) - if self.map: - cb = self.map.colorbar(mco, ticks=self.ticks, format=self.formats, - pad="2%", size="3%", extend=self.extend, - drawedges=drawedges) - else: - cb = plt.colorbar(mco, ticks=self.ticks, format=self.formats, - pad=.06, extend=self.extend, - drawedges=drawedges) +# if self.map: +# cb = self.map.colorbar(mco, ticks=self.ticks, format=self.formats, +# pad="2%", size="3%", extend=self.extend, +# drawedges=drawedges) +# else: + cb = plt.colorbar(mco, ticks=self.ticks, format=self.formats, + pad=.06, extend=self.extend, + drawedges=drawedges) cb.ax.tick_params(labelsize=float(self.coordsopts[1])) for lin in cb.ax.yaxis.get_ticklines(): @@ -351,9 +356,9 @@ class PlotMap(MapCross, MapDrawOptions): plt.clf() return fig_name - map_data = data.getMapData() + map_data = data.get_map_data() # FIXME - scatter_data = data.getScatterData() or cur_scatter_data + scatter_data = data.get_scatter_data() or cur_scatter_data if self.subplot is None: plt.clf() # params = { @@ -366,33 +371,36 @@ class PlotMap(MapCross, MapDrawOptions): # mpl.rcParams.update(params) if not self.nomap: - if self.projection != 'cyl': + if self.projection != 'PlateCarree': lon_0 = (self.lon[-1]-abs(self.lon[0]))/2 lat_0 = (self.lat[-1]-abs(self.lat[0]))/2 #print("-----------", lon_0, lat_0, "-----------") else: lon_0, lat_0 = None, None - if self.subplot: - self.map = Basemap( - ax=self.mgaxis, - projection=self.projection, resolution=self.resolution, - llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], - urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], - lon_0=lon_0, lat_0=lat_0, - fix_aspect=self.keep_aspect, - area_thresh=self.area_thresh, - boundinglat=self.boundinglat, - ) - else: - self.map = Basemap( - projection=self.projection, resolution=self.resolution, - llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], - urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], - lon_0=lon_0, lat_0=lat_0, - fix_aspect=self.keep_aspect, - area_thresh=self.area_thresh, - boundinglat=self.boundinglat, - ) + + self.mgaxis.set_extent([self.lon[0], self.lon[-1], self.lat[0], + self.lat[-1]]) +# if self.subplot: +# self.map = Basemap( +# ax=self.mgaxis, +# projection=self.projection, resolution=self.resolution, +# llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], +# urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], +# lon_0=lon_0, lat_0=lat_0, +# fix_aspect=self.keep_aspect, +# area_thresh=self.area_thresh, +# boundinglat=self.boundinglat, +# ) +# else: +# self.map = Basemap( +# projection=self.projection, resolution=self.resolution, +# llcrnrlon=self.lon[0], llcrnrlat=self.lat[0], +# urcrnrlon=self.lon[-1], urcrnrlat=self.lat[-1], +# lon_0=lon_0, lat_0=lat_0, +# fix_aspect=self.keep_aspect, +# area_thresh=self.area_thresh, +# boundinglat=self.boundinglat, +# ) glon, glat = grid #log.info("GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) @@ -408,10 +416,10 @@ class PlotMap(MapCross, MapDrawOptions): log.info("2. GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) - if not self.nomap: - x, y = self.map(*(glon, glat)) - else: - x, y = glon, glat +# if not self.nomap: +# x, y = self.map(*(glon, glat)) +# else: + x, y = glon, glat log.info("3. GLON: %s, GLAT: %s" % (str(x.shape), str(y.shape))) # print "map_data", map_data[0].shape @@ -427,6 +435,8 @@ class PlotMap(MapCross, MapDrawOptions): if self.nomap: self.mgplot.frameon = False + else: + self.mgaxis.coastlines() # Draw filled contour if not self.keep_aspect: @@ -463,28 +473,28 @@ class PlotMap(MapCross, MapDrawOptions): map_data[0] = np.ma.filled(map_data[0], np.nan) log.info(type(map_data[0])) - if not self.nocontourf and not self.nomap and self.smooth: - log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) - mco = self.map.contourf(x, y, - map_data[0], - cmap=self.cmap, - norm=self.norm, - levels=self.bounds, - extend=self.extend, - horizontalalignment='center', - alpha=self.alpha, - antialiased=True) - - elif not self.nocontourf and not self.nomap and not self.smooth: - log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) - mco = self.map.pcolormesh(x, y, - map_data[0], - cmap=self.cmap, - norm=self.norm, - alpha=self.alpha, - antialiased=True) - - elif not self.nocontourf and self.nomap and self.smooth: +# if not self.nocontourf and not self.nomap and self.smooth: +# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) +# mco = self.map.contourf(x, y, +# map_data[0], +# cmap=self.cmap, +# norm=self.norm, +# levels=self.bounds, +# extend=self.extend, +# horizontalalignment='center', +# alpha=self.alpha, +# antialiased=True) +# +# elif not self.nocontourf and not self.nomap and not self.smooth: +# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) +# mco = self.map.pcolormesh(x, y, +# map_data[0], +# cmap=self.cmap, +# norm=self.norm, +# alpha=self.alpha, +# antialiased=True) + + if not self.nocontourf and self.smooth: mco = plt.contourf(x, y, map_data[0], cmap=self.cmap, @@ -495,9 +505,9 @@ class PlotMap(MapCross, MapDrawOptions): alpha=self.alpha, antialiased=True) - plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) + #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) - elif not self.nocontourf and self.nomap and not self.smooth: + elif not self.nocontourf and not self.smooth: mco = plt.pcolormesh(x, y, map_data[0], cmap=self.cmap, @@ -505,15 +515,15 @@ class PlotMap(MapCross, MapDrawOptions): alpha=self.alpha, antialiased=True) - plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) + #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) # Draw shape files - if self.has_shape_files(): - line_w = float(self.countropts[0]) - for shapef in self.shapefiles: - log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) - self.map.readshapefile(shapef, "%s" % os.path.basename(shapef), linewidth=line_w, color=self.countropts[1]) - line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) +# if self.has_shape_files(): +# line_w = float(self.countropts[0]) +# for shapef in self.shapefiles: +# log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) +# self.map.readshapefile(shapef, "%s" % os.path.basename(shapef), linewidth=line_w, color=self.countropts[1]) +# line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) #self.print_time("contourf") ## FIXME Modify to use scatter inside DATA @@ -521,56 +531,56 @@ class PlotMap(MapCross, MapDrawOptions): if scatter_data is not None: log.info("Plotting scatter data: {} of keys {}".format(str(scatter_data), str(scatter_data.keys()))) if mco and self.bounds: - self.map.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - vmin=self.bounds[0], - vmax=self.bounds[-1], - cmap=self.cmap, - norm=self.norm, - zorder=10) + plt.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + vmin=self.bounds[0], + vmax=self.bounds[-1], + cmap=self.cmap, + norm=self.norm, + zorder=10) elif mco: - self.map.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - cmap=self.cmap, - norm=self.norm, - zorder=10) + plt.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + cmap=self.cmap, + norm=self.norm, + zorder=10) elif self.bounds: - mco = self.map.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - vmin=self.bounds[0], - vmax=self.bounds[-1], - cmap=self.cmap, - norm=self.norm, - zorder=10) + mco = plt.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + vmin=self.bounds[0], + vmax=self.bounds[-1], + cmap=self.cmap, + norm=self.norm, + zorder=10) else: - mco = self.map.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - cmap=self.cmap, - norm=self.norm, - zorder=10) - - if data.hasWindData(): - winds = data.getWindData() + mco = plt.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + cmap=self.cmap, + norm=self.norm, + zorder=10) + + if data.has_wind_data(): + winds = data.get_wind_data() X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) #print("Wind scale is", self.wind_units, self.wind_scale) if winds.has_key('barbs'): - self.map.barbs(X, Y, U, V, #units=self.wind_units, + plt.barbs(X, Y, U, V, #units=self.wind_units, #headlength=self.wind_head_length, #headwidth=self.wind_head_width, #width=self.wind_width, @@ -579,7 +589,7 @@ class PlotMap(MapCross, MapDrawOptions): color='k') #self.print_time("barbs") else: - Q = self.map.quiver(X, Y, U, V, units=self.wind_units, + Q = plt.quiver(X, Y, U, V, units=self.wind_units, headlength=self.wind_head_length, headwidth=self.wind_head_width, width=self.wind_width, @@ -596,12 +606,12 @@ class PlotMap(MapCross, MapDrawOptions): labelpos='S', labelsep=0.05) #self.print_time("quivers") - if data.hasContourData(): + if data.has_contour_data(): interval = self.contours_int exclude = self.contours_exclude_vals cLowBound = -99999 cUppBound = 99999 - cdata = data.getContourData() + cdata = data.get_contour_data() #print(cdata, type(cdata)) try: cMin = min(filter (lambda a: a > cLowBound, cdata.ravel())) @@ -617,7 +627,7 @@ class PlotMap(MapCross, MapDrawOptions): for ex in exclude: lvls = filter (lambda ex: ex != 0, lvls) if (len(lvls) > 0) and map_data != None: - mco = self.map.contourf(x, y, + mco = plt.contourf(x, y, map_data[0], cmap=self.cmap, norm=self.norm, @@ -662,12 +672,13 @@ class PlotMap(MapCross, MapDrawOptions): # self.map.drawparallels(np.arange(self.lat[0]+lat_offset, self.lat[1], self.lat[2]),labels=[1,0,0,0],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) # self.map.drawmeridians(np.arange(self.lon[0]+lon_offset, self.lon[1], self.lon[2]),labels=[0,0,0,1],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) #self.print_time("coords") - self.map.drawparallels(self.lat, labels=[1,0,0,0],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) - self.map.drawmeridians(self.lon, labels=[0,0,0,1],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) - self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) - self.map.drawcoastlines(linewidth=float(self.coastsopts[0]), color=str(self.coastsopts[1])) - if self.continents: - self.map.fillcontinents(color=self.continents, zorder=0) + self.mgaxis.gridlines(draw_labels=True, linestyle='--') +# self.map.drawparallels(self.lat, labels=[1,0,0,0],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) +# self.map.drawmeridians(self.lon, labels=[0,0,0,1],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) +# self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) +# self.map.drawcoastlines(linewidth=float(self.coastsopts[0]), color=str(self.coastsopts[1])) +# if self.continents: +# self.map.fillcontinents(color=self.continents, zorder=0) # drawopts = self.drawopts # if drawopts: @@ -689,28 +700,28 @@ class PlotMap(MapCross, MapDrawOptions): (not self.nocontourf or scatter_data is not None): self.set_color_bar(mco) - #m.bluemarble() - if self.background == 'shadedrelief': - self.map.shadedrelief() - if self.background == 'bluemarble': - self.map.bluemarble() - if self.background == 'etopo': - self.map.etopo() - if self.background == 'GIS': - h0 = float(self.lat[-1] - self.lat[0]) - w0 = float(self.lon[-1] - self.lon[0]) - ff = h0/w0 # form factor - height = 1024*ff # height - size = "%d,%d" % (1024, height) - basemap_url = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/export?\ -bbox=%s,%s,%s,%s&\ -bboxSR=4326&\ -imageSR=4326&\ -size=%s&\ -dpi=%d&\ -format=png24&\ -f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi) - self.map.imshow(imread(urlopen(basemap_url)), origin='upper') +# #m.bluemarble() +# if self.background == 'shadedrelief': +# self.map.shadedrelief() +# if self.background == 'bluemarble': +# self.map.bluemarble() +# if self.background == 'etopo': +# self.map.etopo() +# if self.background == 'GIS': +# h0 = float(self.lat[-1] - self.lat[0]) +# w0 = float(self.lon[-1] - self.lon[0]) +# ff = h0/w0 # form factor +# height = 1024*ff # height +# size = "%d,%d" % (1024, height) +# basemap_url = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/export?\ +#bbox=%s,%s,%s,%s&\ +#bboxSR=4326&\ +#imageSR=4326&\ +#size=%s&\ +#dpi=%d&\ +#format=png24&\ +#f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi) +# self.map.imshow(imread(urlopen(basemap_url)), origin='upper') #logo if self.logo: @@ -970,9 +981,13 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() - self.mgplot, self.mgaxis = plt.subplots() + self.mgaxis = plt.axes(projection=ccrs.PlateCarree()) + self.mgplot = plt.gcf() else: - self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2]) + self.mgaxis = plt.subplot(self.subplot[0], + self.subplot[1], + self.subplot[2], + projection=ccrs.PlateCarree()) self.mgplot = plt.gcf() fn = self.gen_image_map( @@ -1111,11 +1126,15 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): if self.subplot is None: plt.clf() - self.mgplot, self.mgaxis = plt.subplots() + self.mgaxis = plt.axes(projection=ccrs.PlateCarree()) + self.mgplot = plt.gcf() else: - self.mgplot, self.mgaxis = plt.subplot(self.subplot[0], - self.subplot[1], - self.subplot[3]) + self.mgaxis = plt.subplot(self.subplot[0], + self.subplot[1], + self.subplot[3], + projection=ccrs.PlateCarree() + ) + self.mgplot = plt.gcf() valid_tmp = dims[n_time] -- GitLab From 9996949f224107e513cf824146ca92e02a73b753 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Fri, 23 Oct 2020 10:25:39 +0200 Subject: [PATCH 08/27] Add option to set up number of binds in cmaps --- mapgenerator/plotting/definitions.py | 1 + mapgenerator/plotting/plotmap.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 0394c3c..b7f1063 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -713,6 +713,7 @@ class MapCross(MapGenerator): # self.over = kwargs.get('over', None) # self.under = kwargs.get('under', None) self.bad = kwargs.get('bad', None) + self.N = kwargs.get('N', None) self.lat = kwargs.get('lat', []) self.lon = kwargs.get('lon', []) self.wind = kwargs.get('wind', []) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index c362460..2dd4fdd 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -131,11 +131,15 @@ class PlotMap(MapCross, MapDrawOptions): custom_cmap = True else: try: - self.cmap = mpl.cm.get_cmap(self.colors) + if self.N: + self.cmap = mpl.cm.get_cmap(self.colors, self.N) + else: + self.cmap = mpl.cm.get_cmap(self.colors) except: self.cmap = mpl.cm.get_cmap('jet') custom_cmap = False + if self.bad: self.cmap.set_bad(self.bad) -- GitLab From c4273cbc063b6afc1ad0e13f271c415e0c32d6eb Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Fri, 23 Oct 2020 11:45:47 +0200 Subject: [PATCH 09/27] Make default title available also for maps --- mapgenerator/plotting/definitions.py | 12 ++++++++++++ mapgenerator/plotting/plotmap.py | 2 +- mapgenerator/plotting/timeseries.py | 12 +----------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index b7f1063..82e9736 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -700,6 +700,18 @@ class MapGenerator(object): self.shapefiles = kwargs.get('shapefiles', None) self.dpi = kwargs.get('dpi', 200) + @staticmethod + def _get_default_title(cube): + if cube.attributes.get('plot_name', None): + show_name = cube.attributes['plot_name'] + elif cube.long_name and len(cube.long_name) < 45: + show_name = cube.long_name + elif cube.standard_name and len(cube.standard_name) < 45: + show_name = cube.standard_name + else: + show_name = cube.var_name + return f"{show_name} ({cube.units})" + class MapCross(MapGenerator): """ Define common attributes to maps and cross sections """ diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 2dd4fdd..3b07759 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -975,7 +975,7 @@ f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi def plot_cube(self, cube, **kwargs): if 'title' not in kwargs: - kwargs['title'] = '{0.long_name} ({0.units})'.format(cube) + kwargs['title'] = self._get_default_title(cube) self.aplot( cube.coord('longitude').points, diff --git a/mapgenerator/plotting/timeseries.py b/mapgenerator/plotting/timeseries.py index d9e8fd3..5fbf7b9 100644 --- a/mapgenerator/plotting/timeseries.py +++ b/mapgenerator/plotting/timeseries.py @@ -178,17 +178,7 @@ class PlotSeries(MapGenerator): fullname = os.path.join(self.outdir ,f"{name}.{self.filefmt}") self._current_fig.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) - @staticmethod - def _get_default_title(cube): - if cube.attributes.get('plot_name', None): - show_name = cube.attributes['plot_name'] - elif cube.long_name and len(cube.long_name) < 45: - show_name = cube.long_name - elif cube.standard_name and len(cube.standard_name) < 45: - show_name = cube.standard_name - else: - show_name = cube.var_name - return f"{show_name} ({cube.units})" + @staticmethod def _set_time_axis(coord): -- GitLab From f2a24ae796108abcc5f78f84ff12a51bcc53e3d2 Mon Sep 17 00:00:00 2001 From: Francesco Date: Fri, 23 Oct 2020 18:12:31 +0200 Subject: [PATCH 10/27] Basemap removed --- mapgenerator/plotting/config.py | 6 +- mapgenerator/plotting/plotmap.py | 181 ++++++++++++++++--------------- 2 files changed, 97 insertions(+), 90 deletions(-) diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index 35cf6c3..ccba265 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -13,7 +13,7 @@ from mapgenerator import mg_exceptions import logging -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) @@ -61,7 +61,7 @@ class ArgumentParser(object): dest="loglevel", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], - help="logging levelbug (default=WARNING)") + help="logging levelbug (default=INFO)") self.parser.add_argument("--indir", dest="indir", @@ -115,7 +115,7 @@ class ArgumentParser(object): help="extend colormap extremes (default='neither')") self.parser.add_argument("--resolution", dest="resolution", - choices=['c', 'l', 'i', 'h', 'f'], + choices=['110m', '50m', '10m'], help="details level of coastlines/coutries") self.parser.add_argument("--dpi", dest="dpi", diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 6d62c20..148e574 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -23,9 +23,6 @@ import subprocess #import sys from collections import Iterable from PIL import Image -#from mapgenerator.plotting.mapgenerator import MapCross -#from mapgenerator.plotting.mapgenerator import MapDrawOptions -#from mapgenerator.plotting.mapgenerator import MapDataHandler from .definitions import MapData from .definitions import MapCross from .definitions import MapDrawOptions @@ -67,7 +64,7 @@ class PlotCross(object): class PlotMap(MapCross, MapDrawOptions): """ Main class for plotting maps """ - def __init__(self, loglevel='WARNING', **kwargs): + def __init__(self, loglevel='INFO', **kwargs): """ Initialize class with attributes """ MapCross.__init__(self, loglevel, **kwargs) MapDrawOptions.__init__(self, loglevel, **kwargs) @@ -104,18 +101,14 @@ class PlotMap(MapCross, MapDrawOptions): minlon, maxlon = self.lon[0], self.lon[-1] lats = maxlat + abs(minlat) lons = maxlon + abs(minlon) - if lats > 130 and lons > 260: - self.resolution = 'c' - elif lats > 100 and lons > 200: - self.resolution = 'l' - elif lats > 60 and lons > 120: - self.resolution = 'i' - elif lats > 30 and lons > 60: - self.resolution = 'h' - elif lats <= 30 and lons <= 60: - self.resolution = 'f' + if lats > 100 and lons > 200: + self.resolution = '110m' + elif lats > 45 and lons > 75: + self.resolution = '50m' + elif lats <= 45 and lons <= 75: + self.resolution = '10m' else: - self.resolution = 'c' # default + self.resolution = '110m' # default def set_color_map(self): """ Create color map """ @@ -198,9 +191,9 @@ class PlotMap(MapCross, MapDrawOptions): # pad="2%", size="3%", extend=self.extend, # drawedges=drawedges) # else: - cb = plt.colorbar(mco, ticks=self.ticks, format=self.formats, - pad=.06, extend=self.extend, - drawedges=drawedges) + cb = self.mgplot.colorbar(mco, ticks=self.ticks, + format=self.formats, pad=.06, + extend=self.extend, drawedges=drawedges) cb.ax.tick_params(labelsize=float(self.coordsopts[1])) for lin in cb.ax.yaxis.get_ticklines(): @@ -359,8 +352,8 @@ class PlotMap(MapCross, MapDrawOptions): map_data = data.get_map_data() # FIXME scatter_data = data.get_scatter_data() or cur_scatter_data - if self.subplot is None: - plt.clf() + #if self.subplot is None: + # self.mgplot.clear() # params = { # 'font.size': 14, # 'text.fontsize': 28, @@ -428,15 +421,14 @@ class PlotMap(MapCross, MapDrawOptions): #log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) # # nomap option -# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, varName, fig_name)) and not self.overwrite: +# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, varName, +# fig_name)) and not self.overwrite: # print fig_name, " already exists." # plt.clf() # return fig_name if self.nomap: self.mgplot.frameon = False - else: - self.mgaxis.coastlines() # Draw filled contour if not self.keep_aspect: @@ -453,8 +445,9 @@ class PlotMap(MapCross, MapDrawOptions): ystart_norm = (0.8 - ysize_norm) / 2 + ystart_base_norm xstart_norm = (0.8 - xsize_norm) / 2 + xstart_base_norm #ax_map = self.mgaxis.axes([xstart_norm, ystart_norm, xsize_norm, ysize_norm]) -# log.info("Ynorm: %s, Xnorm: %s, Ystart: %s, Xstart: %s" % (ysize_norm, xsize_norm, ystart_norm, xstart_norm)) - self.mgplot.add_axes([xstart_norm, ystart_norm, xsize_norm, ysize_norm]) +# log.info("Ynorm: %s, Xnorm: %s, Ystart: %s, Xstart: %s" % +# (ysize_norm, xsize_norm, ystart_norm, xstart_norm)) + # self.mgplot.add_axes([xstart_norm, ystart_norm, xsize_norm, ysize_norm]) #print "Switching AXES for MAP:", ax_map if self.alpha != None and self.bounds != None: @@ -495,25 +488,24 @@ class PlotMap(MapCross, MapDrawOptions): # antialiased=True) if not self.nocontourf and self.smooth: - mco = plt.contourf(x, y, - map_data[0], - cmap=self.cmap, - norm=self.norm, - levels=self.bounds, - extend=self.extend, - horizontalalignment='center', - alpha=self.alpha, - antialiased=True) + mco = self.mgaxis.contourf(x, y, + map_data[0], + cmap=self.cmap, + norm=self.norm, + levels=self.bounds, + extend=self.extend, + alpha=self.alpha, + antialiased=True) #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) elif not self.nocontourf and not self.smooth: - mco = plt.pcolormesh(x, y, - map_data[0], - cmap=self.cmap, - norm=self.norm, - alpha=self.alpha, - antialiased=True) + mco = self.mgaxis.pcolormesh(x, y, + map_data[0], + cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + antialiased=True) #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) @@ -609,54 +601,56 @@ class PlotMap(MapCross, MapDrawOptions): if data.has_contour_data(): interval = self.contours_int exclude = self.contours_exclude_vals - cLowBound = -99999 - cUppBound = 99999 + clow_bound = -99999 + cupp_bound = 99999 cdata = data.get_contour_data() #print(cdata, type(cdata)) try: - cMin = min(filter (lambda a: a > cLowBound, cdata.ravel())) - adjcMin = int(cMin - (cMin % interval) - interval * 2) + # cmin = min(filter (lambda a: a > clow_bound, cdata.ravel())) + cmin = cdata.where(cdata > clow_bound).min() + adjcmin = int(cmin - (cmin % interval) - interval * 2) except ValueError: - cMin =0 + cmin = 0 try: - cMax = max(filter (lambda a: a < cUppBound, cdata.ravel())) - adjcMax = int(cMax - (cMax % interval) + interval * 2) + # cmax = max(filter (lambda a: a < cupp_bound, cdata.ravel())) + cmax = cdata.where(cdata < cupp_bound).max() + adjcmax = int(cmax - (cmax % interval) + interval * 2) except ValueError: - cMax = 0 - lvls = np.arange(adjcMin, adjcMax, interval) - for ex in exclude: - lvls = filter (lambda ex: ex != 0, lvls) + cmax = 0 + lvls = np.arange(adjcmin, adjcmax, interval) + for exc in exclude: + lvls = [exc for exc in lvls if exc != 0] if (len(lvls) > 0) and map_data != None: mco = plt.contourf(x, y, - map_data[0], - cmap=self.cmap, - norm=self.norm, - levels=self.bounds, - extend=self.extend, - horizontalalignment='center', - alpha=self.alpha) + map_data[0], + cmap=self.cmap, + norm=self.norm, + levels=self.bounds, + extend=self.extend, + horizontalalignment='center', + alpha=self.alpha) #print "-----------------", map_data, cdata, "--------------------" if map_data != None and (map_data[0] == cdata).all(): - print(":::::::::::::::::::::::: SAME !!! :::::::::::::::::::") - CS = plt.contour(x, y, - cdata, - levels=self.bounds, - colors=self.contours_color, - linewidths=self.contours_linewidth, - alpha=self.alpha) + log.debug(":::::::::::::::::::::::: SAME !!! :::::::::::::::::::") + mcs = plt.contour(x, y, + cdata, + levels=self.bounds, + colors=self.contours_color, + linewidths=self.contours_linewidth, + alpha=self.alpha) else: - print(":::::::::::::::::::::::: DIFFERENT !!! :::::::::::::::::::") - print("MIN:", cMin, "MAX:", cMax) - CS = plt.contour(x, y, - cdata, - levels=lvls, - colors=self.contours_color, - linewidths=self.contours_linewidth, - alpha=self.alpha) + log.debug(":::::::::::::::::::::::: DIFFERENT !!! :::::::::::::::::::") + log.debug("MIN: %s - MAX: %s" % (cmin, cmax)) + mcs = plt.contour(x, y, + cdata, + levels=lvls, + colors=self.contours_color, + linewidths=self.contours_linewidth, + alpha=self.alpha) if self.contours_label: - self.mgplot.clabel(CS, + self.mgplot.clabel(mcs, inline=1, fontsize=self.contours_label_fontsize, #backgroundcolor='r', @@ -672,7 +666,9 @@ class PlotMap(MapCross, MapDrawOptions): # self.map.drawparallels(np.arange(self.lat[0]+lat_offset, self.lat[1], self.lat[2]),labels=[1,0,0,0],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) # self.map.drawmeridians(np.arange(self.lon[0]+lon_offset, self.lon[1], self.lon[2]),labels=[0,0,0,1],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) #self.print_time("coords") - self.mgaxis.gridlines(draw_labels=True, linestyle='--') + print("******", self.resolution, "*******") + _ = self.mgaxis.gridlines(draw_labels=True, linestyle='--') + _ = self.mgaxis.coastlines(resolution=self.resolution) # self.map.drawparallels(self.lat, labels=[1,0,0,0],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) # self.map.drawmeridians(self.lon, labels=[0,0,0,1],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) # self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) @@ -725,13 +721,13 @@ class PlotMap(MapCross, MapDrawOptions): #logo if self.logo: - im = Image.open(self.logo[0]) + img = Image.open(self.logo[0]) # height = im.size[1] # width = im.size[0] # We need a float array between 0-1, rather than # a uint8 array between 0-255 - nim = np.array(im).astype(np.float) / 255 + nim = np.array(img).astype(np.float) / 255 # With newer (1.0) versions of matplotlib, you can # use the "zorder" kwarg to make the image overlay @@ -740,7 +736,7 @@ class PlotMap(MapCross, MapDrawOptions): # SAVEFIG fullname = "{}.{}".format(fig_name, self.filefmt) - log.warning("printing {}".format(fullname)) + log.info("printing {}".format(fullname)) if self.kml or self.kmz: #self.mgplot.axes(frameon=0) if self.save: @@ -770,8 +766,13 @@ class PlotMap(MapCross, MapDrawOptions): ) else: self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0,) + if self.save: - self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) + #fig = self.mgaxis.get_figure() + self.mgplot.savefig(fullname, + bbox_inches='tight', + pad_inches=.2, dpi=self.dpi) + return fig_name @@ -1055,7 +1056,7 @@ class PlotMap(MapCross, MapDrawOptions): self.wind_scale = int(self.windopts[3]); log.info("SHP {}".format(str(self.shapefiles))) if self.shapefiles: - self.shapefiles = [f.replace(".shp","") for f in glob(self.shapefiles)] + self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] log.info("SHP {}".format(str(self.shapefiles))) if self.scatter is not None: @@ -1116,12 +1117,12 @@ class PlotMap(MapCross, MapDrawOptions): ss_date = s_date.strftime("%Y%m%d") steps = [] - START = self.timesteps[0] + start = self.timesteps[0] #log.info("Start @ %s - s_date: %s - run_tmp: %s" % (START, s_date, run_tmp)) - if START is None: - START = 0 + if start is None: + start = 0 for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): if self.subplot is None: @@ -1138,14 +1139,19 @@ class PlotMap(MapCross, MapDrawOptions): valid_tmp = dims[n_time] - curr_date = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], valid_tmp[-9:-7], valid_tmp[0:2]), "%Y %b %d %H" ) + curr_date = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], + valid_tmp[-7:-4], + valid_tmp[-9:-7], + valid_tmp[0:2]), + "%Y %b %d %H") s_time = "%02d" % n_time steps.append(s_time) p_time = (curr_date - s_date).total_seconds()/3600 - s_time24 = "%02d" % (p_time%24) - #s_time3d = "%03d" % p_time # Currently unused, only for Valentina who has more than 99 img in a set + # s_time24 = "%02d" % (p_time%24) + # s_time3d = "%03d" % p_time # Currently unused, only for + # Valentina who has more than 99 img in a set if self.img_template: f_name = self.img_template % {'date': ss_date, 'step': s_time} @@ -1197,7 +1203,7 @@ class PlotMap(MapCross, MapDrawOptions): #separate colorbar mpl.rcParams['axes.linewidth'] = 0.1 fig = plt.figure(figsize=(.1, 12)) - ax = fig.add_axes([0.05, 0.80, 0.9, 0.15], axisbg=(1, 1, 1, 0)) + #ax = fig.add_axes([0.05, 0.80, 0.9, 0.15], axisbg=(1, 1, 1, 0)) print("...", self.bounds, "...") cb = mpl.colorbar.ColorbarBase( ax, @@ -1239,6 +1245,7 @@ class PlotMap(MapCross, MapDrawOptions): os.mkdir("%s-%s" % (run_date, self.srcvars[0])) except: pass + if self.save: fig.savefig( "%s/%s-%s/%s-colorbar.png" % (self.outdir, run_date, self.srcvars[0], run_date), -- GitLab From 0b2dc79dafbd1a5a00232d926c54b44f7906e52f Mon Sep 17 00:00:00 2001 From: Francesco Date: Mon, 26 Oct 2020 18:02:24 +0100 Subject: [PATCH 11/27] Fixed issues on Cartopy, set aspect etc. Still missing multiple projections --- mapgenerator/plotting/config.py | 8 +- mapgenerator/plotting/definitions.py | 7 +- mapgenerator/plotting/plotmap.py | 253 ++++++++++++++------------- 3 files changed, 132 insertions(+), 136 deletions(-) diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index ccba265..5c41df8 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -144,9 +144,6 @@ class ArgumentParser(object): self.parser.add_argument("--contours", dest="contours", help="additional contour variable") - self.parser.add_argument("--keep_aspect", - dest="keep_aspect", - help="preserve map aspect ratio when drawing (default=False)") self.parser.add_argument("--shapefiles", dest="shapefiles", help="additional shape files") @@ -164,10 +161,7 @@ class ArgumentParser(object): help="proportion of Y dimension (between 0 and 1)") self.parser.add_argument("--projection", dest="projection", - help="map projection to draw (default=cyl)") - self.parser.add_argument("--area_thresh", - dest="area_thresh", - help="area threshold in surface drawing (including lakes)") + help="map projection to draw (default=PlateCarree)") self.parser.add_argument("--title", dest="title", diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index efcc398..88a52ee 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -634,8 +634,8 @@ class MapDrawOptions(object): ## General Options self.area_thresh = None # Default area threshold for maps self.resolution = None # Default resolution is intermediate - self.xsize = '1.' # (when keep_aspect = False) Default horizontal size [value between 0 and 1] - self.ysize = '1.' # (when keep_aspect = False) Default vertical size [value between 0 and 1] + self.xsize = '1.' # Default horizontal size [value between 0 and 1] + self.ysize = '1.' # Default vertical size [value between 0 and 1] self.dpi = '200' # Default DPI self.coordsopts = ".3,8,grey" self.coastsopts = ".5,grey" @@ -702,8 +702,7 @@ class MapGenerator(object): self.srcgaps = kwargs.get('srcgaps', [0]) self.title = kwargs.get('title', '') self.fontsize = kwargs.get('fontsize', 12) - self.keep_aspect = kwargs.get('keep_aspect', False) - self.projection = kwargs.get('projection', 'cyl') + self.projection = kwargs.get('projection', 'PlateCarree') self.subsetting = kwargs.get('subsetting', True) self.dimension = kwargs.get('dimension', None) self.continents = kwargs.get('continents', None) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 148e574..cba5d6b 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -11,7 +11,8 @@ mpl.use('Agg') import matplotlib.pyplot as plt # from mpl_toolkits.basemap import Basemap import cartopy.crs as ccrs -# import cartopy.feature as cpf +import cartopy.feature as cfeature +import cartopy.io.shapereader as shpreader from matplotlib.cbook import delete_masked_points # from matplotlib.image import imread import numpy as np @@ -166,7 +167,7 @@ class PlotMap(MapCross, MapDrawOptions): self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) - def set_color_bar(self, mco, location='left', drawedges=False, cax=None): + def set_color_bar(self, mco, location='right', drawedges=False, cax=None): """ Create color bar """ # xs = self.xsize # nap = .8-((1-xs)*0.4) @@ -186,17 +187,15 @@ class PlotMap(MapCross, MapDrawOptions): pass log.info("***** {} *****".format(self.formats)) -# if self.map: -# cb = self.map.colorbar(mco, ticks=self.ticks, format=self.formats, -# pad="2%", size="3%", extend=self.extend, -# drawedges=drawedges) -# else: - cb = self.mgplot.colorbar(mco, ticks=self.ticks, - format=self.formats, pad=.06, - extend=self.extend, drawedges=drawedges) - - cb.ax.tick_params(labelsize=float(self.coordsopts[1])) - for lin in cb.ax.yaxis.get_ticklines(): + cax, kwargs = mpl.colorbar.make_axes(self.mgaxis, location=location, + pad=0.02, shrink=0.8) + cbar = self.mgplot.colorbar(mco, cax=cax, ax=self.mgaxis, + ticks=self.ticks, format=self.formats, + pad=.06, extend=self.extend, + drawedges=drawedges, **kwargs) + + cbar.ax.tick_params(labelsize=float(self.coordsopts[1])) + for lin in cbar.ax.yaxis.get_ticklines(): lin.set_visible(False) #self.print_time("colorbar") @@ -363,6 +362,9 @@ class PlotMap(MapCross, MapDrawOptions): # # mpl.rcParams.update(params) + # Draw filled contour + self.mgaxis.set_aspect(self.xsize/self.ysize) + if not self.nomap: if self.projection != 'PlateCarree': lon_0 = (self.lon[-1]-abs(self.lon[0]))/2 @@ -371,8 +373,9 @@ class PlotMap(MapCross, MapDrawOptions): else: lon_0, lat_0 = None, None - self.mgaxis.set_extent([self.lon[0], self.lon[-1], self.lat[0], - self.lat[-1]]) + self.mgaxis.set_extent([self.lon[0], self.lon[-1], + self.lat[0], self.lat[-1]], + crs=getattr(ccrs, self.projection)()) # if self.subplot: # self.map = Basemap( # ax=self.mgaxis, @@ -430,26 +433,6 @@ class PlotMap(MapCross, MapDrawOptions): if self.nomap: self.mgplot.frameon = False - # Draw filled contour - if not self.keep_aspect: -# log.info("Not keeping original aspect") -# log.info("XSIZE: %s" % self.xsize) -# log.info("YSIZE: %s" % self.ysize) - ysize_norm = self.ysize*0.8 - xsize_norm = self.xsize*0.8 - rows = img_title.count("\n") - blines = max(rows - 2, 0) - param = self.fontsize/400 - ystart_base_norm = 0.1 - param*(blines)#img_title - xstart_base_norm = 0.1 - ystart_norm = (0.8 - ysize_norm) / 2 + ystart_base_norm - xstart_norm = (0.8 - xsize_norm) / 2 + xstart_base_norm - #ax_map = self.mgaxis.axes([xstart_norm, ystart_norm, xsize_norm, ysize_norm]) -# log.info("Ynorm: %s, Xnorm: %s, Ystart: %s, Xstart: %s" % -# (ysize_norm, xsize_norm, ystart_norm, xstart_norm)) - # self.mgplot.add_axes([xstart_norm, ystart_norm, xsize_norm, ysize_norm]) - #print "Switching AXES for MAP:", ax_map - if self.alpha != None and self.bounds != None: if self.extend in ('min', 'both'): map_data[0] = np.ma.masked_where(map_data[0] < self.bounds[0], map_data[0]) @@ -495,6 +478,7 @@ class PlotMap(MapCross, MapDrawOptions): levels=self.bounds, extend=self.extend, alpha=self.alpha, + transform=ccrs.PlateCarree(), antialiased=True) #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) @@ -510,12 +494,19 @@ class PlotMap(MapCross, MapDrawOptions): #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) # Draw shape files -# if self.has_shape_files(): -# line_w = float(self.countropts[0]) -# for shapef in self.shapefiles: -# log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) -# self.map.readshapefile(shapef, "%s" % os.path.basename(shapef), linewidth=line_w, color=self.countropts[1]) -# line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) + if self.has_shape_files(): + line_w = float(self.countropts[0]) + for shapef in self.shapefiles: + log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) + adm1_shapes = list(shpreader.Reader(shapef).geometries()) + self.mgaxis.add_geometries(adm1_shapes, getattr(ccrs, + self.projection)(), + linewidth=line_w, + edgecolor=self.countropts[1]) + # self.map.readshapefile(shapef, "%s" % + # os.path.basename(shapef), linewidth=line_w, + # color=self.countropts[1]) + line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) #self.print_time("contourf") ## FIXME Modify to use scatter inside DATA @@ -523,71 +514,71 @@ class PlotMap(MapCross, MapDrawOptions): if scatter_data is not None: log.info("Plotting scatter data: {} of keys {}".format(str(scatter_data), str(scatter_data.keys()))) if mco and self.bounds: - plt.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - vmin=self.bounds[0], - vmax=self.bounds[-1], - cmap=self.cmap, - norm=self.norm, - zorder=10) + self.mgaxis.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + vmin=self.bounds[0], + vmax=self.bounds[-1], + cmap=self.cmap, + norm=self.norm, + zorder=10) elif mco: - plt.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - cmap=self.cmap, - norm=self.norm, - zorder=10) + self.mgaxis.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + cmap=self.cmap, + norm=self.norm, + zorder=10) elif self.bounds: - mco = plt.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - vmin=self.bounds[0], - vmax=self.bounds[-1], - cmap=self.cmap, - norm=self.norm, - zorder=10) + mco = self.mgaxis.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + vmin=self.bounds[0], + vmax=self.bounds[-1], + cmap=self.cmap, + norm=self.norm, + zorder=10) else: - mco = plt.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - cmap=self.cmap, - norm=self.norm, - zorder=10) + mco = self.mgaxis.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + cmap=self.cmap, + norm=self.norm, + zorder=10) if data.has_wind_data(): winds = data.get_wind_data() X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) #print("Wind scale is", self.wind_units, self.wind_scale) if winds.has_key('barbs'): - plt.barbs(X, Y, U, V, #units=self.wind_units, - #headlength=self.wind_head_length, - #headwidth=self.wind_head_width, - #width=self.wind_width, - #minshaft=self.wind_minshaft, - #scale=self.wind_scale, - color='k') + self.mgaxis.barbs(X, Y, U, V, #units=self.wind_units, + #headlength=self.wind_head_length, + #headwidth=self.wind_head_width, + #width=self.wind_width, + #minshaft=self.wind_minshaft, + #scale=self.wind_scale, + color='k') #self.print_time("barbs") else: - Q = plt.quiver(X, Y, U, V, units=self.wind_units, - headlength=self.wind_head_length, - headwidth=self.wind_head_width, - width=self.wind_width, - minshaft=self.wind_minshaft, - scale=self.wind_scale, - color='gray') + Q = self.mgaxis.quiver(X, Y, U, V, units=self.wind_units, + headlength=self.wind_head_length, + headwidth=self.wind_head_width, + width=self.wind_width, + minshaft=self.wind_minshaft, + scale=self.wind_scale, + color='gray') # Draw the key plt.quiverkey(Q, self.wind_label_xpos, @@ -666,11 +657,39 @@ class PlotMap(MapCross, MapDrawOptions): # self.map.drawparallels(np.arange(self.lat[0]+lat_offset, self.lat[1], self.lat[2]),labels=[1,0,0,0],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) # self.map.drawmeridians(np.arange(self.lon[0]+lon_offset, self.lon[1], self.lon[2]),labels=[0,0,0,1],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) #self.print_time("coords") - print("******", self.resolution, "*******") - _ = self.mgaxis.gridlines(draw_labels=True, linestyle='--') - _ = self.mgaxis.coastlines(resolution=self.resolution) -# self.map.drawparallels(self.lat, labels=[1,0,0,0],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) -# self.map.drawmeridians(self.lon, labels=[0,0,0,1],linewidth=float(self.coordsopts[0]), fontsize=float(self.coordsopts[1]), color=str(self.coordsopts[2])) + _ = self.mgaxis.coastlines(resolution=self.resolution, + linewidth=float(self.coastsopts[0]), + color=str(self.coastsopts[1])) + + _ = self.mgaxis.add_feature(cfeature.BORDERS, #.with_scale(self.resolution), + linewidth=float(self.countropts[0]), + color=str(self.countropts[1])) + +# _ = self.mgaxis.add_feature(cfeature.COASTLINE, # coastlines(resolution=self.resolution, +# linewidth=float(self.coastsopts[0]), +# color=str(self.coastsopts[1])) + + if self.projection == 'PlateCarree': + grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, + draw_labels=True, linestyle='--', + linewidth=float(self.coordsopts[0]), + color=str(self.coordsopts[2]), + crs=getattr(ccrs, self.projection)() + ) + + grl.xlabels_top = False + grl.ylabels_right = False + grl.xlabel_style = {'size': float(self.coordsopts[1])} + grl.ylabel_style = grl.xlabel_style + else: + grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, + draw_labels=False, linestyle='--', + linewidth=float(self.coordsopts[0]), + color=str(self.coordsopts[2]), + crs=getattr(ccrs, self.projection)() + ) + + # self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) # self.map.drawcoastlines(linewidth=float(self.coastsopts[0]), color=str(self.coastsopts[1])) # if self.continents: @@ -745,27 +764,7 @@ class PlotMap(MapCross, MapDrawOptions): frameon=0, pad_inches=0, dpi=self.dpi, transparent=True) else: - #a = plt.axes([0.0, 0.0, 1.0, 1.0]) - #plt.axis('off') - plt.setp(self.mgaxis.spines.values(), linewidth=.1) - xpos = self.xsize/2 - if not self.keep_aspect: - ypos = ystart_norm + ysize_norm + 0.05 - else: - ypos = self.ysize*0.95 - - if self.subplot is None: - self.mgplot.text( - xpos, - ypos, - img_title, - horizontalalignment='center', - verticalalignment='top', - fontsize=self.fontsize, - zorder=0, - ) - else: - self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0,) + self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0,) if self.save: #fig = self.mgaxis.get_figure() @@ -982,13 +981,15 @@ class PlotMap(MapCross, MapDrawOptions): fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=ccrs.PlateCarree()) + self.mgaxis = plt.axes(projection=getattr(ccrs, + self.projection)()) #.PlateCarree()) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2], - projection=ccrs.PlateCarree()) + projection=getattr(ccrs, + self.projection())) self.mgplot = plt.gcf() fn = self.gen_image_map( @@ -1127,13 +1128,15 @@ class PlotMap(MapCross, MapDrawOptions): for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=ccrs.PlateCarree()) + self.mgaxis = plt.axes(projection=getattr(ccrs, + self.projection)()) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[3], - projection=ccrs.PlateCarree() + projection=getattr(ccrs, + self.projection)() ) self.mgplot = plt.gcf() -- GitLab From 68faa473134fb3bf7b42054cf968da1911491643 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Tue, 27 Oct 2020 10:11:07 +0100 Subject: [PATCH 12/27] Fix some plotmap issues --- mapgenerator/plotting/plotmap.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 2e8e2e1..1273d58 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -348,9 +348,9 @@ class PlotMap(MapCross, MapDrawOptions): plt.clf() return fig_name - map_data = data.getMapData() + map_data = data.map_data # FIXME - scatter_data = data.getScatterData() or cur_scatter_data + scatter_data = data.scatter_data or cur_scatter_data if self.subplot is None: plt.clf() # params = { @@ -562,8 +562,8 @@ class PlotMap(MapCross, MapDrawOptions): norm=self.norm, zorder=10) - if data.hasWindData(): - winds = data.getWindData() + if data.wind_data: + winds = data.wind_data X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) #print("Wind scale is", self.wind_units, self.wind_scale) if winds.has_key('barbs'): @@ -593,12 +593,12 @@ class PlotMap(MapCross, MapDrawOptions): labelpos='S', labelsep=0.05) #self.print_time("quivers") - if data.hasContourData(): + if data.contour_data: interval = self.contours_int exclude = self.contours_exclude_vals cLowBound = -99999 cUppBound = 99999 - cdata = data.getContourData() + cdata = data.contour_data #print(cdata, type(cdata)) try: cMin = min(filter (lambda a: a > cLowBound, cdata.ravel())) -- GitLab From 2b5045abc8999cfe98ccf60dc82d0f204dd51cd3 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Wed, 28 Oct 2020 11:58:52 +0100 Subject: [PATCH 13/27] Small improvements --- mapgenerator/plotting/definitions.py | 1 + mapgenerator/plotting/plotmap.py | 40 ++++++++++------------------ 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 88a52ee..27353db 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -703,6 +703,7 @@ class MapGenerator(object): self.title = kwargs.get('title', '') self.fontsize = kwargs.get('fontsize', 12) self.projection = kwargs.get('projection', 'PlateCarree') + self.projection_kwargs = kwargs.get('projection_kwargs', dict()) self.subsetting = kwargs.get('subsetting', True) self.dimension = kwargs.get('dimension', None) self.continents = kwargs.get('continents', None) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index cba5d6b..14c5375 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -94,6 +94,10 @@ class PlotMap(MapCross, MapDrawOptions): #log.info("SETATTR: {} - {}".format(key, value)) super(PlotMap, self).__setattr__(key, parse_parameter(key, value)) + + def _build_projection(self): + self._crs = getattr(ccrs, self.projection)(**self.projection_kwargs) + def set_resolution(self): """ 'c','l','i','h','f' """ # lats = 36, 72, 108, 144, 180 @@ -320,6 +324,7 @@ class PlotMap(MapCross, MapDrawOptions): self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace('-', 'm'), str(round(glon.max(), 1)).replace('-', 'm')) + self._build_projection() #print(self.lon, self.lat) # # Fix the printout of tick values to avoid .0 decimals in integers # strs = [] @@ -365,17 +370,9 @@ class PlotMap(MapCross, MapDrawOptions): # Draw filled contour self.mgaxis.set_aspect(self.xsize/self.ysize) - if not self.nomap: - if self.projection != 'PlateCarree': - lon_0 = (self.lon[-1]-abs(self.lon[0]))/2 - lat_0 = (self.lat[-1]-abs(self.lat[0]))/2 - #print("-----------", lon_0, lat_0, "-----------") - else: - lon_0, lat_0 = None, None - - self.mgaxis.set_extent([self.lon[0], self.lon[-1], - self.lat[0], self.lat[-1]], - crs=getattr(ccrs, self.projection)()) + # self.mgaxis.set_extent([self.lon[0], self.lon[-1], + # self.lat[0], self.lat[-1]], + # crs=ccrs.PlateCarree()) # if self.subplot: # self.map = Basemap( # ax=self.mgaxis, @@ -481,7 +478,6 @@ class PlotMap(MapCross, MapDrawOptions): transform=ccrs.PlateCarree(), antialiased=True) - #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) elif not self.nocontourf and not self.smooth: mco = self.mgaxis.pcolormesh(x, y, @@ -489,18 +485,16 @@ class PlotMap(MapCross, MapDrawOptions): cmap=self.cmap, norm=self.norm, alpha=self.alpha, + transform=ccrs.PlateCarree(), antialiased=True) - #plt.axis([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]]) - # Draw shape files if self.has_shape_files(): line_w = float(self.countropts[0]) for shapef in self.shapefiles: log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) adm1_shapes = list(shpreader.Reader(shapef).geometries()) - self.mgaxis.add_geometries(adm1_shapes, getattr(ccrs, - self.projection)(), + self.mgaxis.add_geometries(adm1_shapes, self._crs, linewidth=line_w, edgecolor=self.countropts[1]) # self.map.readshapefile(shapef, "%s" % @@ -674,7 +668,6 @@ class PlotMap(MapCross, MapDrawOptions): draw_labels=True, linestyle='--', linewidth=float(self.coordsopts[0]), color=str(self.coordsopts[2]), - crs=getattr(ccrs, self.projection)() ) grl.xlabels_top = False @@ -686,7 +679,6 @@ class PlotMap(MapCross, MapDrawOptions): draw_labels=False, linestyle='--', linewidth=float(self.coordsopts[0]), color=str(self.coordsopts[2]), - crs=getattr(ccrs, self.projection)() ) @@ -981,15 +973,13 @@ class PlotMap(MapCross, MapDrawOptions): fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=getattr(ccrs, - self.projection)()) #.PlateCarree()) + self.mgaxis = plt.axes(projection=self._crs) #.PlateCarree()) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[2], - projection=getattr(ccrs, - self.projection())) + projection=self._crs) self.mgplot = plt.gcf() fn = self.gen_image_map( @@ -1128,15 +1118,13 @@ class PlotMap(MapCross, MapDrawOptions): for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=getattr(ccrs, - self.projection)()) + self.mgaxis = plt.axes(projection=self._crs) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[3], - projection=getattr(ccrs, - self.projection)() + projection=self._crs ) self.mgplot = plt.gcf() -- GitLab From c0bb070ee2f2f72693cce04a40ed3a8383747cc4 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 28 Oct 2020 12:54:15 +0100 Subject: [PATCH 14/27] Remove Basemap and replaced with Cartopy --- mapgenerator/plotting/config.py | 3 ++ mapgenerator/plotting/definitions.py | 1 + mapgenerator/plotting/plotmap.py | 55 ++++++++++++++-------------- setup.py | 2 +- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index 5c41df8..77e315b 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -159,6 +159,9 @@ class ArgumentParser(object): self.parser.add_argument("--ysize", dest="ysize", help="proportion of Y dimension (between 0 and 1)") + self.parser.add_argument("--orig_projection", + dest="orig_projection", + help="origin map projection (default=PlateCarree)") self.parser.add_argument("--projection", dest="projection", help="map projection to draw (default=PlateCarree)") diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 88a52ee..4270d87 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -702,6 +702,7 @@ class MapGenerator(object): self.srcgaps = kwargs.get('srcgaps', [0]) self.title = kwargs.get('title', '') self.fontsize = kwargs.get('fontsize', 12) + self.orig_projection= kwargs.get('orig_projection', 'PlateCarree') self.projection = kwargs.get('projection', 'PlateCarree') self.subsetting = kwargs.get('subsetting', True) self.dimension = kwargs.get('dimension', None) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index cba5d6b..946d4ee 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -9,7 +9,6 @@ import matplotlib as mpl mpl.use('Agg') import matplotlib.pyplot as plt -# from mpl_toolkits.basemap import Basemap import cartopy.crs as ccrs import cartopy.feature as cfeature import cartopy.io.shapereader as shpreader @@ -308,7 +307,7 @@ class PlotMap(MapCross, MapDrawOptions): def init_map(self, grid): - """ Initialize a Basemap map. Initialization should be performed only once + """ Initialize a map. Initialization should be performed only once the beginning of a serie of images """ glon, glat = grid #print("***",self.lon, self.lat,"***") @@ -951,10 +950,10 @@ class PlotMap(MapCross, MapDrawOptions): # parse wind arguments if self.windopts and (len(self.windopts) == 4): self.wind_scale = int(self.windopts[3]) - log.info("SHP {}".format(str(self.shapefiles))) + log.info("SHP %s", str(self.shapefiles)) if self.shapefiles: self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] - log.info("SHP {}".format(str(self.shapefiles))) + log.info("SHP %s", str(self.shapefiles)) if scatter_data is not None: if isinstance(scatter_data, str): @@ -963,7 +962,7 @@ class PlotMap(MapCross, MapDrawOptions): scatter_data = DataFrameHandler(scatter_data).filter() else: scatter_data = None - log.info("scatter data: {}".format(str(scatter_data))) + log.info("scatter data: %s", str(scatter_data)) data = MapData( map_data=[map_data], #temporarily @@ -992,7 +991,7 @@ class PlotMap(MapCross, MapDrawOptions): self.projection())) self.mgplot = plt.gcf() - fn = self.gen_image_map( + self.gen_image_map( fig_name, grid, data, @@ -1053,12 +1052,12 @@ class PlotMap(MapCross, MapDrawOptions): log.error('Error') # parse wind arguments - if (self.windopts and (len(self.windopts) == 4)): - self.wind_scale = int(self.windopts[3]); - log.info("SHP {}".format(str(self.shapefiles))) + if self.windopts and len(self.windopts) == 4: + self.wind_scale = int(self.windopts[3]) + log.info("SHP %s", str(self.shapefiles)) if self.shapefiles: self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] - log.info("SHP {}".format(str(self.shapefiles))) + log.info("SHP %s", str(self.shapefiles)) if self.scatter is not None: if isinstance(self.scatter, str): @@ -1067,7 +1066,7 @@ class PlotMap(MapCross, MapDrawOptions): scatter_data = DataFrameHandler(self.scatter).filter() else: scatter_data = None - log.info("scatter data: {}".format(str(scatter_data))) + log.info("scatter data: %s", str(scatter_data)) #run = '' run_tmp = '' @@ -1174,7 +1173,7 @@ class PlotMap(MapCross, MapDrawOptions): if scatter_data != None and (curr_date in scatter_data): cur_scatter_data = scatter_data[curr_date] - fn = self.gen_image_map( + fname = self.gen_image_map( fig_name, grid, nc_handler.get_data_for_tstep(n_time), @@ -1182,7 +1181,7 @@ class PlotMap(MapCross, MapDrawOptions): cur_scatter_data=cur_scatter_data ) - fig_names.append(fn) + fig_names.append(fname) plt_names.append((self.mgplot, self.mgaxis)) # Create Max at required intervals @@ -1197,7 +1196,7 @@ class PlotMap(MapCross, MapDrawOptions): p_title ) else: - print("Missing MAXTITLE, thus not generating MAX images!") + log.info("Missing MAXTITLE, thus not generating MAX images!") if self.kml or self.kmz: plt.clf() @@ -1206,10 +1205,10 @@ class PlotMap(MapCross, MapDrawOptions): #separate colorbar mpl.rcParams['axes.linewidth'] = 0.1 fig = plt.figure(figsize=(.1, 12)) - #ax = fig.add_axes([0.05, 0.80, 0.9, 0.15], axisbg=(1, 1, 1, 0)) + cax = fig.add_axes([0.05, 0.80, 0.9, 0.15]) # , axisbg=(1, 1, 1, 0)) print("...", self.bounds, "...") - cb = mpl.colorbar.ColorbarBase( - ax, + cbar = mpl.colorbar.ColorbarBase( + cax, # =self.mgaxis, cmap=self.cmap, norm=self.norm, values=self.bounds, @@ -1217,16 +1216,16 @@ class PlotMap(MapCross, MapDrawOptions): extend=self.extend, drawedges=False) if self.bounds: - cb.set_ticklabels(self.bounds) - else: - try: - cb.set_ticklabels(mco.levels) - except: - pass - cb.ax.tick_params(labelsize=6) - plt.setp(plt.getp(ax, 'yticklabels'), color='w') - plt.setp(plt.getp(ax, 'yticklabels'), fontsize=6) - for lin in cb.ax.yaxis.get_ticklines(): + cbar.set_ticklabels(self.bounds) +# else: +# try: +# cbar.set_ticklabels(mco.levels) +# except: +# pass + cbar.ax.tick_params(labelsize=6) + plt.setp(plt.getp(cax, 'yticklabels'), color='w') + plt.setp(plt.getp(cax, 'yticklabels'), fontsize=6) + for lin in cbar.ax.yaxis.get_ticklines(): lin.set_visible(False) tit = str(p_title) @@ -1242,7 +1241,7 @@ class PlotMap(MapCross, MapDrawOptions): color='w', fontsize=6, ) - ax.xaxis.set_label_coords(-0.025, -0.025) + cax.xaxis.set_label_coords(-0.025, -0.025) try: os.mkdir("%s-%s" % (run_date, self.srcvars[0])) diff --git a/setup.py b/setup.py index 81b93a9..966d4f7 100644 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ setup( install_requires=[ "matplotlib", "pandas", - "Basemap", + "Cartopy", "numpy", "netCDF4", "ConfigArgParse", -- GitLab From 1148616bcfca39ff19f1585bd3b17999b06e6b21 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 28 Oct 2020 17:16:26 +0100 Subject: [PATCH 15/27] Fixed projections management, countries, coastlines and continents color --- mapgenerator/plotting/plotmap.py | 229 +++++++++++++++---------------- 1 file changed, 109 insertions(+), 120 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 3efcefa..ea8fa4b 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -78,9 +78,10 @@ class PlotMap(MapCross, MapDrawOptions): self.kmz = False self.norm = None self.cmap = None - #self.formats = None + self._crs = ccrs.PlateCarree() + self.formats = None # self.map = None #Map - #self.first_image = True # Is is the first image of the batch? + self.first_image = False # Is is the first image of the batch? self.map_name = None # name of the figure to map animation colors # self.drawopts = {'coastlines': # {'linewidth': 0.5, 'color': 'grey', }, @@ -98,7 +99,7 @@ class PlotMap(MapCross, MapDrawOptions): self._crs = getattr(ccrs, self.projection)(**self.projection_kwargs) def set_resolution(self): - """ 'c','l','i','h','f' """ + """ 110m, 50m, 10m """ # lats = 36, 72, 108, 144, 180 # lons = 72, 144, 216, 288, 360 minlat, maxlat = self.lat[0], self.lat[-1] @@ -189,7 +190,7 @@ class PlotMap(MapCross, MapDrawOptions): except: pass - log.info("***** {} *****".format(self.formats)) + log.info("***** %s *****", self.formats) cax, kwargs = mpl.colorbar.make_axes(self.mgaxis, location=location, pad=0.02, shrink=0.8) cbar = self.mgplot.colorbar(mco, cax=cax, ax=self.mgaxis, @@ -216,17 +217,18 @@ class PlotMap(MapCross, MapDrawOptions): else: ticks = mco.levels - f = span/float(len(ticks) - 1) + fac = span/float(len(ticks) - 1) for i in arrows: for j in range(0, len(ticks) - 1): - if (i >= ticks[j] and i <= ticks[j+1]): - if (i == ticks[j+1]): - p = j+1 + if i >= ticks[j] and i <= ticks[j+1]: + if i == ticks[j+1]: + idx = j+1 add = 0 else: - p = j - add = (float(i - ticks[j])/float(ticks[j+1] - ticks[j]))*f - pos = f*p + lower + idx = j + add = (float(i - ticks[j])/float(ticks[j+1] - + ticks[j]))*fac + pos = fac*idx + lower plt.arrow(0.88, pos + add, 0.10, 0, length_includes_head=True, head_length=0.10, head_width=0.025, fill=True, color='k') @@ -256,7 +258,7 @@ class PlotMap(MapCross, MapDrawOptions): try: p_title = str(title) % title_dic #("%sh" % s_time, valid) except Exception as e: - print("Title error: %s", str(e)) + log.error("Title error: %s", str(e)) p_title = title % title_dic #("%sh" % s_time, valid) return p_title @@ -266,22 +268,22 @@ class PlotMap(MapCross, MapDrawOptions): # initial time if 'start_t' not in globals(): global start_t, last_t - print("Creating start_t") + log.info("Creating start_t") start_t = datetime.now() last_t = start_t now_t = datetime.now() diff_t = now_t - last_t if msg != None: - print('TIME:', msg, ' done in ', diff_t.seconds, ' s') + log.info('TIME: %s done in %s s', msg, diff_t.seconds) last_t = now_t - def run_command(self, comm_str, fatal=True): + def run_command(self, comm_str): # , fatal=True): """ Run command """ - print(comm_str) - p = subprocess.Popen(comm_str.split(), stderr=subprocess.PIPE) - output, err = p.communicate() - print(output, '-----', err) + log.info(comm_str) + proc = subprocess.Popen(comm_str.split(), stderr=subprocess.PIPE) + output, err = proc.communicate() + log.info(output, '-----', err) # st, out = commands.getstatusoutput(comm_str) # if st != 0: # print "Error: %s" % str(out) @@ -347,7 +349,7 @@ class PlotMap(MapCross, MapDrawOptions): # overwrite option if os.path.exists(fig_name + ".png") and not self.overwrite: - print(fig_name, " already exists.") + log.info(fig_name, " already exists.") if self.subplot is None: plt.clf() return fig_name @@ -369,9 +371,9 @@ class PlotMap(MapCross, MapDrawOptions): # Draw filled contour self.mgaxis.set_aspect(self.xsize/self.ysize) - # self.mgaxis.set_extent([self.lon[0], self.lon[-1], - # self.lat[0], self.lat[-1]], - # crs=ccrs.PlateCarree()) + self.mgaxis.set_extent([self.lon[0], self.lon[-1], + self.lat[0], self.lat[-1]], + crs=self._crs) # if self.subplot: # self.map = Basemap( # ax=self.mgaxis, @@ -400,27 +402,27 @@ class PlotMap(MapCross, MapDrawOptions): glon, glat = np.meshgrid(glon, glat) #print(glon, glat) - log.info("1. GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) + log.info("1. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) # if curvilinear grid do interpolation, return already gridded coords if not is_grid_regular(glon, glat): map_data[0], glon, glat = do_interpolation(map_data[0], glon, glat) - log.info("2. GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) + log.info("2. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) # if not self.nomap: # x, y = self.map(*(glon, glat)) # else: - x, y = glon, glat + xloc, yloc = glon, glat - log.info("3. GLON: %s, GLAT: %s" % (str(x.shape), str(y.shape))) + log.info("3. GLON: %s, GLAT: %s", str(xloc.shape), str(yloc.shape)) # print "map_data", map_data[0].shape #self.print_time("meshgrid") #log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) # # nomap option -# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, varName, +# if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, var_name, # fig_name)) and not self.overwrite: # print fig_name, " already exists." # plt.clf() @@ -467,7 +469,7 @@ class PlotMap(MapCross, MapDrawOptions): # antialiased=True) if not self.nocontourf and self.smooth: - mco = self.mgaxis.contourf(x, y, + mco = self.mgaxis.contourf(xloc, yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -479,7 +481,7 @@ class PlotMap(MapCross, MapDrawOptions): elif not self.nocontourf and not self.smooth: - mco = self.mgaxis.pcolormesh(x, y, + mco = self.mgaxis.pcolormesh(xloc, yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -491,7 +493,7 @@ class PlotMap(MapCross, MapDrawOptions): if self.has_shape_files(): line_w = float(self.countropts[0]) for shapef in self.shapefiles: - log.info("Processing shape file: {} with line width: {}".format(shapef, line_w)) + log.info("Processing shape file: %s with line width: %s", shapef, line_w) adm1_shapes = list(shpreader.Reader(shapef).geometries()) self.mgaxis.add_geometries(adm1_shapes, self._crs, linewidth=line_w, @@ -505,7 +507,8 @@ class PlotMap(MapCross, MapDrawOptions): ## FIXME Modify to use scatter inside DATA ## if DATA.hasScatterData()... etc... if scatter_data is not None: - log.info("Plotting scatter data: {} of keys {}".format(str(scatter_data), str(scatter_data.keys()))) + log.info("Plotting scatter data: %s of keys %s", + str(scatter_data), str(scatter_data.keys())) if mco and self.bounds: self.mgaxis.scatter(scatter_data['lon'].tolist(), scatter_data['lat'].tolist(), @@ -553,10 +556,13 @@ class PlotMap(MapCross, MapDrawOptions): if data.has_wind_data(): winds = data.get_wind_data() - X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) + x_vec, y_vec, u_vec, v_vec = delete_masked_points(xloc.ravel(), + yloc.ravel(), + winds['u'].ravel(), + winds['v'].ravel()) #print("Wind scale is", self.wind_units, self.wind_scale) if winds.has_key('barbs'): - self.mgaxis.barbs(X, Y, U, V, #units=self.wind_units, + self.mgaxis.barbs(x_vec, y_vec, u_vec, v_vec, #units=self.wind_units, #headlength=self.wind_head_length, #headwidth=self.wind_head_width, #width=self.wind_width, @@ -565,15 +571,15 @@ class PlotMap(MapCross, MapDrawOptions): color='k') #self.print_time("barbs") else: - Q = self.mgaxis.quiver(X, Y, U, V, units=self.wind_units, - headlength=self.wind_head_length, - headwidth=self.wind_head_width, - width=self.wind_width, - minshaft=self.wind_minshaft, - scale=self.wind_scale, - color='gray') + quiv = self.mgaxis.quiver(x_vec, y_vec, u_vec, v_vec, units=self.wind_units, + headlength=self.wind_head_length, + headwidth=self.wind_head_width, + width=self.wind_width, + minshaft=self.wind_minshaft, + scale=self.wind_scale, + color='gray') # Draw the key - plt.quiverkey(Q, + plt.quiverkey(quiv, self.wind_label_xpos, self.wind_label_ypos, self.wind_label_scale, @@ -605,7 +611,7 @@ class PlotMap(MapCross, MapDrawOptions): for exc in exclude: lvls = [exc for exc in lvls if exc != 0] if (len(lvls) > 0) and map_data != None: - mco = plt.contourf(x, y, + mco = plt.contourf(xloc, yloc, map_data[0], cmap=self.cmap, norm=self.norm, @@ -617,7 +623,7 @@ class PlotMap(MapCross, MapDrawOptions): #print "-----------------", map_data, cdata, "--------------------" if map_data != None and (map_data[0] == cdata).all(): log.debug(":::::::::::::::::::::::: SAME !!! :::::::::::::::::::") - mcs = plt.contour(x, y, + mcs = plt.contour(xloc, yloc, cdata, levels=self.bounds, colors=self.contours_color, @@ -625,8 +631,8 @@ class PlotMap(MapCross, MapDrawOptions): alpha=self.alpha) else: log.debug(":::::::::::::::::::::::: DIFFERENT !!! :::::::::::::::::::") - log.debug("MIN: %s - MAX: %s" % (cmin, cmax)) - mcs = plt.contour(x, y, + log.debug("MIN: %s - MAX: %s", cmin, cmax) + mcs = plt.contour(xloc, yloc, cdata, levels=lvls, colors=self.contours_color, @@ -647,26 +653,28 @@ class PlotMap(MapCross, MapDrawOptions): # lon_offset = abs(self.lon[0]) % self.lon[2] if not self.nomap and not self.kml and not self.kmz: -# self.map.drawparallels(np.arange(self.lat[0]+lat_offset, self.lat[1], self.lat[2]),labels=[1,0,0,0],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) -# self.map.drawmeridians(np.arange(self.lon[0]+lon_offset, self.lon[1], self.lon[2]),labels=[0,0,0,1],linewidth=self.coords_linewidth, fontsize=self.coords_fontsize) - #self.print_time("coords") + + if self.continents: + self.mgaxis.add_feature(cfeature.LAND, color=self.continents, zorder=10) + _ = self.mgaxis.coastlines(resolution=self.resolution, linewidth=float(self.coastsopts[0]), - color=str(self.coastsopts[1])) + color=str(self.coastsopts[1]), + zorder=15 + ) - _ = self.mgaxis.add_feature(cfeature.BORDERS, #.with_scale(self.resolution), + _ = self.mgaxis.add_feature(cfeature.BORDERS.with_scale(self.resolution), linewidth=float(self.countropts[0]), - color=str(self.countropts[1])) - -# _ = self.mgaxis.add_feature(cfeature.COASTLINE, # coastlines(resolution=self.resolution, -# linewidth=float(self.coastsopts[0]), -# color=str(self.coastsopts[1])) + edgecolor=str(self.countropts[1]), + zorder=15 + ) if self.projection == 'PlateCarree': grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, draw_labels=True, linestyle='--', linewidth=float(self.coordsopts[0]), color=str(self.coordsopts[2]), + zorder=20 ) grl.xlabels_top = False @@ -678,34 +686,15 @@ class PlotMap(MapCross, MapDrawOptions): draw_labels=False, linestyle='--', linewidth=float(self.coordsopts[0]), color=str(self.coordsopts[2]), + zorder=20 ) - -# self.map.drawcountries(linewidth=float(self.countropts[0]), color=str(self.countropts[1])) -# self.map.drawcoastlines(linewidth=float(self.coastsopts[0]), color=str(self.coastsopts[1])) -# if self.continents: -# self.map.fillcontinents(color=self.continents, zorder=0) - -# drawopts = self.drawopts -# if drawopts: -# # Drawing countries -# if 'countries' in drawopts: -# self.map.drawcountries(**drawopts['countries']) -# #self.print_time("countries") -# # Drawing coastlines (land/sea, lakes, etc...) -# if 'coastlines' in drawopts: -# self.map.drawcoastlines(**drawopts['coastlines']) -# #self.print_time("coastlines") -## # Drawing states (FIXME does it work outside the U.S.?) -## if 'states' in drawopts: -## self.map.drawstates(**drawopts['states']) -## #self.print_time("states") - # Change axes for colorbar if self.colorbar and not self.kml and not self.kmz and \ (not self.nocontourf or scatter_data is not None): self.set_color_bar(mco) +# if self.background == 'shadedrelief': # #m.bluemarble() # if self.background == 'shadedrelief': # self.map.shadedrelief() @@ -746,7 +735,7 @@ class PlotMap(MapCross, MapDrawOptions): # SAVEFIG fullname = "{}.{}".format(fig_name, self.filefmt) - log.info("printing {}".format(fullname)) + log.info("printing %s", fullname) if self.kml or self.kmz: #self.mgplot.axes(frameon=0) if self.save: @@ -778,11 +767,11 @@ class PlotMap(MapCross, MapDrawOptions): # for i in range(0, len(map_names)-1): # items, newMapNames, newMapNameTpl = self.twoMapsCompare([newMapNames, map_names[i+1]], items) - mn = np.array(map_names) - ks = mn.T + mnames = np.array(map_names) + k_s = mnames.T idx = 0 - for item in ks: + for item in k_s: # if idx >= len(newMapNames): # continue imgs = ' '.join([str(i) + '.gif' for i in item]) @@ -793,7 +782,7 @@ class PlotMap(MapCross, MapDrawOptions): #comm = "montage %s -tile 2x -geometry 800x600+1+1 %s.gif" % (imgs, newMapNames[idx]) comm = "montage %s -tile 2x -geometry +1+1 %s.gif" % (imgs, newimg) #print "COMPARE COMMAND:", comm - st, out = subprocess.getstatusoutput(comm) + _, _ = subprocess.getstatusoutput(comm) idx += 1 #montage -tile 2x -geometry 800x600+1+1 @@ -818,12 +807,12 @@ class PlotMap(MapCross, MapDrawOptions): if tpl: nimg0 = tpl % {'date':date, 'step':nimg0} nimg1 = tpl % {'date':date, 'step':nimg1} - st, out = subprocess.getstatusoutput("convert -delay 75 -loop 0 %s.gif %s.gif" % (nimg0, nimg1)) - if st != 0: - print("Error: %s" % str(out)) + status, out = subprocess.getstatusoutput("convert -delay 75 -loop 0 %s.gif %s.gif" % (nimg0, nimg1)) + if status != 0: + log.error("Error: %s", str(out)) - def genKML(self, fig_names, srcvars, dims, online=True): + def gen_kml(self, fig_names, srcvars, dims, online=True): """ """ from lxml import etree # from pykml.parser import Schema @@ -834,20 +823,20 @@ class PlotMap(MapCross, MapDrawOptions): lon = self.lon lat = self.lat - print(fig_names) - #kmlName = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) - kmlName = os.path.basename(fig_names[0])[:-3] - varName = srcvars[0] #fig_names[0].split('-')[-2] + log.info(fig_names) + #kml_name = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) + kml_name = os.path.basename(fig_names[0])[:-3] + var_name = srcvars[0] #fig_names[0].split('-')[-2] run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") begindate = run_date0.strftime("%Y%m%d%H") - dirName = os.path.join(self.outdir, begindate + '-' + varName).replace('./','') + dir_name = os.path.join(self.outdir, begindate + '-' + var_name).replace('./', '') interval = run_date1 - run_date0 datefmt = "%Y-%m-%dT%H:00:00Z" doc = KML.kml( KML.Folder( - KML.name(kmlName), + KML.name(kml_name), KML.LookAt( KML.longitude((lon[1]+lon[-1])/2), KML.latitude((lat[1]+lat[-1])/2), @@ -859,30 +848,30 @@ class PlotMap(MapCross, MapDrawOptions): KML.name("Legend"), KML.open(1), KML.Icon( - KML.href("%s/%s-colorbar.png" % (dirName, begindate)), + KML.href("%s/%s-colorbar.png" % (dir_name, begindate)), ), - KML.overlayXY(x="0",y="1",xunits="fraction",yunits="fraction"), - KML.screenXY(x=".01",y="0.55",xunits="fraction",yunits="fraction"), - KML.rotationXY(x="0",y="0",xunits="fraction",yunits="fraction"), - KML.size(x="0",y="0.5",xunits="fraction",yunits="fraction"), + KML.overlayXY(x="0", y="1", xunits="fraction", yunits="fraction"), + KML.screenXY(x=".01", y="0.55", xunits="fraction", yunits="fraction"), + KML.rotationXY(x="0", y="0", xunits="fraction", yunits="fraction"), + KML.size(x="0", y="0.5", xunits="fraction", yunits="fraction"), id="%s-ScreenOverlay" %(begindate), ), ) ) try: - os.mkdir(dirName) + os.mkdir(dir_name) except: pass if not online: - zf = zipfile.ZipFile("%s.kmz" % kmlName, 'w') + zfile = zipfile.ZipFile("%s.kmz" % kml_name, 'w') for fig_name, dat in zip(fig_names, sorted(dims.keys())): - dt = dims[dat] + dtime = dims[dat] fig_name = fig_name + ".png" - figPath = os.path.join(dirName,os.path.basename(fig_name)) - startdate = datetime.strptime(dt, "%HZ%d%b%Y") + fig_path = os.path.join(dir_name, os.path.basename(fig_name)) + startdate = datetime.strptime(dtime, "%HZ%d%b%Y") begdate = startdate.strftime(datefmt) enddate = (startdate + interval).strftime(datefmt) starth = startdate.hour @@ -893,7 +882,7 @@ class PlotMap(MapCross, MapDrawOptions): KML.end(enddate), ), KML.Icon( - KML.href(figPath), + KML.href(fig_path), KML.viewBoundScale(1.0), ), KML.altitude(0.0), @@ -908,26 +897,26 @@ class PlotMap(MapCross, MapDrawOptions): )) if os.path.exists(fig_name): - os.rename(fig_name, figPath) - zf.write(figPath) + os.rename(fig_name, fig_path) + zfile.write(fig_path) - outf = file("%s.kml" % kmlName, 'w') + outf = file("%s.kml" % kml_name, 'w') outf.write('\n') outf.write(etree.tostring(doc, pretty_print=True)) outf.close() if not online: - zf.write("%s.kml" % kmlName) - zf.write("%s/%s-colorbar.png" % (dirName, begindate)) - zf.close() + zfile.write("%s.kml" % kml_name) + zfile.write("%s/%s-colorbar.png" % (dir_name, begindate)) + zfile.close() - for img in os.listdir(dirName): - os.remove("%s/%s" % (dirName, img)) - os.rmdir(dirName) - os.remove("%s.kml" % kmlName) + for img in os.listdir(dir_name): + os.remove("%s/%s" % (dir_name, img)) + os.rmdir(dir_name) + os.remove("%s.kml" % kml_name) - def aplot(self, x, y, map_data=None, wind_data=None, contour_data=None, scatter_data=None, **kwargs): + def aplot(self, xloc, yloc, map_data=None, wind_data=None, contour_data=None, scatter_data=None, **kwargs): """ Plot one or more maps directly from numerical arrays """ # store options to be restored at the end @@ -962,7 +951,7 @@ class PlotMap(MapCross, MapDrawOptions): contour_data=contour_data, scatter_data=scatter_data ) - grid = (x, y) + grid = (xloc, yloc) self.init_map(grid) if self.img_template: @@ -1246,13 +1235,13 @@ class PlotMap(MapCross, MapDrawOptions): #generate KMZ - Offline if self.kmz: - print("Generating KMZ ...") - self.genKML(fig_names, self.srcvars, dims, online=False) + log.info("Generating KMZ ...") + self.gen_kml(fig_names, self.srcvars, dims, online=False) #generate KML - Online if self.kml: - print("Generating KML ...") - self.genKML(fig_names, self.srcvars, dims) + log.info("Generating KML ...") + self.gen_kml(fig_names, self.srcvars, dims) #generate animation. if self.anim: @@ -1299,7 +1288,7 @@ class PlotMap(MapCross, MapDrawOptions): fpath = parse_path(self.indir, fpath) if not os.path.isfile(fpath): - print("Error %s" % fpath) + log.error("Error %s", fpath) return opts = read_conf(section, fpath) -- GitLab From b73d146cca3a74b0472caaa5ff6ced7bab402b63 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Thu, 29 Oct 2020 10:43:58 +0100 Subject: [PATCH 16/27] Fix maps not appearing --- mapgenerator/plotting/plotmap.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index d3f8e7f..fc26688 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -354,8 +354,9 @@ class PlotMap(MapCross, MapDrawOptions): map_data = data.map_data # FIXME scatter_data = data.scatter_data or cur_scatter_data - if self.subplot is None: - plt.clf() + #if self.subplot is None: + # self.mgplot.clear() + # params = { # 'font.size': 14, # 'text.fontsize': 28, @@ -541,15 +542,15 @@ class PlotMap(MapCross, MapDrawOptions): norm=self.norm, zorder=10) else: - mco = self.map.scatter(scatter_data['lon'].tolist(), - scatter_data['lat'].tolist(), - s=scatter_data['size'].tolist(), - c=scatter_data['color'].tolist(), - marker='o', - linewidth=0.3, - cmap=self.cmap, - norm=self.norm, - zorder=10) + mco = self.mgaxis.scatter(scatter_data['lon'].tolist(), + scatter_data['lat'].tolist(), + s=scatter_data['size'].tolist(), + c=scatter_data['color'].tolist(), + marker='o', + linewidth=0.3, + cmap=self.cmap, + norm=self.norm, + zorder=10) if data.wind_data: winds = data.wind_data -- GitLab From e85d773e1f690bc36a5bcd6c20b66887736f8def Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 29 Oct 2020 13:06:13 +0100 Subject: [PATCH 17/27] Cleaning code, removed debug option, substituted by loglevel, and kml/kmz options --- mapgenerator/plotting/config.py | 49 +-- mapgenerator/plotting/definitions.py | 12 +- mapgenerator/plotting/plotmap.py | 439 ++++++++++++++------------- 3 files changed, 265 insertions(+), 235 deletions(-) diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index 77e315b..f7556cd 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -17,7 +17,7 @@ logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) -#def list_callback(option, opt_str, value, parser): +# def list_callback(option, opt_str, value, parser): # setattr(parser.values, option.dest, value.split(',')) # print("**************", value, type(value)) # print('\toption:', repr(option)) @@ -36,11 +36,11 @@ class ArgumentParser(object): self.parser = argparse.ArgumentParser(description='') self.parser.add_argument('-V', '--version', action='version', version=__version__, - help="returns MapGenerator version number and exit") - self.parser.add_argument('--config', #is_config_file=True, + help="returns version and exit") + self.parser.add_argument('--config', dest="config", help='specifies the config file to read' - ) #required=False) + ) self.parser.add_argument("--section", dest="section", @@ -59,23 +59,27 @@ class ArgumentParser(object): # self.parser.add_argument("--loglevel", dest="loglevel", - choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', + choices=['DEBUG', + 'INFO', + 'WARNING', + 'ERROR', 'CRITICAL'], help="logging levelbug (default=INFO)") self.parser.add_argument("--indir", dest="indir", - help="directory where is/are stored the input file(s)") + help="directory for input file(s)") self.parser.add_argument("--outdir", dest="outdir", - help="directory where is/are to be stored the output file(s)") + help="directory for output file(s)") self.parser.add_argument("--file_format", dest="filefmt", - help="format of the output file(s) (default=png)") + help="outputs format (default=png)") self.parser.add_argument("--timesteps", dest="timesteps", nargs="+", - help="timesteps to plot (list or 'all' - default=[0])") + help="""timesteps (list or 'all' - + default=[0])""") self.parser.add_argument("--bounds", dest="bounds", nargs="+", @@ -85,7 +89,9 @@ class ArgumentParser(object): help="colorbar ticks list (default=None)") self.parser.add_argument("--colors", dest="colors", - help="colors list or colormap name (default='jet'), for a list check here: https://matplotlib.org/users/colormaps.html") + help="""colors list or colormap name + (default='jet'), for a list check here: + https://matplotlib.org/users/colormaps.html""") self.parser.add_argument("--lon", dest="lon", nargs="+", @@ -186,7 +192,8 @@ class ArgumentParser(object): help="dimension name and operation in the form DIMENSION,OPERATION (mean, max, min, ...) as documented in the numpy reference guide. If OPERATION is a number it will extract the correspondent element") self.parser.add_argument("--alpha", dest="alpha", - help="alpha value for colormap netween 0 and 1 (default=1)") + help="""alpha value for colormap netween 0 + and 1 (default=1)""") ############ self.parser.add_argument("--img_template", dest="img_template", @@ -199,10 +206,11 @@ class ArgumentParser(object): help="hide map") # self.parser.add_argument("--kml", # dest="kml", -# help="generate KML. String with base url (default='')") - self.parser.add_argument("--kmz", - dest="kmz", - help="generate KMZ (default=False)") +# help="generate KML. String with base url +# (default='')") +# self.parser.add_argument("--kmz", +# dest="kmz", +# help="generate KMZ (default=False)") self.parser.add_argument("--overwrite", dest="overwrite", help="overwrite outputs (default=True)") @@ -212,7 +220,8 @@ class ArgumentParser(object): help="background orography (default=None)") self.parser.add_argument("--logo", dest="logo", - help="put a logo (image path, x, y - default=None)") + help="""put a logo (image path, x, y - + default=None)""") # self.parser.add_argument("--noruntime", # dest="noruntime", # help="no runtime") @@ -261,18 +270,18 @@ class ArgumentParser(object): # help="max") except Exception as e: - log.error('Unhandled exception on MapGenerator: %s' % e, exc_info=True) + log.error('Unhandled exception on MapGenerator: %s' % e, + exc_info=True) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Parse arguments and preprocess - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- def parse_args(self, args=None): """ Parse arguments given to an executable :param args: """ try: - #return self.parser.parse_args(args) return self.parser.parse_args(args) except Exception as e: print(e) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 88c1072..4017fd6 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -19,7 +19,7 @@ import copy import logging -#logging.basicConfig(level=logging.WARNING) +#logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) @@ -189,7 +189,7 @@ def parse_path(directory, dfile): class MapData(object): """Wrapper class to handle all the data needed to draw a map""" - def __init__(self, loglevel='WARNING', **kwargs): + def __init__(self, loglevel='INFO', **kwargs): log.setLevel(loglevel) log.info("map_data initializer") self.reset() @@ -629,7 +629,7 @@ class DataTransform(object): class MapDrawOptions(object): """ Map draw options """ - def __init__(self, loglevel='WARNING', **kwargs): + def __init__(self, loglevel='INFO', **kwargs): log.setLevel(loglevel) ## General Options self.area_thresh = None # Default area threshold for maps @@ -701,7 +701,8 @@ class MapGenerator(object): self.srcgaps = kwargs.get('srcgaps', [0]) self.title = kwargs.get('title', '') self.fontsize = kwargs.get('fontsize', 12) - self.orig_projection= kwargs.get('orig_projection', 'PlateCarree') + self.orig_projection = kwargs.get('orig_projection', 'PlateCarree') + self.orig_projection_kwargs = kwargs.get('orig_projection_kwargs', dict()) self.projection = kwargs.get('projection', 'PlateCarree') self.projection_kwargs = kwargs.get('projection_kwargs', dict()) self.subsetting = kwargs.get('subsetting', True) @@ -720,7 +721,6 @@ class MapGenerator(object): self.config_dir = kwargs.get('config_dir', \ os.path.join(os.environ['HOME'], '.mapgenerator')) self.config_file = kwargs.get('config_file', 'config.cfg') - self.debug = kwargs.get('debug', False) self.alpha = kwargs.get('alpha', None) self.shapefiles = kwargs.get('shapefiles', None) self.dpi = kwargs.get('dpi', 200) @@ -741,7 +741,7 @@ class MapGenerator(object): class MapCross(MapGenerator): """ Define common attributes to maps and cross sections """ - def __init__(self, loglevel='WARNING', **kwargs): + def __init__(self, loglevel='INFO', **kwargs): """ Initialize class with MapGenerator attributes plus some others """ log.setLevel(loglevel) MapGenerator.__init__(self, **kwargs) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index fc26688..7f7eeaf 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -4,10 +4,7 @@ # @license: https://www.gnu.org/licenses/gpl-3.0.html # @author: see AUTHORS file - import matplotlib as mpl -mpl.use('Agg') - import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature @@ -16,11 +13,8 @@ from matplotlib.cbook import delete_masked_points # from matplotlib.image import imread import numpy as np from datetime import datetime -#from datetime import timedelta -#import time import os.path import subprocess -#import sys from collections import Iterable from PIL import Image from .definitions import MapData @@ -40,17 +34,16 @@ except ImportError: from urllib.request import urlopen import copy from glob import glob - import logging -#logging.basicConfig(level=logging.WARNING) +mpl.use('Agg') log = logging.getLogger(__name__) class PlotCross(): """ Main class for plotting cross sections """ - def __init__(self, loglevel='WARNING', **kwargs): + def __init__(self, loglevel='INFO', **kwargs): pass @@ -74,8 +67,8 @@ class PlotMap(MapCross, MapDrawOptions): self._crs = ccrs.PlateCarree() self.formats = None # self.map = None #Map - self.first_image = False # Is is the first image of the batch? - self.map_name = None # name of the figure to map animation colors + self.first_image = False # Is is the first image of the batch? + self.map_name = None # name of the figure to map animation colors # self.drawopts = {'coastlines': # {'linewidth': 0.5, 'color': 'grey', }, # 'countries': @@ -84,9 +77,12 @@ class PlotMap(MapCross, MapDrawOptions): log.setLevel(loglevel) def __setattr__(self, key, value): - #log.info("SETATTR: {} - {}".format(key, value)) + log.debug("SETATTR: {} - {}".format(key, value)) super(PlotMap, self).__setattr__(key, parse_parameter(key, value)) + def _build_orig_projection(self): + self._orig_crs = getattr(ccrs, self.orig_projection)( + **self.orig_projection_kwargs) def _build_projection(self): self._crs = getattr(ccrs, self.projection)(**self.projection_kwargs) @@ -106,11 +102,12 @@ class PlotMap(MapCross, MapDrawOptions): elif lats <= 45 and lons <= 75: self.resolution = '10m' else: - self.resolution = '110m' # default + self.resolution = '110m' # default def set_color_map(self): """ Create color map """ - if not isinstance(self.colors, str) and isinstance(self.colors, Iterable): + if not isinstance(self.colors, str) and \ + isinstance(self.colors, Iterable): # adjust number of colors to number of bounds if self.bounds: if self.extend in ('min', 'max'): @@ -142,32 +139,32 @@ class PlotMap(MapCross, MapDrawOptions): self.cmap = mpl.cm.get_cmap('jet') custom_cmap = False - if self.bad: self.cmap.set_bad(self.bad) if self.background or self.kml or self.kmz: try: - self.cmap.colors = [(0, 0, 0, 0),]+[c for c in self.cmap.colors[1:]] + self.cmap.colors = [(0, 0, 0, 0), ] + [c for c in + self.cmap.colors[1:]] except: # if self.extend in ('min','both'): self.cmap.set_under(color=(0, 0, 0, 0)) -# else: -# self.cmap.colors = [(0,0,0,0),]+[c for c in self.cmap.colors[1:]] # normalize colormap if self.bounds and self.smooth and not custom_cmap: if self.extend == 'min': - self.norm = mpl.colors.BoundaryNorm([-np.inf]+self.bounds, self.cmap.N+1) + self.norm = mpl.colors.BoundaryNorm([-np.inf]+self.bounds, + self.cmap.N+1) elif self.extend == 'max': - self.norm = mpl.colors.BoundaryNorm(self.bounds+[np.inf], self.cmap.N+1) + self.norm = mpl.colors.BoundaryNorm(self.bounds+[np.inf], + self.cmap.N+1) elif self.extend == 'both': - self.norm = mpl.colors.BoundaryNorm([-np.inf]+self.bounds+[np.inf], self.cmap.N+2) + self.norm = mpl.colors.BoundaryNorm([-np.inf] + self.bounds + + [np.inf], self.cmap.N + 2) else: self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) elif self.bounds: self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) - def set_color_bar(self, mco, location='right', drawedges=False, cax=None): """ Create color bar """ # xs = self.xsize @@ -177,7 +174,7 @@ class PlotMap(MapCross, MapDrawOptions): mpl.rcParams['axes.linewidth'] = 0.1 mpl.rcParams['axes.formatter.useoffset'] = False if self.ticks: - #log.info("ticks %s" % str(self.ticks)) + log.debug("ticks %s" % str(self.ticks)) self.ticks = parse_parameters_list(self.ticks) elif self.bounds: self.ticks = self.bounds @@ -187,7 +184,7 @@ class PlotMap(MapCross, MapDrawOptions): except: pass - log.info("***** %s *****", self.formats) + log.debug("***** %s *****", self.formats) cax, kwargs = mpl.colorbar.make_axes(self.mgaxis, location=location, pad=0.02, shrink=0.8) cbar = self.mgplot.colorbar(mco, cax=cax, ax=self.mgaxis, @@ -198,13 +195,12 @@ class PlotMap(MapCross, MapDrawOptions): cbar.ax.tick_params(labelsize=float(self.coordsopts[1])) for lin in cbar.ax.yaxis.get_ticklines(): lin.set_visible(False) - #self.print_time("colorbar") - ## If required, draw arrow pointing to the specified limits + # If required, draw arrow pointing to the specified limits if self.has_limits(): # The following two values are absolute shrink = 0.75 - upper = 0.863 - 0.45 * (0.8 - shrink) # formula seems to work ok + upper = 0.863 - 0.45 * (0.8 - shrink) # formula seems to work ok lower = 0.137 + 0.45 * (0.8 - shrink) span = upper - lower arrows = self.limits @@ -229,7 +225,7 @@ class PlotMap(MapCross, MapDrawOptions): plt.arrow(0.88, pos + add, 0.10, 0, length_includes_head=True, head_length=0.10, head_width=0.025, fill=True, color='k') - #plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) + # plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) def set_title(self, title, sdate, cdate, step, stime): """ Set the title of the current image to the one provided, substituting the @@ -249,14 +245,14 @@ class PlotMap(MapCross, MapDrawOptions): 'step': step, 'simday': "%d" % (int(stime)/24), 'simhh': stime, - #'simhh': "%02d" % (int(stime)%24) + # 'simhh': "%02d" % (int(stime)%24) } try: - p_title = str(title) % title_dic #("%sh" % s_time, valid) + p_title = str(title) % title_dic # ("%sh" % s_time, valid) except Exception as e: log.error("Title error: %s", str(e)) - p_title = title % title_dic #("%sh" % s_time, valid) + p_title = title % title_dic # ("%sh" % s_time, valid) return p_title @@ -271,7 +267,7 @@ class PlotMap(MapCross, MapDrawOptions): now_t = datetime.now() diff_t = now_t - last_t - if msg != None: + if msg is not None: log.info('TIME: %s done in %s s', msg, diff_t.seconds) last_t = now_t @@ -290,40 +286,43 @@ class PlotMap(MapCross, MapDrawOptions): def gen_anim(self, inpattern, outfile): ''' Generate an animation starting from a set of images, specified by the inpattern parameter ''' - #print "Animation: ", indir, inpattern, outdir, outfile + # print "Animation: ", indir, inpattern, outdir, outfile # Create animation self.run_command( - "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" % \ - (self.anim_delay, self.outdir, inpattern, self.outdir, outfile)) + "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" + % (self.anim_delay, self.outdir, inpattern, self.outdir, outfile)) # Remove intermediate files - #self.run_command("rm %s/%s %s" % (indir, inpattern, self.map_name), False) + # self.run_command("rm %s/%s %s" % (indir, inpattern, self.map_name), + # False) # ccc = [] # ccc.extend(self.colors) -# print "Filtering scatter data for date/hour: ", curr_date.strftime("%Y%m%d"), s_time24 -# csv_lat, csv_lon, csv_val = csv_handler.filter(curr_date.strftime("%Y%m%d"), s_time24) -# #csv_col = self.dataTransform.setColorsFromBuckets(ccc, self.bounds, csv_con) +# print "Filtering scatter data for date/hour: ", +# curr_date.strftime("%Y%m%d"), s_time24 csv_lat, csv_lon, csv_val = +# csv_handler.filter(curr_date.strftime("%Y%m%d"), s_time24) +# #csv_col = self.dataTransform.setColorsFromBuckets(ccc, +# self.bounds, csv_con) # if(len(csv_lat) > 0): # scatter_data = [csv_lon, csv_lat, 40, csv_col] # print "**************** SCATTER_DATA", scatter_data # print "Filtered %d records for scatterplot" % len(scatter_data) - def init_map(self, grid): """ Initialize a map. Initialization should be performed only once the beginning of a serie of images """ glon, glat = grid - #print("***",self.lon, self.lat,"***") - if not self.lat: # or len(self.lat) not in (2,3): + # print("***",self.lon, self.lat,"***") + if not self.lat: # or len(self.lat) not in (2,3): self.lat = "%s-%s" % (str(round(glat.min(), 1)).replace('-', 'm'), str(round(glat.max(), 1)).replace('-', 'm')) - if not self.lon: # or len(self.lon) not in (2,3): + if not self.lon: # or len(self.lon) not in (2,3): self.lon = "%s-%s" % (str(round(glon.min(), 1)).replace('-', 'm'), str(round(glon.max(), 1)).replace('-', 'm')) + self._build_orig_projection() self._build_projection() - #print(self.lon, self.lat) + # print(self.lon, self.lat) # # Fix the printout of tick values to avoid .0 decimals in integers # strs = [] # if self.bounds: @@ -340,8 +339,8 @@ class PlotMap(MapCross, MapDrawOptions): self.first_image = True - - def gen_image_map(self, fig_name, grid, data, img_title, cur_scatter_data=None, **kwargs): + def gen_image_map(self, fig_name, grid, data, img_title, + cur_scatter_data=None, **kwargs): """ Generate image map """ # overwrite option @@ -354,7 +353,7 @@ class PlotMap(MapCross, MapDrawOptions): map_data = data.map_data # FIXME scatter_data = data.scatter_data or cur_scatter_data - #if self.subplot is None: + # if self.subplot is None: # self.mgplot.clear() # params = { @@ -395,12 +394,12 @@ class PlotMap(MapCross, MapDrawOptions): # ) glon, glat = grid - #log.info("GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) + log.debug("0. GLON: %s, GLAT: %s" % (str(glon.shape), str(glat.shape))) + if len(glon.shape) == 1 and len(glat.shape) == 1: glon, glat = np.meshgrid(glon, glat) - #print(glon, glat) - log.info("1. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) + log.debug("1. GLON: %s, GLAT: %s", str(glon.shape), str(glat.shape)) # if curvilinear grid do interpolation, return already gridded coords if not is_grid_regular(glon, glat): @@ -416,8 +415,8 @@ class PlotMap(MapCross, MapDrawOptions): log.info("3. GLON: %s, GLAT: %s", str(xloc.shape), str(yloc.shape)) # print "map_data", map_data[0].shape - #self.print_time("meshgrid") - #log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) + # self.print_time("meshgrid") + # log.info("X: %s, Y: %s" % (str(x.shape), str(y.shape))) # # nomap option # if self.nomap and os.path.exists("%s-%s/%s.png" % (run_date, var_name, @@ -429,9 +428,10 @@ class PlotMap(MapCross, MapDrawOptions): if self.nomap: self.mgplot.frameon = False - if self.alpha != None and self.bounds != None: + if self.alpha is not None and self.bounds is not None: if self.extend in ('min', 'both'): - map_data[0] = np.ma.masked_where(map_data[0] < self.bounds[0], map_data[0]) + map_data[0] = np.ma.masked_where(map_data[0] < self.bounds[0], + map_data[0]) else: map_data[0] = np.ma.masked_where((map_data[0] >= self.bounds[0]) & @@ -446,7 +446,8 @@ class PlotMap(MapCross, MapDrawOptions): log.info(type(map_data[0])) # if not self.nocontourf and not self.nomap and self.smooth: -# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) +# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, +# map_data[0])) # mco = self.map.contourf(x, y, # map_data[0], # cmap=self.cmap, @@ -458,7 +459,8 @@ class PlotMap(MapCross, MapDrawOptions): # antialiased=True) # # elif not self.nocontourf and not self.nomap and not self.smooth: -# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, map_data[0])) +# log.info("X: %s, Y: %s, DATA: %s" % (x.shape, y.shape, +# map_data[0])) # mco = self.map.pcolormesh(x, y, # map_data[0], # cmap=self.cmap, @@ -477,7 +479,6 @@ class PlotMap(MapCross, MapDrawOptions): transform=ccrs.PlateCarree(), antialiased=True) - elif not self.nocontourf and not self.smooth: mco = self.mgaxis.pcolormesh(xloc, yloc, map_data[0], @@ -491,7 +492,8 @@ class PlotMap(MapCross, MapDrawOptions): if self.has_shape_files(): line_w = float(self.countropts[0]) for shapef in self.shapefiles: - log.info("Processing shape file: %s with line width: %s", shapef, line_w) + log.info("Processing shape file: %s with line width: %s", + shapef, line_w) adm1_shapes = list(shpreader.Reader(shapef).geometries()) self.mgaxis.add_geometries(adm1_shapes, self._crs, linewidth=line_w, @@ -499,11 +501,11 @@ class PlotMap(MapCross, MapDrawOptions): # self.map.readshapefile(shapef, "%s" % # os.path.basename(shapef), linewidth=line_w, # color=self.countropts[1]) - line_w = max(self.shapef_width_step, line_w - self.shapef_width_step) - #self.print_time("contourf") + line_w = max(self.shapef_width_step, line_w - + self.shapef_width_step) - ## FIXME Modify to use scatter inside DATA - ## if DATA.hasScatterData()... etc... + # FIXME Modify to use scatter inside DATA + # if DATA.hasScatterData()... etc... if scatter_data is not None: log.info("Plotting scatter data: %s of keys %s", str(scatter_data), str(scatter_data.keys())) @@ -554,19 +556,25 @@ class PlotMap(MapCross, MapDrawOptions): if data.wind_data: winds = data.wind_data - X, Y, U, V = delete_masked_points(x.ravel(), y.ravel(), winds['u'].ravel(), winds['v'].ravel()) - #print("Wind scale is", self.wind_units, self.wind_scale) - if winds.has_key('barbs'): - self.mgaxis.barbs(x_vec, y_vec, u_vec, v_vec, #units=self.wind_units, - #headlength=self.wind_head_length, - #headwidth=self.wind_head_width, - #width=self.wind_width, - #minshaft=self.wind_minshaft, - #scale=self.wind_scale, + x_vec, y_vec, u_vec, v_vec = delete_masked_points( + xloc.ravel(), + yloc.ravel(), + winds['u'].ravel(), + winds['v'].ravel() + ) + + if 'barbs' in winds: + self.mgaxis.barbs(x_vec, y_vec, u_vec, v_vec, + units=self.wind_units, + headlength=self.wind_head_length, + headwidth=self.wind_head_width, + width=self.wind_width, + minshaft=self.wind_minshaft, + scale=self.wind_scale, color='k') - #self.print_time("barbs") else: - quiv = self.mgaxis.quiver(x_vec, y_vec, u_vec, v_vec, units=self.wind_units, + quiv = self.mgaxis.quiver(x_vec, y_vec, u_vec, v_vec, + units=self.wind_units, headlength=self.wind_head_length, headwidth=self.wind_head_width, width=self.wind_width, @@ -581,15 +589,13 @@ class PlotMap(MapCross, MapDrawOptions): label='%s m/s' % self.wind_label_scale, coordinates='axes', labelpos='S', labelsep=0.05) - #self.print_time("quivers") if data.contour_data: interval = self.contours_int exclude = self.contours_exclude_vals - cLowBound = -99999 - cUppBound = 99999 + clow_bound = -99999 + cupp_bound = 99999 cdata = data.contour_data - #print(cdata, type(cdata)) try: # cmin = min(filter (lambda a: a > clow_bound, cdata.ravel())) cmin = cdata.where(cdata > clow_bound).min() @@ -605,7 +611,7 @@ class PlotMap(MapCross, MapDrawOptions): lvls = np.arange(adjcmin, adjcmax, interval) for exc in exclude: lvls = [exc for exc in lvls if exc != 0] - if (len(lvls) > 0) and map_data != None: + if (len(lvls) > 0) and map_data is not None: mco = plt.contourf(xloc, yloc, map_data[0], cmap=self.cmap, @@ -615,9 +621,8 @@ class PlotMap(MapCross, MapDrawOptions): horizontalalignment='center', alpha=self.alpha) - #print "-----------------", map_data, cdata, "--------------------" - if map_data != None and (map_data[0] == cdata).all(): - log.debug(":::::::::::::::::::::::: SAME !!! :::::::::::::::::::") + if map_data is not None and (map_data[0] == cdata).all(): + log.debug(":::::::::::::::::::::: SAME !!! ::::::::::::::::") mcs = plt.contour(xloc, yloc, cdata, levels=self.bounds, @@ -625,7 +630,7 @@ class PlotMap(MapCross, MapDrawOptions): linewidths=self.contours_linewidth, alpha=self.alpha) else: - log.debug(":::::::::::::::::::::::: DIFFERENT !!! :::::::::::::::::::") + log.debug(":::::::::::::::::::: DIFFERENT !!! :::::::::::::") log.debug("MIN: %s - MAX: %s", cmin, cmax) mcs = plt.contour(xloc, yloc, cdata, @@ -638,51 +643,49 @@ class PlotMap(MapCross, MapDrawOptions): self.mgplot.clabel(mcs, inline=1, fontsize=self.contours_label_fontsize, - #backgroundcolor='r', + # backgroundcolor='r', fmt=self.contours_label_format) -# else: -# print "Not drawing contours since levels has ", len(lvls), " elements" - #coords normalization + # coords normalization # lat_offset = abs(self.lat[0]) % self.lat[2] # lon_offset = abs(self.lon[0]) % self.lon[2] if not self.nomap and not self.kml and not self.kmz: if self.continents: - self.mgaxis.add_feature(cfeature.LAND, color=self.continents, zorder=10) - - _ = self.mgaxis.coastlines(resolution=self.resolution, - linewidth=float(self.coastsopts[0]), - color=str(self.coastsopts[1]), - zorder=15 - ) - - _ = self.mgaxis.add_feature(cfeature.BORDERS.with_scale(self.resolution), - linewidth=float(self.countropts[0]), - edgecolor=str(self.countropts[1]), - zorder=15 - ) - - if self.projection == 'PlateCarree': - grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, - draw_labels=True, linestyle='--', - linewidth=float(self.coordsopts[0]), - color=str(self.coordsopts[2]), - zorder=20 - ) - - grl.xlabels_top = False - grl.ylabels_right = False - grl.xlabel_style = {'size': float(self.coordsopts[1])} - grl.ylabel_style = grl.xlabel_style - else: - grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, - draw_labels=False, linestyle='--', - linewidth=float(self.coordsopts[0]), - color=str(self.coordsopts[2]), - zorder=20 - ) + self.mgaxis.add_feature(cfeature.LAND, color=self.continents, + zorder=10) + + self.mgaxis.coastlines(resolution=self.resolution, + linewidth=float(self.coastsopts[0]), + color=str(self.coastsopts[1]), + zorder=15) + + self.mgaxis.add_feature(cfeature.BORDERS.with_scale( + self.resolution), + linewidth=float(self.countropts[0]), + edgecolor=str(self.countropts[1]), + zorder=15) + + grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, + crs=self._orig_crs, + draw_labels=True, linestyle='--', + linewidth=float(self.coordsopts[0]), + color=str(self.coordsopts[2]), + zorder=20) + + grl.xlabels_top = False + grl.ylabels_right = False + grl.xlabel_style = {'size': float(self.coordsopts[1])} + grl.ylabel_style = grl.xlabel_style +# if self.projection == 'PlateCarree': +# else: +# grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, +# draw_labels=False, linestyle='--', +# linewidth=float(self.coordsopts[0]), +# color=str(self.coordsopts[2]), +# zorder=20 +# ) # Change axes for colorbar if self.colorbar and not self.kml and not self.kmz and \ @@ -703,17 +706,18 @@ class PlotMap(MapCross, MapDrawOptions): # ff = h0/w0 # form factor # height = 1024*ff # height # size = "%d,%d" % (1024, height) -# basemap_url = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/export?\ -#bbox=%s,%s,%s,%s&\ -#bboxSR=4326&\ -#imageSR=4326&\ -#size=%s&\ -#dpi=%d&\ -#format=png24&\ -#f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, self.dpi) -# self.map.imshow(imread(urlopen(basemap_url)), origin='upper') - - #logo +# basemap_url = +# "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/export?\ +# bbox=%s,%s,%s,%s&\ +# bboxSR=4326&\ +# imageSR=4326&\ +# size=%s&\ +# dpi=%d&\ +# format=png24&\ +# f=image" % (self.lon[0], self.lat[0], self.lon[-1], self.lat[-1], size, +# self.dpi) self.map.imshow(imread(urlopen(basemap_url)), origin='upper') + + # logo if self.logo: img = Image.open(self.logo[0]) # height = im.size[1] @@ -732,57 +736,53 @@ class PlotMap(MapCross, MapDrawOptions): fullname = "{}.{}".format(fig_name, self.filefmt) log.info("printing %s", fullname) if self.kml or self.kmz: - #self.mgplot.axes(frameon=0) + # self.mgplot.axes(frameon=0) if self.save: - self.mgplot.savefig(fullname, - bbox_inches='tight', - frameon=0, - pad_inches=0, dpi=self.dpi, transparent=True) + self.mgplot.savefig(fullname, bbox_inches='tight', + frameon=0, pad_inches=0, dpi=self.dpi, + transparent=True) else: - self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0,) + self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0) if self.save: - #fig = self.mgaxis.get_figure() self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) return fig_name - def compare_maps(self, map_names, outdir, date, steps, tpl=''): """ Generate maps comparison """ os.chdir(outdir) - #print "MAPNAMES", map_names - #for map_name in map_names: + # print "MAPNAMES", map_names + # for map_name in map_names: # newMapNames = map_names[0] # items = {} #[i[0] for i in map_names] # for i in range(0, len(map_names)-1): -# items, newMapNames, newMapNameTpl = self.twoMapsCompare([newMapNames, map_names[i+1]], items) +# items, newMapNames, newMapNameTpl = +# self.twoMapsCompare([newMapNames, map_names[i+1]], items) mnames = np.array(map_names) k_s = mnames.T idx = 0 for item in k_s: -# if idx >= len(newMapNames): -# continue + # if idx >= len(newMapNames): + # continue imgs = ' '.join([str(i) + '.gif' for i in item]) if tpl: - newimg = tpl % {'date':date, 'step':steps[idx]} + newimg = tpl % {'date': date, 'step': steps[idx]} else: newimg = steps[idx] - #comm = "montage %s -tile 2x -geometry 800x600+1+1 %s.gif" % (imgs, newMapNames[idx]) comm = "montage %s -tile 2x -geometry +1+1 %s.gif" % (imgs, newimg) - #print "COMPARE COMMAND:", comm _, _ = subprocess.getstatusoutput(comm) idx += 1 - #montage -tile 2x -geometry 800x600+1+1 + # montage -tile 2x -geometry 800x600+1+1 - #rename from 00-06-... to 00-01 ... + # rename from 00-06-... to 00-01 ... # st, out = commands.getstatusoutput("ls %s*" % newMapNameTpl) # if st != 0: # print "Error: %s" % str(out) @@ -791,8 +791,10 @@ class PlotMap(MapCross, MapDrawOptions): # #print names # for num in range(0, len(names)): # snum = "%02d" % num -# #print "mv %s %s.gif" % (names[num], newMapNameTpl.replace("??", snum)) -# st2, out2 = commands.getstatusoutput("mv -f %s %s.gif" % (names[num], newMapNameTpl.replace("??", snum))) +# #print "mv %s %s.gif" % (names[num], +# newMapNameTpl.replace("??", snum)) st2, out2 = +# commands.getstatusoutput("mv -f %s %s.gif" % (names[num], +# newMapNameTpl.replace("??", snum))) # if st2 != 0: # print "Error: %s" % str(out2) @@ -800,13 +802,13 @@ class PlotMap(MapCross, MapDrawOptions): nimg0 = "??" nimg1 = "loop" if tpl: - nimg0 = tpl % {'date':date, 'step':nimg0} - nimg1 = tpl % {'date':date, 'step':nimg1} - status, out = subprocess.getstatusoutput("convert -delay 75 -loop 0 %s.gif %s.gif" % (nimg0, nimg1)) + nimg0 = tpl % {'date': date, 'step': nimg0} + nimg1 = tpl % {'date': date, 'step': nimg1} + status, out = subprocess.getstatusoutput( + "convert -delay 75 -loop 0 %s.gif %s.gif" % (nimg0, nimg1)) if status != 0: log.error("Error: %s", str(out)) - def gen_kml(self, fig_names, srcvars, dims, online=True): """ """ from lxml import etree @@ -819,13 +821,14 @@ class PlotMap(MapCross, MapDrawOptions): lat = self.lat log.info(fig_names) - #kml_name = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) + # kml_name = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) kml_name = os.path.basename(fig_names[0])[:-3] - var_name = srcvars[0] #fig_names[0].split('-')[-2] - run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") - run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") #.strftime("%Y%m%d%H") + var_name = srcvars[0] # fig_names[0].split('-')[-2] + run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") + run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") begindate = run_date0.strftime("%Y%m%d%H") - dir_name = os.path.join(self.outdir, begindate + '-' + var_name).replace('./', '') + dir_name = os.path.join(self.outdir, begindate + '-' + + var_name).replace('./', '') interval = run_date1 - run_date0 datefmt = "%Y-%m-%dT%H:00:00Z" @@ -845,11 +848,16 @@ class PlotMap(MapCross, MapDrawOptions): KML.Icon( KML.href("%s/%s-colorbar.png" % (dir_name, begindate)), ), - KML.overlayXY(x="0", y="1", xunits="fraction", yunits="fraction"), - KML.screenXY(x=".01", y="0.55", xunits="fraction", yunits="fraction"), - KML.rotationXY(x="0", y="0", xunits="fraction", yunits="fraction"), - KML.size(x="0", y="0.5", xunits="fraction", yunits="fraction"), - id="%s-ScreenOverlay" %(begindate), + KML.overlayXY(x="0", y="1", xunits="fraction", + yunits="fraction"), + KML.screenXY(x=".01", y="0.55", xunits="fraction", + yunits="fraction"), + KML.rotationXY(x="0", y="0", xunits="fraction", + yunits="fraction"), + KML.size(x="0", y="0.5", + xunits="fraction", + yunits="fraction"), + id="%s-ScreenOverlay" % begindate, ), ) ) @@ -895,7 +903,7 @@ class PlotMap(MapCross, MapDrawOptions): os.rename(fig_name, fig_path) zfile.write(fig_path) - outf = file("%s.kml" % kml_name, 'w') + outf = open("%s.kml" % kml_name, 'w') outf.write('\n') outf.write(etree.tostring(doc, pretty_print=True)) outf.close() @@ -910,8 +918,8 @@ class PlotMap(MapCross, MapDrawOptions): os.rmdir(dir_name) os.remove("%s.kml" % kml_name) - - def aplot(self, xloc, yloc, map_data=None, wind_data=None, contour_data=None, scatter_data=None, **kwargs): + def aplot(self, xloc, yloc, map_data=None, wind_data=None, + contour_data=None, scatter_data=None, **kwargs): """ Plot one or more maps directly from numerical arrays """ # store options to be restored at the end @@ -928,12 +936,14 @@ class PlotMap(MapCross, MapDrawOptions): self.wind_scale = int(self.windopts[3]) log.info("SHP %s", str(self.shapefiles)) if self.shapefiles: - self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] + self.shapefiles = [f.replace(".shp", "") for f in + glob(self.shapefiles)] log.info("SHP %s", str(self.shapefiles)) if scatter_data is not None: if isinstance(scatter_data, str): - scatter_data = DataFrameHandler(parse_path(self.indir, scatter_data)).filter() + scatter_data = DataFrameHandler( + parse_path(self.indir, scatter_data)).filter() else: scatter_data = DataFrameHandler(scatter_data).filter() else: @@ -941,7 +951,7 @@ class PlotMap(MapCross, MapDrawOptions): log.info("scatter data: %s", str(scatter_data)) data = MapData( - map_data=[map_data], #temporarily + map_data=[map_data], # temporarily wind_data=wind_data, contour_data=contour_data, scatter_data=scatter_data @@ -956,7 +966,7 @@ class PlotMap(MapCross, MapDrawOptions): fig_name = "%s/%s" % (self.outdir, f_name) if self.subplot is None: plt.clf() - self.mgaxis = plt.axes(projection=self._crs) #.PlateCarree()) + self.mgaxis = plt.axes(projection=self._crs) self.mgplot = plt.gcf() else: self.mgaxis = plt.subplot(self.subplot[0], @@ -970,14 +980,15 @@ class PlotMap(MapCross, MapDrawOptions): grid, data, self.title, - #scatter_data=scatter_data + # scatter_data=scatter_data ) # # Create Max at required intervals # if self.maxdata and n_time in self.maxdata: # if (self.maxtitle != None): # fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) -# p_title = self.set_title(self.maxtitle, s_date, curr_date, s_time) +# p_title = self.set_title(self.maxtitle, s_date, curr_date, +# s_time) # self.gen_image_map( # fig_name, # grid, @@ -1030,19 +1041,21 @@ class PlotMap(MapCross, MapDrawOptions): self.wind_scale = int(self.windopts[3]) log.info("SHP %s", str(self.shapefiles)) if self.shapefiles: - self.shapefiles = [f.replace(".shp", "") for f in glob(self.shapefiles)] + self.shapefiles = [f.replace(".shp", "") for f in + glob(self.shapefiles)] log.info("SHP %s", str(self.shapefiles)) if self.scatter is not None: if isinstance(self.scatter, str): - scatter_data = DataFrameHandler(parse_path(self.indir, self.scatter)).filter() + scatter_data = DataFrameHandler( + parse_path(self.indir, self.scatter)).filter() else: scatter_data = DataFrameHandler(self.scatter).filter() else: scatter_data = None log.info("scatter data: %s", str(scatter_data)) - #run = '' + # run = '' run_tmp = '' map_names = [] @@ -1072,33 +1085,36 @@ class PlotMap(MapCross, MapDrawOptions): if self.timesteps == 'all': self.timesteps = list(sorted(dims.keys())) - run_tmp = dims[0] #12:30Z30NOV2010 + run_tmp = dims[0] # 12:30Z30NOV2010 if run_tmp[:-10] == "01:30": run_tmp = run_tmp.replace("01:30", "00") - #run = "%sh %s %s %s" % (run_tmp[:-10], run_tmp[-9:-7], run_tmp[-7:-4], run_tmp[-4:]) + # run = "%sh %s %s %s" % (run_tmp[:-10], run_tmp[-9:-7], + # run_tmp[-7:-4], run_tmp[-4:]) # return grid grid = nc_handler.grid - #if not NOMAP: + # if not NOMAP: self.init_map(grid) s_date = datetime.strptime("%s %s %s %s" % (run_tmp[-4:], run_tmp[-7:-4], run_tmp[-9:-7], - run_tmp[0:2]), "%Y %b %d %H") + run_tmp[0:2]), + "%Y %b %d %H") ss_date = s_date.strftime("%Y%m%d") steps = [] start = self.timesteps[0] - #log.info("Start @ %s - s_date: %s - run_tmp: %s" % (START, s_date, run_tmp)) + # log.info("Start @ %s - s_date: %s - run_tmp: %s" % (START, s_date, + # run_tmp)) if start is None: start = 0 - for n_time in self.timesteps: #range(START,int(TOTAL),INTERVAL): + for n_time in self.timesteps: # range(START,int(TOTAL),INTERVAL): if self.subplot is None: plt.clf() self.mgaxis = plt.axes(projection=self._crs) @@ -1107,11 +1123,9 @@ class PlotMap(MapCross, MapDrawOptions): self.mgaxis = plt.subplot(self.subplot[0], self.subplot[1], self.subplot[3], - projection=self._crs - ) + projection=self._crs) self.mgplot = plt.gcf() - valid_tmp = dims[n_time] curr_date = datetime.strptime("%s %s %s %s" % (valid_tmp[-4:], valid_tmp[-7:-4], @@ -1130,19 +1144,23 @@ class PlotMap(MapCross, MapDrawOptions): if self.img_template: f_name = self.img_template % {'date': ss_date, 'step': s_time} fig_name = "%s/%s" % (self.outdir, f_name) - loop_name = self.img_template % {'date': ss_date, 'step': 'loop'} + loop_name = self.img_template % {'date': ss_date, + 'step': 'loop'} else: - f_name = '.'.join(os.path.basename(self.srcfiles[0]).split('.')[:-1]) + f_name = '.'.join(os.path.basename(self.srcfiles[0]) + .split('.')[:-1]) fig_name = "%s/%s_%s" % (self.outdir, f_name, s_time) loop_name = "%s_loop" % (f_name) - #valid = "%02dUTC %s" % (p_time % 24, curr_date.strftime("%d %b %Y")) + # valid = "%02dUTC %s" % (p_time % 24, curr_date.strftime("%d %b + # %Y")) - #stime = "%02d" % (int(run_tmp.split('Z')[0])+p_time) + # stime = "%02d" % (int(run_tmp.split('Z')[0])+p_time) stime = "%02d" % (p_time) - p_title = self.set_title(self.title, s_date, curr_date, s_time, stime) + p_title = self.set_title(self.title, s_date, curr_date, s_time, + stime) cur_scatter_data = None - if scatter_data != None and (curr_date in scatter_data): + if scatter_data is not None and (curr_date in scatter_data): cur_scatter_data = scatter_data[curr_date] fname = self.gen_image_map( @@ -1158,9 +1176,10 @@ class PlotMap(MapCross, MapDrawOptions): # Create Max at required intervals if self.maxdata and n_time in self.maxdata: - if self.maxtitle != None: + if self.maxtitle is not None: fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) - p_title = self.set_title(self.maxtitle, s_date, curr_date, s_time, stime) + p_title = self.set_title(self.maxtitle, s_date, curr_date, + s_time, stime) self.gen_image_map( fig_name, grid, @@ -1168,16 +1187,17 @@ class PlotMap(MapCross, MapDrawOptions): p_title ) else: - log.info("Missing MAXTITLE, thus not generating MAX images!") + log.info("Missing MAXTITLE, not generating MAX images!") if self.kml or self.kmz: plt.clf() run_date = s_date.strftime("%Y%m%d%H") - #separate colorbar + # separate colorbar mpl.rcParams['axes.linewidth'] = 0.1 fig = plt.figure(figsize=(.1, 12)) - cax = fig.add_axes([0.05, 0.80, 0.9, 0.15]) # , axisbg=(1, 1, 1, 0)) + cax = fig.add_axes([0.05, 0.80, 0.9, 0.15]) + # , axisbg=(1, 1, 1, 0)) print("...", self.bounds, "...") cbar = mpl.colorbar.ColorbarBase( cax, # =self.mgaxis, @@ -1208,7 +1228,7 @@ class PlotMap(MapCross, MapDrawOptions): var_unit = '' plt.xlabel( p_title, - #"%s\n%s" % (self.srcvars[0], var_unit), + # "%s\n%s" % (self.srcvars[0], var_unit), horizontalalignment='left', color='w', fontsize=6, @@ -1222,33 +1242,36 @@ class PlotMap(MapCross, MapDrawOptions): if self.save: fig.savefig( - "%s/%s-%s/%s-colorbar.png" % (self.outdir, run_date, self.srcvars[0], run_date), + "%s/%s-%s/%s-colorbar.png" % (self.outdir, run_date, + self.srcvars[0], run_date), bbox_inches='tight', pad_inches=0, dpi=self.dpi, transparent=True ) - #generate KMZ - Offline + # generate KMZ - Offline if self.kmz: log.info("Generating KMZ ...") self.gen_kml(fig_names, self.srcvars, dims, online=False) - #generate KML - Online + # generate KML - Online if self.kml: log.info("Generating KML ...") self.gen_kml(fig_names, self.srcvars, dims) - #generate animation. + # generate animation. if self.anim: self.gen_anim( "%s.%s" % (loop_name.replace('loop', '*'), self.filefmt), "%s.gif" % loop_name ) -# fulloutdir = os.path.join(self.outdir, os.path.dirname(self.img_template)) +# fulloutdir = os.path.join(self.outdir, +# os.path.dirname(self.img_template)) # loop_name = os.path.basename(loop_name) # self.gen_anim( # fulloutdir, -# "%s.%s" % (loop_name.replace('loop','*'), self.filefmt), +# "%s.%s" % (loop_name.replace('loop','*'), +# self.filefmt), # fulloutdir, # "%s.gif" % loop_name # ) @@ -1258,20 +1281,18 @@ class PlotMap(MapCross, MapDrawOptions): # restore options without local function parameters vars(self).update(localvars) - #print "Returning ", plt_names - #return plt_names + # print "Returning ", plt_names + # return plt_names # num = len(map_names) # if num > 1: # mg.compare_maps(map_names, OUTDIR, ss_date, steps, JOINT_TEMPLATE ) - def reset_conf(self): """ Back to the initial conditions. """ self.__init__() - def load_conf(self, section=None, fpath=None, reset=False): """ Load existing configurations from file. """ @@ -1294,7 +1315,6 @@ class PlotMap(MapCross, MapDrawOptions): self.reset_conf() vars(self).update(parse_parameters(opts)) - def write_conf(self, section, fpath=None): """ Write configurations on file. """ @@ -1316,7 +1336,8 @@ class PlotMap(MapCross, MapDrawOptions): self.reset_conf() defaults = copy.deepcopy(vars(self)) # calculate diff - diff = {k : curropts[k] for k in curropts if curropts[k] != defaults[k]} + diff = {k: curropts[k] for k in curropts if curropts[k] != + defaults[k]} # reload diff vars(self).update(diff) # save diff -- GitLab From f86b8c58ef997f43782e432094c7680ddce287bb Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 29 Oct 2020 13:32:53 +0100 Subject: [PATCH 18/27] Cleaned code and created init_environ function --- mapgenerator/plotting/plotmap.py | 34 +++++++++++++------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 7f7eeaf..448d5cb 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -918,15 +918,13 @@ class PlotMap(MapCross, MapDrawOptions): os.rmdir(dir_name) os.remove("%s.kml" % kml_name) - def aplot(self, xloc, yloc, map_data=None, wind_data=None, - contour_data=None, scatter_data=None, **kwargs): - """ Plot one or more maps directly from numerical arrays """ + def init_environ(self, **kwargs): + """ initialize environment with all arguments """ - # store options to be restored at the end - localvars = copy.deepcopy(vars(self)) # update from config file if ('config' in kwargs) and ('section' in kwargs): self.load_conf(kwargs['section'], kwargs['config']) + # update from command line vars(self).update(parse_parameters(kwargs)) # arguments are only local @@ -940,6 +938,15 @@ class PlotMap(MapCross, MapDrawOptions): glob(self.shapefiles)] log.info("SHP %s", str(self.shapefiles)) + def aplot(self, xloc, yloc, map_data=None, wind_data=None, + contour_data=None, scatter_data=None, **kwargs): + """ Plot one or more maps directly from numerical arrays """ + + # store options to be restored at the end + localvars = copy.deepcopy(vars(self)) + + self.init_environ(**kwargs) + if scatter_data is not None: if isinstance(scatter_data, str): scatter_data = DataFrameHandler( @@ -1019,12 +1026,8 @@ class PlotMap(MapCross, MapDrawOptions): # store options to be restored at the end localvars = copy.deepcopy(vars(self)) - # update from config file - if ('config' in kwargs) and ('section' in kwargs): - self.load_conf(kwargs['section'], kwargs['config']) - # update from command line - vars(self).update(parse_parameters(kwargs)) - # arguments are only local + + self.init_environ(**kwargs) if isinstance(self.srcvars, str): self.srcvars = [self.srcvars] @@ -1036,15 +1039,6 @@ class PlotMap(MapCross, MapDrawOptions): elif not isinstance(self.srcfiles, list): log.error('Error') - # parse wind arguments - if self.windopts and len(self.windopts) == 4: - self.wind_scale = int(self.windopts[3]) - log.info("SHP %s", str(self.shapefiles)) - if self.shapefiles: - self.shapefiles = [f.replace(".shp", "") for f in - glob(self.shapefiles)] - log.info("SHP %s", str(self.shapefiles)) - if self.scatter is not None: if isinstance(self.scatter, str): scatter_data = DataFrameHandler( -- GitLab From 5c02025c2a78d0d85b9c81100f8167eff126cf3c Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 29 Oct 2020 14:14:42 +0100 Subject: [PATCH 19/27] Optimized some funcions --- mapgenerator/plotting/plotmap.py | 61 ++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 448d5cb..9d55fdb 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -106,6 +106,25 @@ class PlotMap(MapCross, MapDrawOptions): def set_color_map(self): """ Create color map """ + + extend_opts = { + 'max': { + 'cmap_arg': self.colors[:-1], + 'cmap_ext': ('set_over'), + 'ext_arg': (self.colors[-1]) + }, + 'min': { + 'cmap_arg': self.colors[1:], + 'cmap_ext': ('set_under'), + 'ext_arg': (self.colors[0]) + }, + 'both': { + 'cmap_arg': self.colors[1:-1], + 'cmap_ext': ('set_over', 'set_under'), + 'ext_arg': (self.colors[-1], self.colors[0]) + } + } + if not isinstance(self.colors, str) and \ isinstance(self.colors, Iterable): # adjust number of colors to number of bounds @@ -116,16 +135,13 @@ class PlotMap(MapCross, MapDrawOptions): elif self.extend == 'both': if len(self.bounds)+1 < len(self.colors): self.colors = self.colors[:len(self.bounds)+1] - if self.extend == 'max': - self.cmap = mpl.colors.ListedColormap(self.colors[:-1]) - self.cmap.set_over(self.colors[-1]) - elif self.extend == 'min': - self.cmap = mpl.colors.ListedColormap(self.colors[1:]) - self.cmap.set_under(self.colors[0]) - elif self.extend == 'both': - self.cmap = mpl.colors.ListedColormap(self.colors[1:-1]) - self.cmap.set_over(self.colors[-1]) - self.cmap.set_under(self.colors[0]) + + if self.extend in extend_opts: + self.cmap = mpl.colors.ListedColormap( + extend_opts[self.extend]['cmap_arg']) + for ext, arg in zip(extend_opts[self.extend]['cmap_ext'], + extend_opts[self.extend]['ext_arg']): + getattr(self.cmap, ext)(arg) else: self.cmap = mpl.colors.ListedColormap(self.colors) custom_cmap = True @@ -667,25 +683,24 @@ class PlotMap(MapCross, MapDrawOptions): edgecolor=str(self.countropts[1]), zorder=15) + if self.projection == 'PlateCarree': + draw_labels = True + else: + draw_labels = False + grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, crs=self._orig_crs, - draw_labels=True, linestyle='--', + draw_labels=draw_labels, + linestyle='--', linewidth=float(self.coordsopts[0]), color=str(self.coordsopts[2]), zorder=20) - grl.xlabels_top = False - grl.ylabels_right = False - grl.xlabel_style = {'size': float(self.coordsopts[1])} - grl.ylabel_style = grl.xlabel_style -# if self.projection == 'PlateCarree': -# else: -# grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, -# draw_labels=False, linestyle='--', -# linewidth=float(self.coordsopts[0]), -# color=str(self.coordsopts[2]), -# zorder=20 -# ) + if draw_labels: + grl.xlabels_top = False + grl.ylabels_right = False + grl.xlabel_style = {'size': float(self.coordsopts[1])} + grl.ylabel_style = grl.xlabel_style # Change axes for colorbar if self.colorbar and not self.kml and not self.kmz and \ -- GitLab From de45d4e629c1c353d865eac1f13e6888cce535de Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Thu, 29 Oct 2020 14:46:56 +0100 Subject: [PATCH 20/27] Support polar stereo projections --- mapgenerator/plotting/plotmap.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index fc26688..a2fb859 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -5,6 +5,7 @@ # @author: see AUTHORS file +from cartopy.crs import PlateCarree import matplotlib as mpl mpl.use('Agg') @@ -13,6 +14,7 @@ import cartopy.crs as ccrs import cartopy.feature as cfeature import cartopy.io.shapereader as shpreader from matplotlib.cbook import delete_masked_points +from matplotlib.path import Path # from matplotlib.image import imread import numpy as np from datetime import datetime @@ -367,11 +369,24 @@ class PlotMap(MapCross, MapDrawOptions): # mpl.rcParams.update(params) # Draw filled contour - self.mgaxis.set_aspect(self.xsize/self.ysize) + # self.mgaxis.set_aspect(self.xsize/self.ysize) + if self.projection.lower().endswith('polarstereo'): + self.mgaxis.set_extent([-180, 179.9999999999999, + self.lat[0], self.lat[-1]], + ccrs.PlateCarree()) + # Compute a circle in axes coordinates, which we can use as a boundary + # for the map. We can pan/zoom as much as we like - the boundary will be + # permanently circular. + theta = np.linspace(0, 2*np.pi, 100) + center, radius = [0.5, 0.5], 0.5 + verts = np.vstack([np.sin(theta), np.cos(theta)]).T + circle = Path(verts * radius + center) + self.mgaxis.set_boundary(circle, transform=self.mgaxis.transAxes) + else: + self.mgaxis.set_extent([self.lon[0], self.lon[-1], + self.lat[0], self.lat[-1]], + ccrs.PlateCarree()) - self.mgaxis.set_extent([self.lon[0], self.lon[-1], - self.lat[0], self.lat[-1]], - crs=self._crs) # if self.subplot: # self.map = Basemap( # ax=self.mgaxis, -- GitLab From ea490edab2f7493fa5755d77dbb3ff850605d370 Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 3 Nov 2020 14:21:28 +0100 Subject: [PATCH 21/27] Moved some functions outside PlotMap class to the tools.py module --- mapgenerator/plotting/definitions.py | 239 ++++++++++++++------------- mapgenerator/plotting/plotmap.py | 223 +++---------------------- 2 files changed, 146 insertions(+), 316 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 4017fd6..c218f09 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -5,8 +5,8 @@ # @author: see AUTHORS file from netCDF4 import Dataset as nc -#from netCDF4 import MFDataset as mnc -#from grads import GaLab +# from netCDF4 import MFDataset as mnc +# from grads import GaLab import pandas as pd import numpy as np import numpy.ma as ma @@ -19,34 +19,34 @@ import copy import logging -#logging.basicConfig(level=logging.INFO) -log = logging.getLogger(__name__) +# logging.basicConfig(level=logging.INFO) +LOG = logging.getLogger(__name__) def is_grid_regular(lon, lat): """ Check spacing if uniform. Returns True if grid is regular """ - #print("Checking if lon is strictly_increasing ...") - if not np.array([strictly_increasing(lon[x]) \ - for x in np.arange(lon.shape[0])]).all(): + # print("Checking if lon is strictly_increasing ...") + if not np.array([strictly_increasing(lon[x]) for x in + np.arange(lon.shape[0])]).all(): return False - #print("Checking if lat is strictly_increasing ...") - if not np.array([strictly_increasing(lat[:, y]) \ - for y in np.arange(lat.shape[1])]).all(): + # print("Checking if lat is strictly_increasing ...") + if not np.array([strictly_increasing(lat[:, y]) for y in + np.arange(lat.shape[1])]).all(): return False - #print("Checking lon spacing ...") - is_x_reg = np.array([(np.spacing(lon[x]) == np.spacing(lon[x+1])).all() \ - for x in np.arange(lon.shape[0]-1)]).all() + # print("Checking lon spacing ...") + is_x_reg = np.array([(np.spacing(lon[x]) == np.spacing(lon[x+1])).all() + for x in np.arange(lon.shape[0]-1)]).all() - print(is_x_reg) + LOG(is_x_reg) if not is_x_reg: return is_x_reg - #print("Checking lat spacing ...") - return np.array([(np.spacing(lat[:, y]) == np.spacing(lat[:, y+1])).all() \ - for y in np.arange(lat.shape[1]-1)]).all() + # print("Checking lat spacing ...") + return np.array([(np.spacing(lat[:, y]) == np.spacing(lat[:, y+1])).all() + for y in np.arange(lat.shape[1]-1)]).all() def strictly_increasing(vector): @@ -60,12 +60,13 @@ def do_interpolation(data, lon, lat): avoid """ # lon as second dimension # lat as first dimension - log.info("Irregular grid, performing interpolation ...") + LOG.info("Irregular grid, performing interpolation ...") lat_s = lat.shape[0] lon_s = lon.shape[1] reglon = np.linspace(lon.min(), lon.max(), lon_s) reglat = np.linspace(lat.min(), lat.max(), lat_s) - log.info("LAT: %s, LON: %s, DATA: %s" % (reglon.shape, reglat.shape, data.shape)) + LOG.info("LAT: %s, LON: %s, DATA: %s", reglon.shape, reglat.shape, + data.shape) regx, regy = np.meshgrid(reglon, reglat) result = np.empty(data.shape)*np.nan if ma.is_masked(data): @@ -73,17 +74,20 @@ def do_interpolation(data, lon, lat): # assuming that lat and lon are the latest dimensions if len(data.shape) == 2: - result = griddata((lon.ravel(), lat.ravel()), data.ravel(), (regx, regy)) + result = griddata((lon.ravel(), lat.ravel()), data.ravel(), (regx, + regy)) elif len(data.shape) == 3: for i in range(data.shape[0]): - tmp = griddata((lon.ravel(), lat.ravel()), data[i].ravel(), (regx, regy)) + tmp = griddata((lon.ravel(), lat.ravel()), data[i].ravel(), (regx, + regy)) result[i] = tmp.reshape(result[i].shape) elif len(data.shape) == 4: for i in range(data.shape[0]): for j in range(data.shape[1]): - tmp = griddata((lon.ravel(), lat.ravel()), data[i, j].ravel(), (regx, regy)) + tmp = griddata((lon.ravel(), lat.ravel()), data[i, j].ravel(), + (regx, regy)) result[i, j, :, :] = tmp.reshape(result[i, j, :, :].shape) - #elif len(data.shape) == 5: + # elif len(data.shape) == 5: return np.ma.masked_where(np.isnan(result), result), regx, regy @@ -91,32 +95,35 @@ def extract_coords(self, var, coord='lons'): """ Given a variable and a coordinate name (lons, lats) extract values related to given coordinate interval """ var = var[:].squeeze() - log.info("VAR SHAPE %s" % str(var.shape)) + LOG.info("VAR SHAPE %s" % str(var.shape)) if not self.subsetting: - log.info("Not subsetting") + LOG.info("Not subsetting") return var[:], None cur_coord = self.__getattribute__(coord) - log.info("Coord: %s, cur_coord: %s" % (coord, str(cur_coord))) - if cur_coord:# and len(cur_coord) in (2,3): + LOG.info("Coord: %s, cur_coord: %s" % (coord, str(cur_coord))) + if cur_coord: # and len(cur_coord) in (2,3): if len(var.shape) == 2: if coord == 'lons': var1 = var[0] else: var1 = var[:, 0] - else: var1 = var + else: + var1 = var if strictly_increasing(var1[:]): - idxs = np.where((var1[:] >= cur_coord[0]) & \ - (var1[:] <= cur_coord[-1])) - log.info("idxs %s %s %s %s" % (str(idxs), var1.shape, len(idxs), idxs[0].shape)) + idxs = np.where((var1[:] >= cur_coord[0]) & + (var1[:] <= cur_coord[-1])) + LOG.info("idxs %s %s %s %s", str(idxs), var1.shape, len(idxs), + idxs[0].shape) return var1[idxs], idxs[0] - log.info("Not subsetting") + LOG.info("Not subsetting") return var[:], None def parse_parameters_list(plist): """ Parser for list parameter to be able to enter intervals and number of steps (examples: 0,1,2 0-6,2 ...""" - if isinstance(plist, list) or isinstance(plist, tuple) or isinstance(plist, np.ndarray): + if isinstance(plist, list) or isinstance(plist, tuple) or \ + isinstance(plist, np.ndarray): if len(plist) == 1: plist = plist[0] else: @@ -134,11 +141,11 @@ def parse_parameters_list(plist): steps = (end-start)/10 else: steps = float(steps) - return list(np.arange(start, end+int(steps), steps)) #, endpoint=True)) + return list(np.arange(start, end+int(steps), steps)) else: res = eval(str(plist).replace('m', '-')) if isinstance(res, int) or isinstance(res, float): - return [res,] + return [res] return list(res) @@ -148,7 +155,7 @@ def parse_parameter(var, val): if var in ('lon', 'lat', 'timesteps', 'bounds'): if var == 'timesteps' and val in ('all', ['all']): return 'all' - if val != None: + if val is not None: return parse_parameters_list(val) # parse float arguments @@ -159,7 +166,7 @@ def parse_parameter(var, val): # parse simple list arguments elif var in ('coordsopts', 'coastsopts', 'countropts'): if val and isinstance(val, str): - #log.info(val, type(val)) + LOG.debug(val, type(val)) return val.split(',') return val @@ -169,20 +176,20 @@ def parse_parameters(params): """ Parser for parameters """ ret = copy.deepcopy(params) for var in params: - #log.info("{}: {}".format(var, params[var])) + LOG.debug("%s: %s", var, params[var]) val = parse_parameter(var, params[var]) ret[var] = val - #log.info("{}: {}".format(var, val)) + LOG.debug("%s: %s", var, val) return ret def parse_path(directory, dfile): """ Path parser """ - #print "Opening data file", f + # print "Opening data file", f if os.path.isabs(dfile): return dfile else: - #log.info("Input: %s", f) + LOG.debug("Input: %s", dfile) return os.path.join(directory, dfile) @@ -190,8 +197,8 @@ class MapData(object): """Wrapper class to handle all the data needed to draw a map""" def __init__(self, loglevel='INFO', **kwargs): - log.setLevel(loglevel) - log.info("map_data initializer") + LOG.setLevel(loglevel) + LOG.info("map_data initializer") self.reset() self.max_reset() # Process optional arguments @@ -209,11 +216,9 @@ class MapData(object): self.max_data = None def set_grid_data(self, data): - #print "Setting grid data" self.grid_data = data def set_map_data(self, data): - #print "Setting map data" self.map_data = data def set_max_data(self, data): @@ -247,38 +252,26 @@ class MapData(object): return self.scatter_data def has_map_data(self): - return (self.map_data != None) + return (self.map_data is not None) def has_wind_data(self): - return (self.wind_data != None) + return (self.wind_data is not None) def has_contour_data(self): return self.contour_data is not None def has_max_data(self): - return (self.max_data != None) + return (self.max_data is not None) def has_scatter_data(self): - return (self.scatter_data != None) + return (self.scatter_data is not None) class MapDataHandler(object): -# ncdf = None -# data = None -# srcs = [] -# srcgaps = [] -# windsrc = [] -# windopts = [] -# indir = None -# lats = [] -# lons = [] -# max = None -# transf = False def __init__(self, sources, srcvars, indir, srcgaps, windsource, windopts, lats, lons, transf, subsetting, dimension, **kwargs): - #self.galab = GaLab(Bin='grads', Window=False, Echo=False) - self.ncdf = [[], []] # list of lists, srcfiles and windfiles + self.ncdf = [[], []] # list of lists, srcfiles and windfiles self.srcs = sources self.srcvars = srcvars self.srcgaps = srcgaps @@ -302,14 +295,14 @@ class MapDataHandler(object): if ('winds' in kwargs) \ and (kwargs['winds']['src']) \ and (kwargs['winds']['opts']): - log.info("Will do winds") + LOG.info("Will do winds") self.do_winds = True self.windsrc = kwargs['winds']['src'] self.windopts = kwargs['winds']['opts'] if ('contours' in kwargs) \ and (kwargs['contours']['var']): - log.info("Will do contours") + LOG.info("Will do contours") self.do_contours = True self.contoursrc = self.srcs[0] self.contourvar = kwargs['contours']['var'] @@ -319,7 +312,6 @@ class MapDataHandler(object): if vcond: self.varconds = eval(vcond) - #self.galab.cmd("reinit") # Open sources for sfile in self.srcs: fpath = parse_path(self.indir, sfile) @@ -330,7 +322,8 @@ class MapDataHandler(object): if vname in ('longitude', 'lon', 'nav_lon'): x, x_idxs = extract_coords(self, fin.variables[vname]) elif vname in ('latitude', 'lat', 'nav_lat'): - y, y_idxs = extract_coords(self, fin.variables[vname], coord='lats') + y, y_idxs = extract_coords(self, fin.variables[vname], + coord='lats') self.grid = (x, y) if x_idxs is not None and y_idxs is not None: @@ -338,9 +331,9 @@ class MapDataHandler(object): # Open wind source if available for w in self.windsrc: - log.info("WINDS: %s" % w) + LOG.info("WINDS: %s", w) wpath = parse_path(self.indir, w) - log.info("WPATH: %s" % wpath) + LOG.info("WPATH: %s", wpath) self.ncdf[1].append(nc(wpath)) # if self.transf: @@ -353,12 +346,11 @@ class MapDataHandler(object): f.close() for w in self.ncdf[1]: w.close() - #print "Resetting GraDs engine" def dim_operation(self, srcvar, srcfile): """ Operations over dimensions """ dim, oper = self.dimension.split(',') - log.info("Dimension %s, operation or index/value: %s" % (dim, oper)) + LOG.info("Dimension %s, operation or index/value: %s" % (dim, oper)) dimidx = srcvar.dimensions.index(dim) # string try: @@ -377,21 +369,22 @@ class MapDataHandler(object): def get_data_for_tstep(self, step): """ Get data per timestep """ self.data.reset() - #print "Getting map data for step ", step + # print "Getting map data for step ", step self.data.set_map_data(self.get_map_data_for_tstep(step)) if self.do_winds: - log.info("Getting wind data for step '%s'" % step) + LOG.info("Getting wind data for step '%s'" % step) self.data.set_wind_data(self.get_wind_data_for_tstep(step)) if self.do_contours: - log.info("Getting contour data for step '%s'" % step) + LOG.info("Getting contour data for step '%s'" % step) self.data.set_contour_data(self.get_contour_data_for_tstep(step)) # Calculate maximum over data # FIXME should this step be optional? # if (self.data.has_max_data()): -# self.data.set_max_data(maximum(self.data.get_max_data(), self.data.get_map_data())) +# self.data.set_max_data(maximum(self.data.get_max_data(), +# self.data.get_map_data())) # else: # self.data.set_max_data(self.data.get_map_data()) # self.last_tstep = step @@ -401,7 +394,8 @@ class MapDataHandler(object): '''Get current max data. NOTE: A call to this method will reset current max data, so it can be called only once! ''' - log.info("Retrieving max (last calculated tstep: %d); this will reset current MAX data!!" % self.last_tstep) + LOG.info("""Retrieving max (last calculated tstep: %d); this will reset + current MAX data!!""", self.last_tstep) max_data = MapData(map_data=self.data.get_max_data()) self.data.max_reset() return max_data @@ -412,8 +406,8 @@ class MapDataHandler(object): for src_n in range(len(self.srcs)): tstep = step + int(self.srcgaps[src_n]) srcfile = self.ncdf[0][src_n] - #TODO zoom on lat lon according to the domain - #FIXME variable operations + # TODO zoom on lat lon according to the domain + # FIXME variable operations # if self.varconds and len(self.varconds)>=src_n: # varcond = self.varconds[src_n] # cond = varcond[0] @@ -443,11 +437,14 @@ class MapDataHandler(object): srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: # if no expression - #log.info("SRCVAR: %s - GRID_IDXS: %s" % (str(srcvar.shape), str(self.grid_idxs))) - values = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + # LOG.info("SRCVAR: %s - GRID_IDXS: %s" % + # (str(srcvar.shape), str(self.grid_idxs))) + values = srcvar[tstep, :, :][self.grid_idxs[1], + :][:, self.grid_idxs[0]] else: values = srcvar[tstep, :, :] - log.info("1. SRCVAR: %s - GRID_IDXS: %s" % (str(values.shape), str(self.grid_idxs))) + LOG.info("1. SRCVAR: %s - GRID_IDXS: %s", str(values.shape), + str(self.grid_idxs)) else: # otherwise search for a variable of the dataset into the # expression given @@ -458,14 +455,16 @@ class MapDataHandler(object): if self.dimension: srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - newvar = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + newvar = srcvar[tstep, :, :][ + self.grid_idxs[1], :][:, self.grid_idxs[0]] else: newvar = srcvar[tstep, :, :] values = eval(expvar) break - log.info("2. SRCVAR: %s - GRID_IDXS: %s" % (str(values.shape), str(self.grid_idxs))) + LOG.info("2. SRCVAR: %s - GRID_IDXS: %s", str(values.shape), + str(self.grid_idxs)) if map_data: - #exp_res = self.ncdf.exp(EXPVAR) + # exp_res = self.ncdf.exp(EXPVAR) map_data.append(values) else: map_data = [values] @@ -475,7 +474,7 @@ class MapDataHandler(object): def get_wind_data_for_tstep(self, tstep): """ Get wind data for tstep """ wind_data = {'u': None, 'v': None} - log.info("WINDOPTS: %s" % str(self.windopts)) + LOG.info("WINDOPTS: %s" % str(self.windopts)) srcfile = self.ncdf[1][0] srcvar_u = srcfile.variables[self.windopts[0]] srcvar_v = srcfile.variables[self.windopts[1]] @@ -483,8 +482,10 @@ class MapDataHandler(object): srcvar_u = self.dim_operation(srcvar_u, srcfile) srcvar_v = self.dim_operation(srcvar_v, srcfile) if self.grid_idxs: - wind_data['u'] = srcvar_u[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] - wind_data['v'] = srcvar_v[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + wind_data['u'] = srcvar_u[tstep, :, :][self.grid_idxs[1], + :][:, self.grid_idxs[0]] + wind_data['v'] = srcvar_v[tstep, :, :][self.grid_idxs[1], + :][:, self.grid_idxs[0]] else: wind_data['u'] = srcvar_u[tstep, :, :] wind_data['v'] = srcvar_v[tstep, :, :] @@ -495,33 +496,35 @@ class MapDataHandler(object): def get_contour_data_for_tstep(self, tstep): """ Get contour data for tstep """ cdata = None - log.info("Contours variable: %s" % self.contourvar) + LOG.info("Contours variable: %s" % self.contourvar) srcfile = self.ncdf[0][0] srcvar = srcfile.variables[self.contourvar] if self.dimension: srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - cdata = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + cdata = srcvar[tstep, :, :][self.grid_idxs[1], + :][:, self.grid_idxs[0]] else: cdata = srcvar[tstep, :, :] return cdata def get_scatter_data_for_tstep(self, tstep): """ """ - log.info("Scatter variable: %s" % self.scatter) + LOG.info("Scatter variable: %s" % self.scatter) srcfile = self.ncdf[0][0] srcvar = srcfile.variables[self.contourvar] if self.dimension: srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: - cdata = srcvar[tstep, :, :][self.grid_idxs[1], :][:, self.grid_idxs[0]] + cdata = srcvar[tstep, :, :][self.grid_idxs[1], + :][:, self.grid_idxs[0]] else: cdata = srcvar[tstep, :, :] return cdata def get_dims(self): - log.info("Getting dimensions from file '%s'" % self.srcs[0]) - #self.ncdf.cmd("set dfile 1") + LOG.info("Getting dimensions from file '%s'" % self.srcs[0]) + # self.ncdf.cmd("set dfile 1") for vname in self.ncdf[0][0].variables: if vname in ('time', 'time_counter'): tvar = vname @@ -535,7 +538,8 @@ class MapDataHandler(object): tim = tim.split('.')[0] tfmt = '%HZ%d%b%Y' try: - dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M:%S') + dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d + %H:%M:%S') except: dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M') if typ == 'minutes': @@ -546,7 +550,7 @@ class MapDataHandler(object): cdate = {(np.where(tdata == i)[0][0], (dtime + timedelta(seconds=int(i))).strftime(tfmt)) for i in tdata} - else: #typ == 'hours': + else: # typ == 'hours': cdate = {(np.where(tdata == i)[0][0], (dtime + timedelta(hours=int(i))).strftime(tfmt)) for i in tdata} @@ -558,7 +562,7 @@ class DataFrameHandler(object): def __init__(self, dframe, separator=',', ): if isinstance(dframe, str): - log.info("opening '%s'" % dframe) + LOG.info("opening '%s'" % dframe) self.reader = pd.read_csv(dframe, sep=separator, parse_dates=True) else: self.reader = dframe @@ -566,10 +570,10 @@ class DataFrameHandler(object): def filter(self, **kwargs): """ Filtering dataframes """ + latcol = 'latcol' in kwargs and kwargs['latcol'] or 'lat' + loncol = 'loncol' in kwargs and kwargs['loncol'] or 'lon' + colcol = 'colcol' in kwargs and kwargs['colcol'] or 'color' datecol = 'datecol' in kwargs and kwargs['datecol'] or 'date' - latcol = 'latcol' in kwargs and kwargs['latcol'] or 'lat' - loncol = 'loncol' in kwargs and kwargs['loncol'] or 'lon' - colcol = 'colcol' in kwargs and kwargs['colcol'] or 'color' sizecol = 'sizecol' in kwargs and kwargs['sizecol'] or 'size' ret = {} @@ -587,7 +591,7 @@ class DataFrameHandler(object): group = gdf.get_group(gname) dtime = datetime.strptime(gname, "%Y-%m-%d %H:%M:00") ret[dtime] = group[[loncol, latcol, colcol, sizecol]] - #log.info('filtering on date: %s and hour %s' % (date, hour)) + #LOG.info('filtering on date: %s and hour %s' % (date, hour)) return ret @@ -602,9 +606,9 @@ class DataTransform(object): Colors and bounds must have the same size''' - log.info('bounds: %s' % bounds) - log.info('colors: %s' % colors) - log.info('values: %s' % values) + LOG.info('bounds: %s' % bounds) + LOG.info('colors: %s' % colors) + LOG.info('values: %s' % values) if len(colors) != len(bounds) + 1: raise Exception("Wrong size for colors/bounds") @@ -612,17 +616,18 @@ class DataTransform(object): out = [] for idx in range(0, len(values)): - log.info("processing %s" % str(values[idx])) + LOG.info("processing %s" % str(values[idx])) if values[idx] < bounds[0]: out.append(colors[0]) elif values[idx] >= bounds[len(bounds) - 1]: out.append(colors[len(colors) - 1]) else: for bidx in range(0, len(bounds) - 1): - if values[idx] >= bounds[bidx] and values[idx] < bounds[bidx + 1]: + if values[idx] >= bounds[bidx] and \ + values[idx] < bounds[bidx + 1]: out.append(colors[bidx + 1]) - log.info("data transform output: %s" % str(out)) + LOG.info("data transform output: %s" % str(out)) return out @@ -630,7 +635,7 @@ class MapDrawOptions(object): """ Map draw options """ def __init__(self, loglevel='INFO', **kwargs): - log.setLevel(loglevel) + LOG.setLevel(loglevel) ## General Options self.area_thresh = None # Default area threshold for maps self.resolution = None # Default resolution is intermediate @@ -657,29 +662,29 @@ class MapDrawOptions(object): self.wind_head_width = 8 # Wind arrowhead width default self.wind_width = 0.008 # Wind arrow width default self.wind_minshaft = 0.25 # Wind arrow minimum shaft default - self.wind_label_xpos = 0.8 # Wind label x position (relative to quiver plot) - self.wind_label_ypos = -0.075 # Wind label y position (relative to quiver plot) - self.wind_label_scale = 20 # Size of the label (represents m/s) + self.wind_label_xpos = 0.8 # Wind label x position (relative to quiver plot) + self.wind_label_ypos = -0.075 # Wind label y position (relative to quiver plot) + self.wind_label_scale = 20 # Size of the label (represents m/s) - ## CONTOUR OPTIONS + # CONTOUR OPTIONS self.contours_int = 1 # Default contour interval - self.contours_color = 'k' # Default contour color + self.contours_color = 'k' # Default contour color self.contours_linewidth = 0.8 # Default line width for contours - self.contours_label = False # if True labels of contours are showed - self.contours_label_fontsize = 13 # Default font size for contour label - self.contours_label_format = '%d' # Default format for contour label + self.contours_label = False # if True labels of contours are showed + self.contours_label_fontsize = 13 # Default font size for contour label + self.contours_label_format = '%d' # Default format for contour label self.contours_exclude_vals = [0] # Default list of values to exclude from drawing # self.nocontourf = False # if True contourf is hidden # self.nomap = False # if True map is hidden # self.nointerp = False # if False use contourf, else pcolor - ## SHAPEFILES + # SHAPEFILES self.shapefiles = [] # Default shapefiles list is empty self.shapef_width_init = 0.75 # Initial line width for shapefiles self.shapef_width_step = 0.25 # Line width difference between two consecutive shapefiles - log.setLevel(loglevel) + LOG.setLevel(loglevel) def has_shape_files(self): return (len(self.shapefiles) > 0) @@ -743,7 +748,7 @@ class MapCross(MapGenerator): def __init__(self, loglevel='INFO', **kwargs): """ Initialize class with MapGenerator attributes plus some others """ - log.setLevel(loglevel) + LOG.setLevel(loglevel) MapGenerator.__init__(self, **kwargs) self.bounds = kwargs.get('bounds', None) self.ticks = kwargs.get('ticks', None) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 049003c..4fb3714 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -29,6 +29,10 @@ from .definitions import parse_parameter from .definitions import parse_parameters from .definitions import parse_parameters_list from .definitions import parse_path +from .tools import set_title +from .tools import set_resolution +from .tools import gen_kml +from .tools import run_command # try: # from urllib2 import urlopen # except ImportError: @@ -90,23 +94,6 @@ class PlotMap(MapCross, MapDrawOptions): def _build_projection(self): self._crs = getattr(ccrs, self.projection)(**self.projection_kwargs) - def set_resolution(self): - """ 110m, 50m, 10m """ - # lats = 36, 72, 108, 144, 180 - # lons = 72, 144, 216, 288, 360 - minlat, maxlat = self.lat[0], self.lat[-1] - minlon, maxlon = self.lon[0], self.lon[-1] - lats = maxlat + abs(minlat) - lons = maxlon + abs(minlon) - if lats > 100 and lons > 200: - self.resolution = '110m' - elif lats > 45 and lons > 75: - self.resolution = '50m' - elif lats <= 45 and lons <= 75: - self.resolution = '10m' - else: - self.resolution = '110m' # default - def set_color_map(self): """ Create color map """ @@ -114,17 +101,22 @@ class PlotMap(MapCross, MapDrawOptions): 'max': { 'cmap_arg': self.colors[:-1], 'cmap_ext': ('set_over'), - 'ext_arg': (self.colors[-1]) + 'ext_arg': (self.colors[-1]), + 'norm_arg': (self.bounds+[np.inf], self.cmap.N+1) }, 'min': { 'cmap_arg': self.colors[1:], 'cmap_ext': ('set_under'), - 'ext_arg': (self.colors[0]) + 'ext_arg': (self.colors[0]), + 'norm_arg': ([-np.inf]+self.bounds, self.cmap.N+1) }, 'both': { 'cmap_arg': self.colors[1:-1], 'cmap_ext': ('set_over', 'set_under'), - 'ext_arg': (self.colors[-1], self.colors[0]) + 'ext_arg': (self.colors[-1], self.colors[0]), + 'norm_arg': ([-np.inf] + self.bounds + [np.inf], + self.cmap.N + 2) + } } @@ -170,15 +162,9 @@ class PlotMap(MapCross, MapDrawOptions): # normalize colormap if self.bounds and self.smooth and not custom_cmap: - if self.extend == 'min': - self.norm = mpl.colors.BoundaryNorm([-np.inf]+self.bounds, - self.cmap.N+1) - elif self.extend == 'max': - self.norm = mpl.colors.BoundaryNorm(self.bounds+[np.inf], - self.cmap.N+1) - elif self.extend == 'both': - self.norm = mpl.colors.BoundaryNorm([-np.inf] + self.bounds + - [np.inf], self.cmap.N + 2) + if self.extend in ('min', 'max', 'both'): + self.norm = mpl.colors.BoundaryNorm( + *extend_opts[self.extend]['norm_arg']) else: self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) elif self.bounds: @@ -243,68 +229,12 @@ class PlotMap(MapCross, MapDrawOptions): head_width=0.025, fill=True, color='k') # plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) - def set_title(self, title, sdate, cdate, step, stime): - """ Set the title of the current image to the one provided, substituting the - patterns """ - - title_dic = { - 'syear': sdate.strftime("%Y"), - 'smonth': sdate.strftime("%m"), - 'sMONTH': sdate.strftime("%b"), - 'sday': sdate.strftime("%d"), - 'shour': sdate.strftime("%H"), - 'year': cdate.strftime("%Y"), - 'month': cdate.strftime("%m"), - 'MONTH': cdate.strftime("%b"), - 'day': cdate.strftime("%d"), - 'hour': cdate.strftime("%H"), - 'step': step, - 'simday': "%d" % (int(stime)/24), - 'simhh': stime, - # 'simhh': "%02d" % (int(stime)%24) - } - - try: - p_title = str(title) % title_dic # ("%sh" % s_time, valid) - except Exception as e: - LOG.error("Title error: %s", str(e)) - p_title = title % title_dic # ("%sh" % s_time, valid) - - return p_title - - def print_time(self, msg=None): - """ Print time """ - # initial time - if 'start_t' not in globals(): - global start_t, last_t - LOG.info("Creating start_t") - start_t = datetime.now() - last_t = start_t - - now_t = datetime.now() - diff_t = now_t - last_t - if msg is not None: - LOG.info('TIME: %s done in %s s', msg, diff_t.seconds) - last_t = now_t - - def run_command(self, comm_str): # , fatal=True): - """ Run command """ - LOG.info(comm_str) - proc = subprocess.Popen(comm_str.split(), stderr=subprocess.PIPE) - output, err = proc.communicate() - LOG.info(output, '-----', err) -# st, out = commands.getstatusoutput(comm_str) -# if st != 0: -# print "Error: %s" % str(out) -# if (fatal == True): -# sys.exit(1) - def gen_anim(self, inpattern, outfile): ''' Generate an animation starting from a set of images, specified by the inpattern parameter ''' # print "Animation: ", indir, inpattern, outdir, outfile # Create animation - self.run_command( + run_command( "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" % (self.anim_delay, self.outdir, inpattern, self.outdir, outfile)) # Remove intermediate files @@ -351,7 +281,7 @@ class PlotMap(MapCross, MapDrawOptions): # Set the colormap self.set_color_map() if not self.resolution: - self.set_resolution() + self.resolution = set_resolution(self.lon, self.lat) self.first_image = True @@ -835,112 +765,6 @@ class PlotMap(MapCross, MapDrawOptions): if status != 0: LOG.error("Error: %s", str(out)) - def gen_kml(self, fig_names, srcvars, dims, online=True): - """ """ - from lxml import etree -# from pykml.parser import Schema - from pykml.factory import KML_ElementMaker as KML -# from datetime import datetime, timedelta - import zipfile - - lon = self.lon - lat = self.lat - - LOG.info(fig_names) - # kml_name = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) - kml_name = os.path.basename(fig_names[0])[:-3] - var_name = srcvars[0] # fig_names[0].split('-')[-2] - run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") - run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") - begindate = run_date0.strftime("%Y%m%d%H") - dir_name = os.path.join(self.outdir, begindate + '-' + - var_name).replace('./', '') - interval = run_date1 - run_date0 - datefmt = "%Y-%m-%dT%H:00:00Z" - - doc = KML.kml( - KML.Folder( - KML.name(kml_name), - KML.LookAt( - KML.longitude((lon[1]+lon[-1])/2), - KML.latitude((lat[1]+lat[-1])/2), - KML.range(8500000.0), - KML.tilt(0), - KML.heading(0), - ), - KML.ScreenOverlay( - KML.name("Legend"), - KML.open(1), - KML.Icon( - KML.href("%s/%s-colorbar.png" % (dir_name, begindate)), - ), - KML.overlayXY(x="0", y="1", xunits="fraction", - yunits="fraction"), - KML.screenXY(x=".01", y="0.55", xunits="fraction", - yunits="fraction"), - KML.rotationXY(x="0", y="0", xunits="fraction", - yunits="fraction"), - KML.size(x="0", y="0.5", - xunits="fraction", - yunits="fraction"), - id="%s-ScreenOverlay" % begindate, - ), - ) - ) - - os.makedirs(dir_name) - - if not online: - zfile = zipfile.ZipFile("%s.kmz" % kml_name, 'w') - - for fig_name, dat in zip(fig_names, sorted(dims.keys())): - dtime = dims[dat] - fig_name = fig_name + ".png" - fig_path = os.path.join(dir_name, os.path.basename(fig_name)) - startdate = datetime.strptime(dtime, "%HZ%d%b%Y") - begdate = startdate.strftime(datefmt) - enddate = (startdate + interval).strftime(datefmt) - starth = startdate.hour - doc.Folder.append(KML.GroundOverlay( - KML.name("%02d:00:00Z" % starth), - KML.TimeSpan( - KML.begin(begdate), - KML.end(enddate), - ), - KML.Icon( - KML.href(fig_path), - KML.viewBoundScale(1.0), - ), - KML.altitude(0.0), - KML.altitudeMode("relativeToGround"), - KML.LatLonBox( - KML.south(lat[0]), - KML.north(lat[-1]), - KML.west(lon[0]), - KML.east(lon[-1]), - KML.rotation(0.0), - ), - )) - - if os.path.exists(fig_name): - os.rename(fig_name, fig_path) - zfile.write(fig_path) - - outf = open("%s.kml" % kml_name, 'w') - outf.write('\n') - outf.write(etree.tostring(doc, pretty_print=True)) - outf.close() - - if not online: - zfile.write("%s.kml" % kml_name) - zfile.write("%s/%s-colorbar.png" % (dir_name, begindate)) - zfile.close() - - for img in os.listdir(dir_name): - os.remove("%s/%s" % (dir_name, img)) - os.rmdir(dir_name) - os.remove("%s.kml" % kml_name) - def init_environ(self, **kwargs): """ initialize environment with all arguments """ @@ -1174,8 +998,7 @@ class PlotMap(MapCross, MapDrawOptions): # stime = "%02d" % (int(run_tmp.split('Z')[0])+p_time) stime = "%02d" % (p_time) - p_title = self.set_title(self.title, s_date, curr_date, s_time, - stime) + p_title = set_title(self.title, s_date, curr_date, s_time, stime) cur_scatter_data = None if scatter_data is not None and (curr_date in scatter_data): cur_scatter_data = scatter_data[curr_date] @@ -1195,8 +1018,8 @@ class PlotMap(MapCross, MapDrawOptions): if self.maxdata and n_time in self.maxdata: if self.maxtitle is not None: fig_name = "%s_%s" % (f_name, 'MAXD%d' % (n_time/24)) - p_title = self.set_title(self.maxtitle, s_date, curr_date, - s_time, stime) + p_title = set_title(self.maxtitle, s_date, curr_date, + s_time, stime) self.gen_image_map( fig_name, grid, @@ -1266,12 +1089,14 @@ class PlotMap(MapCross, MapDrawOptions): # generate KMZ - Offline if self.kmz: LOG.info("Generating KMZ ...") - self.gen_kml(fig_names, self.srcvars, dims, online=False) + gen_kml(fig_names, self.srcvars[0], self.lon, self.lat, dims, + self.outdir, online=False) # generate KML - Online if self.kml: LOG.info("Generating KML ...") - self.gen_kml(fig_names, self.srcvars, dims) + gen_kml(fig_names, self.srcvars[0], self.lon, self.lat, dims, + self.outdir) # generate animation. if self.anim: -- GitLab From f182bef0b97535b7c3d303093d2d727573f9a2bc Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 3 Nov 2020 14:23:22 +0100 Subject: [PATCH 22/27] Fixed syntax error --- mapgenerator/plotting/definitions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index c218f09..f5b8ae0 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -538,8 +538,7 @@ class MapDataHandler(object): tim = tim.split('.')[0] tfmt = '%HZ%d%b%Y' try: - dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d - %H:%M:%S') + dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M:%S') except: dtime = datetime.strptime("%s %s" % (dat, tim), '%Y-%m-%d %H:%M') if typ == 'minutes': -- GitLab From 9f6520f338d1dd7236b7eb3ca7e17f5637a68477 Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 3 Nov 2020 16:49:27 +0100 Subject: [PATCH 23/27] Added tools module with functions from plotmap --- mapgenerator/plotting/plotmap.py | 32 +---- mapgenerator/plotting/tools.py | 210 +++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 27 deletions(-) create mode 100644 mapgenerator/plotting/tools.py diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 4fb3714..80b9191 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -32,7 +32,7 @@ from .definitions import parse_path from .tools import set_title from .tools import set_resolution from .tools import gen_kml -from .tools import run_command +from .tools import gen_anim # try: # from urllib2 import urlopen # except ImportError: @@ -229,30 +229,6 @@ class PlotMap(MapCross, MapDrawOptions): head_width=0.025, fill=True, color='k') # plt.colorbar(drawedges=drawedges, cax=cax, ticks=self.bounds) - def gen_anim(self, inpattern, outfile): - ''' Generate an animation starting from a set of - images, specified by the inpattern parameter ''' - # print "Animation: ", indir, inpattern, outdir, outfile - # Create animation - run_command( - "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" - % (self.anim_delay, self.outdir, inpattern, self.outdir, outfile)) - # Remove intermediate files - # self.run_command("rm %s/%s %s" % (indir, inpattern, self.map_name), - # False) - -# ccc = [] -# ccc.extend(self.colors) -# print "Filtering scatter data for date/hour: ", -# curr_date.strftime("%Y%m%d"), s_time24 csv_lat, csv_lon, csv_val = -# csv_handler.filter(curr_date.strftime("%Y%m%d"), s_time24) -# #csv_col = self.dataTransform.setColorsFromBuckets(ccc, -# self.bounds, csv_con) -# if(len(csv_lat) > 0): -# scatter_data = [csv_lon, csv_lat, 40, csv_col] -# print "**************** SCATTER_DATA", scatter_data -# print "Filtered %d records for scatterplot" % len(scatter_data) - def init_map(self, grid): """ Initialize a map. Initialization should be performed only once the beginning of a serie of images """ @@ -1100,9 +1076,11 @@ class PlotMap(MapCross, MapDrawOptions): # generate animation. if self.anim: - self.gen_anim( + gen_anim( "%s.%s" % (loop_name.replace('loop', '*'), self.filefmt), - "%s.gif" % loop_name + "%s.gif" % loop_name, + self.outdir, + self.anim_delay ) # fulloutdir = os.path.join(self.outdir, # os.path.dirname(self.img_template)) diff --git a/mapgenerator/plotting/tools.py b/mapgenerator/plotting/tools.py new file mode 100644 index 0000000..a788d3f --- /dev/null +++ b/mapgenerator/plotting/tools.py @@ -0,0 +1,210 @@ +from datetime import datetime +import subprocess +import os +import logging + +LOG = logging.getLogger(__name__) + + +def print_time(msg=None): + """ Print time """ + # initial time + if 'start_t' not in globals(): + global start_t, last_t + LOG.info("Creating start_t") + start_t = datetime.now() + last_t = start_t + + now_t = datetime.now() + diff_t = now_t - last_t + if msg is not None: + LOG.info('TIME: %s done in %s s', msg, diff_t.seconds) + last_t = now_t + + +def run_command(comm_str): # , fatal=True): + """ Run command """ + LOG.info(comm_str) + proc = subprocess.Popen(comm_str.split(), stderr=subprocess.PIPE) + output, err = proc.communicate() + LOG.info(output, '-----', err) +# st, out = commands.getstatusoutput(comm_str) +# if st != 0: +# print "Error: %s" % str(out) +# if (fatal == True): +# sys.exit(1) + + +def gen_anim(inpattern, outfile, outdir, anim_delay): + ''' Generate an animation starting from a set of + images, specified by the inpattern parameter ''' + # print "Animation: ", indir, inpattern, outdir, outfile + # Create animation + run_command( + "/usr/bin/convert -delay %s -loop 0 -layers Optimize %s/%s %s/%s" + % (anim_delay, outdir, inpattern, outdir, outfile)) + # Remove intermediate files + # run_command("rm %s/%s %s" % (indir, inpattern, map_name), + # False) + +# ccc = [] +# ccc.extend(colors) +# print "Filtering scatter data for date/hour: ", +# curr_date.strftime("%Y%m%d"), s_time24 csv_lat, csv_lon, csv_val = +# csv_handler.filter(curr_date.strftime("%Y%m%d"), s_time24) +# #csv_col = dataTransform.setColorsFromBuckets(ccc, +# bounds, csv_con) +# if(len(csv_lat) > 0): +# scatter_data = [csv_lon, csv_lat, 40, csv_col] +# print "**************** SCATTER_DATA", scatter_data +# print "Filtered %d records for scatterplot" % len(scatter_data) + + +def set_resolution(lon, lat): + """ 110m, 50m, 10m """ + # lats = 36, 72, 108, 144, 180 + # lons = 72, 144, 216, 288, 360 + minlat, maxlat = lat[0], lat[-1] + minlon, maxlon = lon[0], lon[-1] + lats = maxlat + abs(minlat) + lons = maxlon + abs(minlon) + if lats > 100 and lons > 200: + resolution = '110m' + elif lats > 45 and lons > 75: + resolution = '50m' + elif lats <= 45 and lons <= 75: + resolution = '10m' + else: + resolution = '110m' # default + return resolution + + +def set_title(title, sdate, cdate, step, stime): + """ Set the title of the current image to the one provided, substituting the + patterns """ + + title_dic = { + 'syear': sdate.strftime("%Y"), + 'smonth': sdate.strftime("%m"), + 'sMONTH': sdate.strftime("%b"), + 'sday': sdate.strftime("%d"), + 'shour': sdate.strftime("%H"), + 'year': cdate.strftime("%Y"), + 'month': cdate.strftime("%m"), + 'MONTH': cdate.strftime("%b"), + 'day': cdate.strftime("%d"), + 'hour': cdate.strftime("%H"), + 'step': step, + 'simday': "%d" % (int(stime)/24), + 'simhh': stime, + # 'simhh': "%02d" % (int(stime)%24) + } + + try: + p_title = str(title) % title_dic # ("%sh" % s_time, valid) + except Exception as err: + LOG.error("Title error: %s", str(err)) + p_title = title % title_dic # ("%sh" % s_time, valid) + + return p_title + + +def gen_kml(fig_names, var_name, lon, lat, dims, outdir='.', online=True): + """ Generate KML/KMZ files """ + from lxml import etree + from pykml.factory import KML_ElementMaker as KML + import zipfile + + LOG.info(fig_names) + # kml_name = '-'.join(os.path.basename(fig_names[0]).split('-')[:-1]) + kml_name = os.path.basename(fig_names[0])[:-3] + run_date0 = datetime.strptime(dims[0], "%HZ%d%b%Y") + run_date1 = datetime.strptime(dims[1], "%HZ%d%b%Y") + begindate = run_date0.strftime("%Y%m%d%H") + dir_name = os.path.join(outdir, begindate + '-' + + var_name).replace('./', '') + interval = run_date1 - run_date0 + datefmt = "%Y-%m-%dT%H:00:00Z" + + doc = KML.kml( + KML.Folder( + KML.name(kml_name), + KML.LookAt( + KML.longitude((lon[1]+lon[-1])/2), + KML.latitude((lat[1]+lat[-1])/2), + KML.range(8500000.0), + KML.tilt(0), + KML.heading(0), + ), + KML.ScreenOverlay( + KML.name("Legend"), + KML.open(1), + KML.Icon( + KML.href("%s/%s-colorbar.png" % (dir_name, begindate)), + ), + KML.overlayXY(x="0", y="1", xunits="fraction", + yunits="fraction"), + KML.screenXY(x=".01", y="0.55", xunits="fraction", + yunits="fraction"), + KML.rotationXY(x="0", y="0", xunits="fraction", + yunits="fraction"), + KML.size(x="0", y="0.5", + xunits="fraction", + yunits="fraction"), + id="%s-ScreenOverlay" % begindate, + ), + ) + ) + + os.makedirs(dir_name) + + if not online: + zfile = zipfile.ZipFile("%s.kmz" % kml_name, 'w') + + for fig_name, dat in zip(fig_names, sorted(dims.keys())): + dtime = dims[dat] + fig_name = fig_name + ".png" + fig_path = os.path.join(dir_name, os.path.basename(fig_name)) + startdate = datetime.strptime(dtime, "%HZ%d%b%Y") + begdate = startdate.strftime(datefmt) + enddate = (startdate + interval).strftime(datefmt) + starth = startdate.hour + doc.Folder.append(KML.GroundOverlay( + KML.name("%02d:00:00Z" % starth), + KML.TimeSpan( + KML.begin(begdate), + KML.end(enddate), + ), + KML.Icon( + KML.href(fig_path), + KML.viewBoundScale(1.0), + ), + KML.altitude(0.0), + KML.altitudeMode("relativeToGround"), + KML.LatLonBox( + KML.south(lat[0]), + KML.north(lat[-1]), + KML.west(lon[0]), + KML.east(lon[-1]), + KML.rotation(0.0), + ), + )) + + if os.path.exists(fig_name): + os.rename(fig_name, fig_path) + zfile.write(fig_path) + + outf = open("%s.kml" % kml_name, 'w') + outf.write('\n') + outf.write(etree.tostring(doc, pretty_print=True)) + outf.close() + + if not online: + zfile.write("%s.kml" % kml_name) + zfile.write("%s/%s-colorbar.png" % (dir_name, begindate)) + zfile.close() + + for img in os.listdir(dir_name): + os.remove("%s/%s" % (dir_name, img)) + os.rmdir(dir_name) + os.remove("%s.kml" % kml_name) -- GitLab From 4897e2cbf7de7f8a80719dc2b9b54b48f62f9423 Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 3 Nov 2020 21:01:17 +0100 Subject: [PATCH 24/27] Fixed problem on colorbar --- mapgenerator/plotting/definitions.py | 2 +- mapgenerator/plotting/plotmap.py | 31 ++++++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index f5b8ae0..8b5cd6c 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -40,7 +40,7 @@ def is_grid_regular(lon, lat): is_x_reg = np.array([(np.spacing(lon[x]) == np.spacing(lon[x+1])).all() for x in np.arange(lon.shape[0]-1)]).all() - LOG(is_x_reg) + LOG.debug(is_x_reg) if not is_x_reg: return is_x_reg diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 80b9191..627db7b 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -99,23 +99,26 @@ class PlotMap(MapCross, MapDrawOptions): extend_opts = { 'max': { - 'cmap_arg': self.colors[:-1], + 'cmap_arg': lambda colors: colors[:-1], 'cmap_ext': ('set_over'), - 'ext_arg': (self.colors[-1]), - 'norm_arg': (self.bounds+[np.inf], self.cmap.N+1) + 'ext_arg': (lambda colors: colors[-1]), + 'norm_arg': (lambda bounds: bounds+[np.inf], + lambda cmap: cmap.N+1) }, 'min': { - 'cmap_arg': self.colors[1:], + 'cmap_arg': lambda colors: colors[1:], 'cmap_ext': ('set_under'), - 'ext_arg': (self.colors[0]), - 'norm_arg': ([-np.inf]+self.bounds, self.cmap.N+1) + 'ext_arg': (lambda colors: colors[0]), + 'norm_arg': (lambda bounds: [-np.inf]+bounds, + lambda cmap: cmap.N+1) }, 'both': { 'cmap_arg': self.colors[1:-1], 'cmap_ext': ('set_over', 'set_under'), - 'ext_arg': (self.colors[-1], self.colors[0]), - 'norm_arg': ([-np.inf] + self.bounds + [np.inf], - self.cmap.N + 2) + 'ext_arg': (lambda colors: colors[-1], + lambda colors: colors[0]), + 'norm_arg': (lambda bounds: [-np.inf]+bounds+[np.inf], + lambda cmap: cmap.N + 2) } } @@ -133,10 +136,10 @@ class PlotMap(MapCross, MapDrawOptions): if self.extend in extend_opts: self.cmap = mpl.colors.ListedColormap( - extend_opts[self.extend]['cmap_arg']) + extend_opts[self.extend]['cmap_arg'](self.colors)) for ext, arg in zip(extend_opts[self.extend]['cmap_ext'], extend_opts[self.extend]['ext_arg']): - getattr(self.cmap, ext)(arg) + getattr(self.cmap, ext)(arg(self.colors)) else: self.cmap = mpl.colors.ListedColormap(self.colors) custom_cmap = True @@ -164,7 +167,9 @@ class PlotMap(MapCross, MapDrawOptions): if self.bounds and self.smooth and not custom_cmap: if self.extend in ('min', 'max', 'both'): self.norm = mpl.colors.BoundaryNorm( - *extend_opts[self.extend]['norm_arg']) + extend_opts[self.extend]['norm_arg'][0](self.bounds), + extend_opts[self.extend]['norm_arg'][1](self.cmap) + ) else: self.norm = mpl.colors.BoundaryNorm(self.bounds, self.cmap.N) elif self.bounds: @@ -183,7 +188,7 @@ class PlotMap(MapCross, MapDrawOptions): self.ticks = parse_parameters_list(self.ticks) elif self.bounds: self.ticks = self.bounds - elif mco: + elif hasattr(mco, 'levels'): self.ticks = mco.levels LOG.debug("***** Formats: %s *****", self.formats) -- GitLab From 4d88696623f6b6a7c9dbaa42a4cfded8bd77b98c Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 4 Nov 2020 21:50:55 +0100 Subject: [PATCH 25/27] Added backgrounds --- mapgenerator/plotting/config.py | 17 +++--- mapgenerator/plotting/definitions.py | 18 ++++--- mapgenerator/plotting/plotmap.py | 79 +++++++++++++++++----------- mapgenerator/plotting/tools.py | 5 +- 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/mapgenerator/plotting/config.py b/mapgenerator/plotting/config.py index f7556cd..c18887a 100644 --- a/mapgenerator/plotting/config.py +++ b/mapgenerator/plotting/config.py @@ -13,7 +13,7 @@ from mapgenerator import mg_exceptions import logging -logging.basicConfig(level=logging.INFO) +logging.basicConfig(level=logging.WARNING) log = logging.getLogger(__name__) @@ -64,7 +64,7 @@ class ArgumentParser(object): 'WARNING', 'ERROR', 'CRITICAL'], - help="logging levelbug (default=INFO)") + help="logging levelbug (default=WARNING)") self.parser.add_argument("--indir", dest="indir", @@ -204,13 +204,12 @@ class ArgumentParser(object): self.parser.add_argument("--nomap", dest="nomap", help="hide map") -# self.parser.add_argument("--kml", -# dest="kml", -# help="generate KML. String with base url -# (default='')") -# self.parser.add_argument("--kmz", -# dest="kmz", -# help="generate KMZ (default=False)") + self.parser.add_argument("--kml", + dest="kml", + help="generate KML (default=False)") + self.parser.add_argument("--kmz", + dest="kmz", + help="generate KMZ (default=False)") self.parser.add_argument("--overwrite", dest="overwrite", help="overwrite outputs (default=True)") diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 8b5cd6c..3ffb5d3 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -152,7 +152,7 @@ def parse_parameters_list(plist): def parse_parameter(var, val): """ Parameter parser """ # parse numeric list arguments - if var in ('lon', 'lat', 'timesteps', 'bounds'): + if var in ('lon', 'lat', 'timesteps', 'bounds', 'ticks'): if var == 'timesteps' and val in ('all', ['all']): return 'all' if val is not None: @@ -404,7 +404,8 @@ class MapDataHandler(object): """ Get map data for tstep """ map_data = [] for src_n in range(len(self.srcs)): - tstep = step + int(self.srcgaps[src_n]) + tstep = int(step + self.srcgaps[src_n]) + LOG.info("TSTEP: %s", tstep) srcfile = self.ncdf[0][src_n] # TODO zoom on lat lon according to the domain # FIXME variable operations @@ -451,7 +452,9 @@ class MapDataHandler(object): for vname in srcfile.variables: if vname in expvar: expvar = expvar.replace(vname, 'newvar') + LOG.info("EXPVAR: %s", expvar) srcvar = srcfile.variables[vname] + LOG.info("SRCVAR: %s", srcvar) if self.dimension: srcvar = self.dim_operation(srcvar, srcfile) if self.grid_idxs: @@ -561,7 +564,7 @@ class DataFrameHandler(object): def __init__(self, dframe, separator=',', ): if isinstance(dframe, str): - LOG.info("opening '%s'" % dframe) + LOG.debug("opening '%s'" % dframe) self.reader = pd.read_csv(dframe, sep=separator, parse_dates=True) else: self.reader = dframe @@ -588,9 +591,12 @@ class DataFrameHandler(object): gdf = self.reader.groupby(datecol) for gname in gdf.groups: group = gdf.get_group(gname) - dtime = datetime.strptime(gname, "%Y-%m-%d %H:%M:00") + if isinstance(gname, str): + dtime = datetime.strptime(gname, "%Y-%m-%d %H:%M:00") + else: + dtime = gname.to_pydatetime() ret[dtime] = group[[loncol, latcol, colcol, sizecol]] - #LOG.info('filtering on date: %s and hour %s' % (date, hour)) + # LOG.info('filtering on date: %s and hour %s', date, hour) return ret @@ -704,7 +710,7 @@ class MapGenerator(object): self.outdir = kwargs.get('outdir', '.') self.srcgaps = kwargs.get('srcgaps', [0]) self.title = kwargs.get('title', '') - self.fontsize = kwargs.get('fontsize', 12) + self.fontsize = kwargs.get('fontsize', 10) self.orig_projection = kwargs.get('orig_projection', 'PlateCarree') self.orig_projection_kwargs = kwargs.get('orig_projection_kwargs', dict()) self.projection = kwargs.get('projection', 'PlateCarree') diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index 627db7b..d5a308c 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -48,14 +48,14 @@ LOG = logging.getLogger(__name__) class PlotCross(object): """ Main class for plotting cross sections """ - def __init__(self, loglevel='INFO', **kwargs): + def __init__(self, loglevel='WARNING', **kwargs): pass class PlotMap(MapCross, MapDrawOptions): """ Main class for plotting maps """ - def __init__(self, loglevel='INFO', **kwargs): + def __init__(self, loglevel='WARNING', **kwargs): """ Initialize class with attributes """ MapCross.__init__(self, loglevel, **kwargs) MapDrawOptions.__init__(self, loglevel, **kwargs) @@ -73,6 +73,8 @@ class PlotMap(MapCross, MapDrawOptions): self._crs = ccrs.PlateCarree() self.mgaxis = None self.mgplot = None + self.stamen_terrain = None + self.zoom_level = None # self.map = None #Map self.first_image = False # Is is the first image of the batch? self.map_name = None # name of the figure to map animation colors @@ -81,7 +83,6 @@ class PlotMap(MapCross, MapDrawOptions): # 'countries': # {'linewidth': 0.3, 'color': 'grey', }, # } - LOG.setLevel(loglevel) def __setattr__(self, key, value): LOG.debug("SETATTR: %s - %s", key, value) @@ -92,7 +93,14 @@ class PlotMap(MapCross, MapDrawOptions): **self.orig_projection_kwargs) def _build_projection(self): - self._crs = getattr(ccrs, self.projection)(**self.projection_kwargs) + if self.background: + import cartopy.io.img_tiles as cimgt + self.stamen_terrain = cimgt.Stamen('terrain-background') + self._crs = self.stamen_terrain.crs + self.projection = 'Stamen' + else: + self._crs = getattr(ccrs, self.projection)( + **self.projection_kwargs) def set_color_map(self): """ Create color map """ @@ -262,7 +270,10 @@ class PlotMap(MapCross, MapDrawOptions): # Set the colormap self.set_color_map() if not self.resolution: - self.resolution = set_resolution(self.lon, self.lat) + self.resolution, self.zoom_level = \ + set_resolution(self.lon, self.lat) + else: + _, self.zoom_level = set_resolution(self.lon, self.lat) self.first_image = True @@ -298,7 +309,7 @@ class PlotMap(MapCross, MapDrawOptions): if self.projection.lower().endswith('polarstereo'): self.mgaxis.set_extent([-180, 179.9999999999999, self.lat[0], self.lat[-1]], - self._crs) + self._orig_crs) # Compute a circle in axes coordinates, which we can use as a # boundary for the map. We can pan/zoom as much as we like - the # boundary will be permanently circular. @@ -310,7 +321,7 @@ class PlotMap(MapCross, MapDrawOptions): else: self.mgaxis.set_extent([self.lon[0], self.lon[-1], self.lat[0], self.lat[-1]], - self._crs) + self._orig_crs) # if self.subplot: # self.map = Basemap( @@ -369,16 +380,16 @@ class PlotMap(MapCross, MapDrawOptions): if self.nomap: self.mgplot.frameon = False - if self.alpha is not None and self.bounds is not None: - if self.extend in ('min', 'both'): - map_data[0] = np.ma.masked_where(map_data[0] < self.bounds[0], - map_data[0]) - else: - map_data[0] = np.ma.masked_where((map_data[0] >= - self.bounds[0]) & - (map_data[0] < - self.bounds[1]), - map_data[0]) +# if self.alpha is not None and self.bounds is not None: +# if self.extend in ('min', 'both'): +# map_data[0] = np.ma.masked_where(map_data[0] < +# self.bounds[0], map_data[0]) +# else: +# map_data[0] = np.ma.masked_where((map_data[0] >= +# self.bounds[0]) & +# (map_data[0] < +# self.bounds[1]), +# map_data[0]) mco = None @@ -417,7 +428,7 @@ class PlotMap(MapCross, MapDrawOptions): levels=self.bounds, extend=self.extend, alpha=self.alpha, - transform=ccrs.PlateCarree(), + transform=self._orig_crs, antialiased=True) elif not self.nocontourf and not self.smooth: @@ -426,7 +437,7 @@ class PlotMap(MapCross, MapDrawOptions): cmap=self.cmap, norm=self.norm, alpha=self.alpha, - transform=ccrs.PlateCarree(), + transform=self._orig_crs, antialiased=True) # Draw shape files @@ -461,6 +472,7 @@ class PlotMap(MapCross, MapDrawOptions): vmax=self.bounds[-1], cmap=self.cmap, norm=self.norm, + transform=self._orig_crs, zorder=10) elif mco: self.mgaxis.scatter(scatter_data['lon'].tolist(), @@ -471,6 +483,7 @@ class PlotMap(MapCross, MapDrawOptions): linewidth=0.3, cmap=self.cmap, norm=self.norm, + transform=self._orig_crs, zorder=10) elif self.bounds: mco = self.mgaxis.scatter(scatter_data['lon'].tolist(), @@ -483,6 +496,7 @@ class PlotMap(MapCross, MapDrawOptions): vmax=self.bounds[-1], cmap=self.cmap, norm=self.norm, + transform=self._orig_crs, zorder=10) else: mco = self.mgaxis.scatter(scatter_data['lon'].tolist(), @@ -493,6 +507,7 @@ class PlotMap(MapCross, MapDrawOptions): linewidth=0.3, cmap=self.cmap, norm=self.norm, + transform=self._orig_crs, zorder=10) if data.wind_data: @@ -512,6 +527,7 @@ class PlotMap(MapCross, MapDrawOptions): width=self.wind_width, minshaft=self.wind_minshaft, scale=self.wind_scale, + transform=self._orig_crs, color='k') else: quiv = self.mgaxis.quiver(x_vec, y_vec, u_vec, v_vec, @@ -521,6 +537,7 @@ class PlotMap(MapCross, MapDrawOptions): width=self.wind_width, minshaft=self.wind_minshaft, scale=self.wind_scale, + transform=self._orig_crs, color='gray') # Draw the key plt.quiverkey(quiv, @@ -611,7 +628,7 @@ class PlotMap(MapCross, MapDrawOptions): draw_labels = bool(self.projection == 'PlateCarree') grl = self.mgaxis.gridlines(xlocs=self.lon, ylocs=self.lat, - crs=self._orig_crs, + crs=self._crs, draw_labels=draw_labels, linestyle='--', linewidth=float(self.coordsopts[0]), @@ -629,6 +646,8 @@ class PlotMap(MapCross, MapDrawOptions): (not self.nocontourf or scatter_data is not None): self.set_color_bar(mco) + if self.background: + self.mgaxis.add_image(self.stamen_terrain, self.zoom_level) # if self.background == 'shadedrelief': # #m.bluemarble() # if self.background == 'shadedrelief': @@ -682,8 +701,7 @@ class PlotMap(MapCross, MapDrawOptions): self.mgaxis.set_title(img_title, fontsize=self.fontsize, zorder=0) if self.save: - self.mgplot.savefig(fullname, - bbox_inches='tight', + self.mgplot.savefig(fullname, bbox_inches='tight', pad_inches=.2, dpi=self.dpi) return fig_name @@ -775,6 +793,8 @@ class PlotMap(MapCross, MapDrawOptions): self.init_environ(**kwargs) + LOG.setLevel(self.loglevel) + if scatter_data is not None: if isinstance(scatter_data, str): scatter_data = DataFrameHandler( @@ -857,6 +877,8 @@ class PlotMap(MapCross, MapDrawOptions): self.init_environ(**kwargs) + LOG.setLevel(self.loglevel) + if isinstance(self.srcvars, str): self.srcvars = [self.srcvars] elif not isinstance(self.srcvars, list): @@ -1067,17 +1089,12 @@ class PlotMap(MapCross, MapDrawOptions): transparent=True ) - # generate KMZ - Offline - if self.kmz: + # generate KMZ/KML - Offline/Online - online priority + if self.kmz or self.kml: + online = self.kml LOG.info("Generating KMZ ...") gen_kml(fig_names, self.srcvars[0], self.lon, self.lat, dims, - self.outdir, online=False) - - # generate KML - Online - if self.kml: - LOG.info("Generating KML ...") - gen_kml(fig_names, self.srcvars[0], self.lon, self.lat, dims, - self.outdir) + self.outdir, online=online) # generate animation. if self.anim: diff --git a/mapgenerator/plotting/tools.py b/mapgenerator/plotting/tools.py index a788d3f..ce3708c 100644 --- a/mapgenerator/plotting/tools.py +++ b/mapgenerator/plotting/tools.py @@ -70,13 +70,16 @@ def set_resolution(lon, lat): lons = maxlon + abs(minlon) if lats > 100 and lons > 200: resolution = '110m' + zoom = 1 elif lats > 45 and lons > 75: resolution = '50m' elif lats <= 45 and lons <= 75: resolution = '10m' + zoom = 4 else: resolution = '110m' # default - return resolution + zoom = 8 + return resolution, zoom def set_title(title, sdate, cdate, step, stime): -- GitLab From 11f4295b6b0b4b90faf720c992e7cfdbbfe1bc9c Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 5 Nov 2020 10:00:24 +0100 Subject: [PATCH 26/27] Fixing loglevel --- mapgenerator/plotting/definitions.py | 8 +++----- mapgenerator/plotting/plotmap.py | 6 ++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mapgenerator/plotting/definitions.py b/mapgenerator/plotting/definitions.py index 3ffb5d3..8607b5b 100644 --- a/mapgenerator/plotting/definitions.py +++ b/mapgenerator/plotting/definitions.py @@ -166,7 +166,7 @@ def parse_parameter(var, val): # parse simple list arguments elif var in ('coordsopts', 'coastsopts', 'countropts'): if val and isinstance(val, str): - LOG.debug(val, type(val)) + LOG.debug("%s: %s :: %s", var, val, type(val)) return val.split(',') return val @@ -196,8 +196,7 @@ def parse_path(directory, dfile): class MapData(object): """Wrapper class to handle all the data needed to draw a map""" - def __init__(self, loglevel='INFO', **kwargs): - LOG.setLevel(loglevel) + def __init__(self, **kwargs): LOG.info("map_data initializer") self.reset() self.max_reset() @@ -711,6 +710,7 @@ class MapGenerator(object): self.srcgaps = kwargs.get('srcgaps', [0]) self.title = kwargs.get('title', '') self.fontsize = kwargs.get('fontsize', 10) + self.loglevel = kwargs.get('loglevel', 'WARNING') self.orig_projection = kwargs.get('orig_projection', 'PlateCarree') self.orig_projection_kwargs = kwargs.get('orig_projection_kwargs', dict()) self.projection = kwargs.get('projection', 'PlateCarree') @@ -758,8 +758,6 @@ class MapCross(MapGenerator): self.bounds = kwargs.get('bounds', None) self.ticks = kwargs.get('ticks', None) self.colors = kwargs.get('colors', 'jet') -# self.over = kwargs.get('over', None) -# self.under = kwargs.get('under', None) self.bad = kwargs.get('bad', None) self.N = kwargs.get('N', None) self.lat = kwargs.get('lat', []) diff --git a/mapgenerator/plotting/plotmap.py b/mapgenerator/plotting/plotmap.py index d5a308c..5dc0836 100644 --- a/mapgenerator/plotting/plotmap.py +++ b/mapgenerator/plotting/plotmap.py @@ -775,6 +775,8 @@ class PlotMap(MapCross, MapDrawOptions): vars(self).update(parse_parameters(kwargs)) # arguments are only local + LOG.setLevel(self.loglevel) + # parse wind arguments if self.windopts and (len(self.windopts) == 4): self.wind_scale = int(self.windopts[3]) @@ -793,8 +795,6 @@ class PlotMap(MapCross, MapDrawOptions): self.init_environ(**kwargs) - LOG.setLevel(self.loglevel) - if scatter_data is not None: if isinstance(scatter_data, str): scatter_data = DataFrameHandler( @@ -877,8 +877,6 @@ class PlotMap(MapCross, MapDrawOptions): self.init_environ(**kwargs) - LOG.setLevel(self.loglevel) - if isinstance(self.srcvars, str): self.srcvars = [self.srcvars] elif not isinstance(self.srcvars, list): -- GitLab From 0d6bcab3c0ea23cc1b2f4febce4c2e106ae23362 Mon Sep 17 00:00:00 2001 From: Francesco Date: Thu, 5 Nov 2020 11:05:23 +0100 Subject: [PATCH 27/27] Updated setup.py and version to 1.0.0 --- mapgenerator/__init__.py | 5 +++-- setup.py | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mapgenerator/__init__.py b/mapgenerator/__init__.py index 7fcd7fe..8afaa8d 100644 --- a/mapgenerator/__init__.py +++ b/mapgenerator/__init__.py @@ -1,3 +1,4 @@ -""" Map Generator is a toolkit that provides easy to use 2D plotting and evaluation functions for Earth Sciences """ +""" Map Generator is a toolkit that provides easy to use 2D plotting and +evaluation functions for Earth Sciences """ -__version__ = "0.1.5" +__version__ = "1.0.0" diff --git a/setup.py b/setup.py index 966d4f7..b3175a3 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,6 @@ # You should have received a copy of the GNU General Public License # along with MapGenerator. If not, see . -from os import path from setuptools import setup from mapgenerator import __version__ @@ -51,7 +50,6 @@ setup( author_email="francesco.benincasa@bsc.es", # Packages - #package_dir={'mapgenerator': 'mapgenerator/mapgenerator'}, packages=['mapgenerator', 'mapgenerator.plotting'], # Include additional files into the package @@ -63,7 +61,8 @@ setup( keywords=['earth sciences', 'weather', 'climate', 'air quality', '2D maps'], - description="Map Generator is a tool that provides easy to use 2D plotting functions for Earth sciences datasets.", + description="""Map Generator is a tool that provides easy to use 2D plotting + functions for Earth sciences datasets.""", # long_description=open("README.rst").read(), # long_description_content_type='text/x-rst', -- GitLab