diff --git a/src/LIB/Python/Panel_Plot.py b/src/LIB/Python/Panel_Plot.py index 8e095faf07fd46b6dcd4f01df5dddca6b0b5759a..328d26264ad1f209de65a0ecdc36f563e07dde70 100644 --- a/src/LIB/Python/Panel_Plot.py +++ b/src/LIB/Python/Panel_Plot.py @@ -20,33 +20,54 @@ import cartopy.feature as cfeature class PanelPlot(): def __init__(self, nb_l, nb_c, Lfigsize, bigtitle, titlepad=40, minmaxpad=1.03, timepad=-0.06, lateralminmaxpad=0.86, - labelcolorbarpad=6.0, colorbaraspect=20, colorbarpad=0.04 ): - self.bigtitle = bigtitle - self.Lfigsize = Lfigsize - self.nb_l = nb_l - self.nb_c = nb_c + labelcolorbarpad=6.0, colorbaraspect=20, colorbarpad=0.04, tickspad=0.8, + minmaxTextSize=10, bigtitleSize=13, titleSize=12, + xlabelSize=11, ylabelSize=11, timeSize=11, cbTicksLabelSize=11, cbTitleSize=11, xyTicksLabelSize=10, figBoxLinewidth=1, + xyTicksWidth=1, xyTicksLength=6): + + self.bigtitle = bigtitle # Panel title + self.Lfigsize = Lfigsize # Panel size + self.nb_l = nb_l # Panel number of lines + self.nb_c = nb_c # Panel number of rows self.nb_graph = 0 # New independent graph within the subplot + self.titlepad = titlepad # Title pad (vertical shift) from graph + self.tickspad = tickspad # Ticks pad (between ticks and axis label) self.minmaxpad = minmaxpad # Min/Max print pad (vertical shift) self.timepad = timepad # Time print pad (vertical shift) self.colorbarpad = colorbarpad # Colorbar pad (horizontal shift from graph) self.lateralminmaxpad = lateralminmaxpad - self.labelcolorbarpad = labelcolorbarpad # Vertical colorbal label pad - self.colorbaraspect = colorbaraspect # Ratio of long to short dimensions of colorbar w.r.t. the figure - + self.labelcolorbarpad = labelcolorbarpad # Vertical colorbal label pad + self.colorbaraspect = colorbaraspect # Ratio of long to short dimensions of colorbar w.r.t. the figure + + self.minmaxTextSize = minmaxTextSize # min/max text fontsize + self.bigtitleSize = bigtitleSize # Panel title fontsize + self.titleSize = titleSize # Graph title fontsize + self.xlabelSize = xlabelSize # X-label fontsize + self.ylabelSize = ylabelSize # Y-label fontsize + self.timeSize = timeSize # Time attribute of the graphs fontsize + self.cbTicksLabelSize = cbTicksLabelSize # Colorbar ticks label fontsize + self.cbTitleSize = cbTitleSize # Colorbar title fontsize + self.xyTicksLabelSize = xyTicksLabelSize # X/Y ticks label fontsize + self.figBoxLinewidth = figBoxLinewidth # Figure Box contour line width + + self.xyTicksWidth = xyTicksWidth # Ticks width + self.xyTicksLength = xyTicksLength # Ticks length + # Initialization of the panel plots self.fig = plt.figure(figsize=(self.Lfigsize[0],self.Lfigsize[1])) self.fig.set_dpi(125) self.fig.suptitle(self.bigtitle,fontsize=16) - def save_graph(self, iplt, fig): + def save_graph(self, iplt, fig, fig_name='tempgraph'): """ - Create a temporary png file of (sub)-plot(s) which can be converted to PDF + Create a temporary png file of the panel plot which can be converted to PDF """ self.iplt = iplt self.fig = fig + self.fig_name=fig_name # .png figure prefix name - self.fig.savefig('tempgraph'+str(self.iplt)) #TODO possibility to change the default value of .png file name + self.fig.savefig(self.fig_name+str(self.iplt)) self.iplt+=1 return self.iplt @@ -57,7 +78,7 @@ class PanelPlot(): self.drawCoastLines = drawCoastLines self.projo = projo - # Grid lines and labels + # Grid lines and labels if 'PlateCarree' in str(projo): gl = ax.gridlines(crs=self.projo, draw_labels=True, linewidth=1, color='gray') if float(cartopy.__version__[:4]) >= 0.18: @@ -67,7 +88,7 @@ class PanelPlot(): gl.xlabels_top = False gl.ylabels_right = False - # Coastlines + # Coastlines if self.drawCoastLines and 'GeoAxes' in str(type(ax)): ax.coastlines(resolution='10m') @@ -75,14 +96,19 @@ class PanelPlot(): ax.add_feature(cfeature.BORDERS) ax.add_feature(cfeature.LAKES, alpha=0.7) - def addWhitecm(self, Laddwhite, colormap_in, nb_level): - if Laddwhite: + def addWhitecm(self, colormap_in, nb_level,whiteTop=False): + """ + Add a white color at the top (whiteTop=True) or bottom of the colormap w.r.t. the number of independent colors used + """ color_map = cm.get_cmap(colormap_in, 256) newcolor_map = color_map(np.linspace(0, 1, 256)) whites = np.array([1, 1, 1, 1]) #RBG code + opacity - for i in range(int(256/nb_level)): newcolor_map[:i, :] = whites + if whiteTop: + for i in range(int(256/nb_level)): newcolor_map[-i, :] = whites + else: + for i in range(int(256/nb_level)): newcolor_map[:i, :] = whites newcmp = ListedColormap(newcolor_map) - return newcmp + return newcmp def set_Title(self, ax, i, title, Lid_overlap, xlab, ylab): @@ -97,10 +123,10 @@ class PanelPlot(): self.i = i #self.ax[self.i].set_xlabel("test", fontweight='bold') if not Lid_overlap: - self.ax[self.i].set_title(title, pad=self.titlepad) + self.ax[self.i].set_title(title, pad=self.titlepad, fontsize=self.titleSize) else: # If graph overlap, title is concatenated new_title = self.ax[self.i].get_title() + ' and ' + title - self.ax[self.i].set_title(new_title, pad=self.titlepad) + self.ax[self.i].set_title(new_title, pad=self.titlepad, fontsize=self.titleSize) def set_xlim(self, ax, i, xlim): """ @@ -110,7 +136,7 @@ class PanelPlot(): self.xlim = xlim self.i = i - self.ax[self.i].set_xlim(xlim[0],xlim[1]) + self.ax[self.i].set_xlim(xlim[0],xlim[1])#, fontsize=self.xlabelSize) def set_ylim(self, ax, i, ylim): """ @@ -120,7 +146,7 @@ class PanelPlot(): self.ylim = ylim self.i = i - self.ax[self.i].set_ylim(ylim[0],ylim[1]) + self.ax[self.i].set_ylim(ylim[0],ylim[1])#, fontsize=self.ylabelSize) def set_XYaxislab(self, ax, i, xlab, ylab): """ @@ -136,14 +162,14 @@ class PanelPlot(): # https://github.com/SciTools/cartopy/issues/1332 if 'GeoAxes' in str(type(self.ax[self.i])): self.ax[self.i].text(-0.11, 0.45, ylab, verticalalignment='top', horizontalalignment='left', - rotation='vertical', rotation_mode='anchor', transform=self.ax[self.i].transAxes, color='black', fontsize=11) + rotation='vertical', rotation_mode='anchor', transform=self.ax[self.i].transAxes, color='black', fontsize=self.ylabelSize) self.ax[self.i].text(0.45, -0.06, xlab, verticalalignment='top', horizontalalignment='left', - rotation='horizontal', rotation_mode='anchor', transform=self.ax[self.i].transAxes, color='black', fontsize=11) + rotation='horizontal', rotation_mode='anchor', transform=self.ax[self.i].transAxes, color='black', fontsize=self.xlabelSize) else: - self.ax[self.i].set_xlabel(xlab) - self.ax[self.i].set_ylabel(ylab) + self.ax[self.i].set_xlabel(xlab, fontsize=self.xlabelSize, labelpad=0.1) + self.ax[self.i].set_ylabel(ylab, fontsize=self.ylabelSize, labelpad=0.1) - def addLine(self, ax, beg_coord, end_coord, color='black', linewidth=1): + def addLine(self, ax, beg_coord, end_coord, color='black', linewidth=0.2): self.ax = ax self.beg_coord = beg_coord self.end_coord = end_coord @@ -171,10 +197,10 @@ class PanelPlot(): strtext = " min = " + "{:.3e}".format(np.nanmin(var*facconv)) + " max = " + "{:.3e}".format(np.nanmax(var*facconv)) if not Lid_overlap: self.ax[self.i].text(0.01, self.minmaxpad, strtext, verticalalignment='top', horizontalalignment='left', - transform=self.ax[self.i].transAxes, color='black', fontsize=10) + transform=self.ax[self.i].transAxes, color='black', fontsize=self.minmaxTextSize) else: self.ax[self.i].text(self.lateralminmaxpad, self.minmaxpad, strtext, verticalalignment='top', horizontalalignment='right', - transform=self.ax[self.i].transAxes, color='black', fontsize=10) + transform=self.ax[self.i].transAxes, color='black', fontsize=self.minmaxTextSize) # Print to help choose min/max value for ticks self.print_minmax(var*facconv, title) @@ -188,12 +214,12 @@ class PanelPlot(): strtext = "Time = " + timetxt self.ax[self.i].text(0.0, self.timepad, strtext, verticalalignment='top', horizontalalignment='left', - transform=self.ax[self.i].transAxes, color='black', fontsize=10) + transform=self.ax[self.i].transAxes, color='black', fontsize=self.timeSize) def psectionV(self, Lxx=[], Lzz=[], Lvar=[], Lxlab=[], Lylab=[], Ltitle=[], Lminval=[], Lmaxval=[], Lstep=[], Lstepticks=[], Lcolormap=[], Lcbarlabel=[], LcolorLine=[], - Lfacconv=[], ax=[], Lid_overlap=[], colorbar=True, orog=[], Lxlim=[], Lylim=[], Ltime=[], Lpltype=[], LaddWhite_cm=[]): + Lfacconv=[], ax=[], Lid_overlap=[], colorbar=True, orog=[], Lxlim=[], Lylim=[], Ltime=[], Lpltype=[], LaddWhite_cm=[], LwhiteTop=[]): """ Horizontal cross section plot Parameters : @@ -216,9 +242,10 @@ class PanelPlot(): - Lfacconv : List of factors for unit conversion of each variables - ax : List of fig.axes for ploting multiple different types of plots in a subplot panel - Lid_overlap: List of number index of plot to overlap current variables - - Lpltype : List of types of plot 'cf' or 'c'. cf=contourf, c=contour (lines only) + - Lpltype : List of types of plot 'cf' or 'c'. cf=contourf, c=contour (lines only) - colorbar : show colorbar or not - - LaddWhite_cm : List of boolean to add white color to a colormap at the first (low value) tick colorbar + - LaddWhite_cm : List of boolean to add white color to a colormap at the last bottom (low value) tick colorbar + - LwhiteTop : List of boolean to add the white color at the first top (high value). If false, the white is added at the bottom if Laddwhite_cm=T """ self.ax = ax firstCall = (len(self.ax) == 0) @@ -235,6 +262,7 @@ class PanelPlot(): if not Lcolormap: LcolorLine=['black']*len(Lvar) if not Lpltype: Lpltype=['cf']*len(Lvar) if not LaddWhite_cm: LaddWhite_cm=[False]*len(Lvar) + if not LwhiteTop: LwhiteTop=[False]*len(Lvar) if not Lylab: Lylab = ['']*len(Lvar) if not Lxlab: Lxlab = ['']*len(Lvar) # Add an extra percentage of the top max value for forcing the colorbar show the true user maximum value (correct a bug) @@ -261,7 +289,7 @@ class PanelPlot(): # Print time validity if Ltime: self.showTimeText(self.ax, iax, str(Ltime[i])) - + # Number of contours level if not Lstep[i]: # Default value of number of steps is 20 Lstep[i] = (Lmaxval[i] - Lminval[i])/20 @@ -270,16 +298,16 @@ class PanelPlot(): levels_contour = np.arange(Lminval[i],Lmaxval[i],step=Lstep[i]) # Add White to colormap - if LaddWhite_cm[i] and Lcolormap: Lcolormap[i]=self.addWhitecm(LaddWhite_cm[i], Lcolormap[i], len(levels_contour)) + if LaddWhite_cm[i] and Lcolormap: Lcolormap[i]=self.addWhitecm(Lcolormap[i], len(levels_contour), LwhiteTop[i]) # Plot if Lpltype[i]=='c': # Contour if LcolorLine: cf = self.ax[iax].contour(Lxx[i], Lzz[i], var*Lfacconv[i], levels=levels_contour, - norm=norm, vmin=Lminval[i], vmax=Lmaxval[i], colors=LcolorLine[i]) + norm=norm, vmin=Lminval[i], vmax=Lmaxval[i], colors=LcolorLine[i], linewidths=0.1) else: cf = self.ax[iax].contour(Lxx[i], Lzz[i], var*Lfacconv[i], levels=levels_contour, - norm=norm, vmin=Lminval[i], vmax=Lmaxval[i], cmap=Lcolormap[i]) + norm=norm, vmin=Lminval[i], vmax=Lmaxval[i], cmap=Lcolormap[i], linewidths=0.1) else: # Contourf cf = self.ax[iax].contourf(Lxx[i], Lzz[i], var*Lfacconv[i], levels=levels_contour, norm=norm, vmin=Lminval[i], vmax=Lmaxval[i], cmap=Lcolormap[i]) @@ -289,6 +317,13 @@ class PanelPlot(): # X/Y Axis label self.set_XYaxislab(self.ax, iax, Lxlab[i], Lylab[i]) + + # Ticks label + self.ax[iax].tick_params(axis='both', labelsize=self.xyTicksLabelSize, width=self.xyTicksWidth, length=self.xyTicksLength, pad=self.tickspad) + + # Bounding box of the plot line width + for axis in ['top','bottom','left','right']: + self.ax[iax].spines[axis].set_linewidth(self.figBoxLinewidth) # X/Y Axis limits value if Lxlim: @@ -304,17 +339,17 @@ class PanelPlot(): # Color label on contour-line if Lpltype[i]=='c': # Contour - self.ax[iax].clabel(cf) - #self.ax[iax].clabel(cf, levels=np.arange(Lminval[i],Lmaxval[i],step=Lstep[i])) #TODO bug, levels not recognized + self.ax[iax].clabel(cf, fontsize=self.cbTicksLabelSize) + #self.ax[iax].clabel(cf, levels=np.arange(Lminval[i],Lmaxval[i],step=Lstep[i]), fontsize=self.cbTicksLabelSize) #TODO bug, levels not recognized #Filling area under topography if not orog==[]: - self.ax[iax].fill_between(Lxx[i][0,:], orog, color='black') + self.ax[iax].fill_between(Lxx[i][0,:], orog, color='black', linewidth=0.2) # Colorbar if colorbar: cb=plt.colorbar(cf, ax=self.ax[iax], fraction=0.031, pad=self.colorbarpad, ticks=np.arange(Lminval[i],Lmaxval[i],Lstepticks[i]), aspect=self.colorbaraspect) - cb.ax.set_title(Lcbarlabel[i], pad = self.labelcolorbarpad, loc='left') #This creates a new AxesSubplot only for the colorbar y=0 ==> location at the bottom + cb.ax.set_title(Lcbarlabel[i], pad=self.labelcolorbarpad, loc='left', fontsize=self.cbTitleSize) # This creates a new AxesSubplot only for the colorbar y=0 ==> location at the bottom return self.fig @@ -324,7 +359,7 @@ class PanelPlot(): """ XY (multiple)-lines plot Parameters : - - Lxx : List of variables to plot or coordinates along the X axis #TODO : ajouter Lfacconv pour les deux axes. Impact tous les cas test avec lignes X/Y + - Lxx : List of variables to plot or coordinates along the X axis - Lyy : List of variables to plot or coordinates along the Y axis - Lxlab : List of x-axis label - Lylab : List of y-axis label @@ -410,7 +445,7 @@ class PanelPlot(): def psectionH(self, lon=[],lat=[], Lvar=[], Lcarte=[], Llevel=[], Lxlab=[], Lylab=[], Ltitle=[], Lminval=[], Lmaxval=[], Lstep=[], Lstepticks=[], Lcolormap=[], LcolorLine=[], Lcbarlabel=[], Lproj=[], Lfacconv=[], coastLines=True, ax=[], - Lid_overlap=[], colorbar=True, Ltime=[], LaddWhite_cm=[], Lpltype=[], Lcbformatlabel=[]): + Lid_overlap=[], colorbar=True, Ltime=[], LaddWhite_cm=[], LwhiteTop=[], Lpltype=[], Lcbformatlabel=[]): """ Horizontal cross section plot Parameters : @@ -438,6 +473,7 @@ class PanelPlot(): - colorbar : show colorbar or not - Lpltype : List of types of plot 'cf' or 'c'. cf=contourf, c=contour (lines only) - LaddWhite_cm : List of boolean to add white color to a colormap at the first (low value) tick colorbar + - LwhiteTop : List of boolean to add the white color at the first top (high value). If false, the white is added at the bottom if Laddwhite_cm=T - Lcbformatlabel: List of boolean to reduce the format to exponential 1.1E+02 format colorbar label """ self.ax = ax @@ -457,6 +493,7 @@ class PanelPlot(): if not Lcolormap: LcolorLine=['black']*len(Lvar) if not Lpltype: Lpltype=['cf']*len(Lvar) if not LaddWhite_cm: LaddWhite_cm=[False]*len(Lvar) + if not LwhiteTop: LwhiteTop=[False]*len(Lvar) if not Lcbformatlabel: Lcbformatlabel=[False]*len(Lvar) # Add an extra percentage of the top max value for forcing the colorbar show the true user maximum value (correct a bug) if Lstep: Lmaxval = list(map(lambda x, y: x + 1E-6*y, Lmaxval, Lstep) ) #The extra value is 1E-6 times the step ticks of the colorbar @@ -511,7 +548,7 @@ class PanelPlot(): levels_contour = np.arange(Lminval[i],Lmaxval[i],step=Lstep[i]) # Add White to colormap - if LaddWhite_cm[i] and Lcolormap: Lcolormap[i]=self.addWhitecm(LaddWhite_cm[i], Lcolormap[i], len(levels_contour)) + if LaddWhite_cm[i] and Lcolormap: Lcolormap[i]=self.addWhitecm(Lcolormap[i], len(levels_contour), LwhiteTop[i]) # Plot if Lproj: @@ -544,14 +581,14 @@ class PanelPlot(): # Color label on contour-line if Lpltype[i]=='c': # Contour if 'GeoAxes' in str(type(self.ax[self.i])): # cartopy does not like the levels arguments in clabel, known issue - self.ax[iax].clabel(cf) + self.ax[iax].clabel(cf, fontsize=self.cbTicksLabelSize) else: - self.ax[iax].clabel(cf, levels=np.arange(Lminval[i],Lmaxval[i],step=Lstep[i])) + self.ax[iax].clabel(cf, levels=np.arange(Lminval[i],Lmaxval[i],step=Lstep[i]), fontsize=self.cbTicksLabelSize) # Colorbar if colorbar: cb=plt.colorbar(cf, ax=self.ax[iax], fraction=0.031, pad=self.colorbarpad, ticks=np.arange(Lminval[i],Lmaxval[i],Lstepticks[i]), aspect=self.colorbaraspect) - cb.ax.set_title(Lcbarlabel[i], pad = self.labelcolorbarpad, loc='left') #This creates a new AxesSubplot only for the colorbar y=0 ==> location at the bottom + cb.ax.set_title(Lcbarlabel[i], pad = self.labelcolorbarpad, loc='left', fontsize=self.cbTitleSize) #This creates a new AxesSubplot only for the colorbar y=0 ==> location at the bottom if Lcbformatlabel[i]: cb.ax.set_yticklabels(["{:.1E}".format(i) for i in cb.get_ticks()]) return self.fig