@@ -171,14 +171,12 @@ def get_site_plot_extent(self, datasets, point_test_key='longitude', bbox_test_k
171171
172172 return extent
173173
174- def setup_site_plot (self , fig , ax , extent , crs = None , bg_img_name = None , bg_img_resolution = 'low' , coastlines_resolution = '10m' , add_gridlines = True ):
174+ def setup_site_plot (self , ax , extent , crs = None , bg_img_name = None , bg_img_resolution = 'low' , coastlines_resolution = '10m' , add_gridlines = True , draw_gridline_labels = True ):
175175 """
176176 Setup the site map
177177
178178 This sets fixed properties of the map, such as extent and base map.
179179
180- :param fig: The figure object
181- :type fig: matplotlib.figure.Figure
182180 :param ax: The axis object
183181 :type ax: matplotlib.axes.Axes
184182 :param extent: The geographical bounding box extent for the map
@@ -200,6 +198,8 @@ def setup_site_plot(self, fig, ax, extent, crs=None, bg_img_name=None, bg_img_re
200198 :type coastlines_resolution: str
201199 :param add_gridlines: Add gridlines to the map
202200 :type add_gridlines: bool
201+ :param draw_gridline_labels: Draw gridline labels on the map
202+ :type draw_gridline_labels: bool
203203 """
204204
205205 if not crs :
@@ -214,14 +214,12 @@ def setup_site_plot(self, fig, ax, extent, crs=None, bg_img_name=None, bg_img_re
214214 ax .stock_img ()
215215
216216 if add_gridlines :
217- ax .gridlines ()
217+ ax .gridlines (draw_labels = draw_gridline_labels )
218218
219- def plot_point_site (self , fig , ax , dataset , xkey = 'longitude' , ykey = 'latitude' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
219+ def plot_point_site (self , ax , dataset , xkey = 'longitude' , ykey = 'latitude' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
220220 """
221221 Plot the site information for the given dataset on the map
222222
223- :param fig: The figure object
224- :type fig: matplotlib.figure.Figure
225223 :param ax: The axis object
226224 :type ax: matplotlib.axes.Axes
227225 :param dataset: The dataset to plot
@@ -264,12 +262,10 @@ def plot_point_site(self, fig, ax, dataset, xkey='longitude', ykey='latitude', s
264262 except KeyError :
265263 pass
266264
267- def plot_bbox_site (self , fig , ax , dataset , xminkey = 'geospatial_lon_min' , xmaxkey = 'geospatial_lon_max' , yminkey = 'geospatial_lat_min' , ymaxkey = 'geospatial_lat_max' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
265+ def plot_bbox_site (self , ax , dataset , xminkey = 'geospatial_lon_min' , xmaxkey = 'geospatial_lon_max' , yminkey = 'geospatial_lat_min' , ymaxkey = 'geospatial_lat_max' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
268266 """
269267 Plot the site information for the given dataset on the map
270268
271- :param fig: The figure object
272- :type fig: matplotlib.figure.Figure
273269 :param ax: The axis object
274270 :type ax: matplotlib.axes.Axes
275271 :param dataset: The dataset to plot
@@ -324,12 +320,10 @@ def plot_bbox_site(self, fig, ax, dataset, xminkey='geospatial_lon_min', xmaxkey
324320 except KeyError :
325321 pass
326322
327- def plot_site (self , fig , ax , dataset , point_test_key = 'longitude' , bbox_test_key = 'geospatial_lon_min' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
323+ def plot_site (self , ax , dataset , point_test_key = 'longitude' , bbox_test_key = 'geospatial_lon_min' , site_key = 'site' , transform = None , xoffset = 0 , yoffset = - 0.5 , fontsize = 'large' , horizontalalignment = 'left' , opts = {}):
328324 """
329325 Plot the site information for the given dataset on the map
330326
331- :param fig: The figure object
332- :type fig: matplotlib.figure.Figure
333327 :param ax: The axis object
334328 :type ax: matplotlib.axes.Axes
335329 :param dataset: The dataset to plot
@@ -361,13 +355,13 @@ def plot_site(self, fig, ax, dataset, point_test_key='longitude', bbox_test_key=
361355 # Plot according to whether site coordinates are given by a point
362356 # or a bounding box
363357 if point_test_key in dataset .metadata ['header' ]:
364- self .plot_point_site (fig , ax , dataset , site_key = site_key , transform = transform , xoffset = xoffset , yoffset = yoffset , fontsize = fontsize , horizontalalignment = horizontalalignment , opts = opts )
358+ self .plot_point_site (ax , dataset , site_key = site_key , transform = transform , xoffset = xoffset , yoffset = yoffset , fontsize = fontsize , horizontalalignment = horizontalalignment , opts = opts )
365359 elif bbox_test_key in dataset .metadata ['header' ]:
366- self .plot_bbox_site (fig , ax , dataset , site_key = site_key , transform = transform , xoffset = xoffset , yoffset = yoffset , fontsize = fontsize , horizontalalignment = horizontalalignment , opts = opts )
360+ self .plot_bbox_site (ax , dataset , site_key = site_key , transform = transform , xoffset = xoffset , yoffset = yoffset , fontsize = fontsize , horizontalalignment = horizontalalignment , opts = opts )
367361 else :
368362 raise KeyError (f"Cannot plot site on the map as no spatial coordinate keys were found in the header" )
369363
370- def setup_figure_and_axes (self , figsize = None , width_ratios = [1 ,1 ], projection = None ):
364+ def setup_figure_and_axes (self , figsize = None , nrows = 1 , ncols = 2 , width_ratios = [1 ,1 ], projection = None ):
371365 """
372366 Setup the figure and axes array
373367
@@ -381,9 +375,15 @@ def setup_figure_and_axes(self, figsize=None, width_ratios=[1,1], projection=Non
381375
382376 :param figsize: The figure size tuple as (width, height)
383377 :type figsize: tuple
384- :param width_ratios: The width ratios of the two subplots - the data
385- plot and the map, in that order. For example, [2,1] will make the
386- plot twice the size of the map
378+ :param nrows: The number of rows for the subplots (1 or 2)
379+ :type nrows: int
380+ :param ncols: The number of columns for the subplots (1 or 2)
381+ :type ncols: int
382+ :param width_ratios: The width ratios of the subplots. If there is
383+ only one subplot, then it is the map, and this should be [1]. If
384+ there are two subplots, then these are the data plot and the map, in
385+ that order. For example, [2,1] will make the plot twice the size of
386+ the map
387387 :type width_ratios: list
388388 :param projection: The projection to transform the coordinates on the
389389 map. If not specified, it defaults to ccrs.PlateCarree()
@@ -394,12 +394,109 @@ def setup_figure_and_axes(self, figsize=None, width_ratios=[1,1], projection=Non
394394 projection = ccrs .PlateCarree ()
395395
396396 self .fig = plt .figure (figsize = figsize )
397- gs = self .fig .add_gridspec (1 , 2 , width_ratios = width_ratios )
397+ gs = self .fig .add_gridspec (nrows = nrows , ncols = ncols , width_ratios = width_ratios )
398+
399+ if nrows * ncols > 1 :
400+ self .axs .append (self .fig .add_subplot (gs [0 , 0 ]))
401+ self .axs .append (self .fig .add_subplot (gs [0 , 1 ], projection = projection ))
402+ else :
403+ self .axs .append (self .fig .add_subplot (gs [0 , 0 ], projection = projection ))
404+
405+ def _setup_fallback_figure_and_axes (self , fig = None , axs = None , plot_on_map = False ):
406+ """
407+ Setup a fallback figure and axes array
408+
409+ This calls setup_figure_and_axes() if it hasn't already beeen called
410+
411+ :param fig: The figure object
412+ :type fig: matplotlib.figure.Figure
413+ :param axs: The axes array
414+ :type axs: matplotlib.axes.Axes
415+ :param plot_on_map: Only setup a map, as the data are to be plotted
416+ directly on the map
417+ :type plot_on_map: bool
418+ """
419+
420+ if fig :
421+ self .fig = fig
422+
423+ if axs :
424+ self .axs = axs
425+
426+ if not self .fig or not self .axs :
427+ fa_opts = {'nrows' : 1 , 'ncols' : 2 , 'width_ratios' : [1 ,1 ]}
428+
429+ if plot_on_map :
430+ fa_opts = {'nrows' : 1 , 'ncols' : 1 , 'width_ratios' : [1 ]}
431+
432+ self .setup_figure_and_axes (** fa_opts )
433+
434+ def _add_figure_annotations (self , axs_idx = 0 , map_axs_idx = 1 , plot_on_map = False ):
435+ """
436+ Add annotations to the figure
437+
438+ :param axs_idx: The index of the axis object in the axs array
439+ :type axs_idx: int
440+ :param map_axs_idx: The index of the map axis object in the axs array
441+ :type map_axs_idx: int
442+ :param plot_on_map: Only setup a map, as the data are to be plotted
443+ directly on the map
444+ :type plot_on_map: bool
445+ """
446+
447+ self .add_figure_title (self .title )
448+ self .add_figure_caption (self .caption )
449+
450+ if plot_on_map :
451+ self .setup_site_plot (self .axs [axs_idx ], self .get_site_plot_extent (self .datasets ))
452+ else :
453+ self .setup_data_plot (self .axs [axs_idx ], xlabel = self .xlabel , ylabel = self .ylabel )
454+ self .setup_site_plot (self .axs [map_axs_idx ], self .get_site_plot_extent (self .datasets ))
455+
456+ def _plot_datasets (self , axs_idx = 0 , map_axs_idx = 1 , plot_on_map = False , invert_xaxis = False , invert_yaxis = False , opts = {}):
457+ """
458+ Plot the data for the figure datasets
459+
460+ :param axs_idx: The index of the axis object in the axs array
461+ :type axs_idx: int
462+ :param map_axs_idx: The index of the map axis object in the axs array
463+ :type map_axs_idx: int
464+ :param plot_on_map: Only setup a map, as the data are to be plotted
465+ directly on the map
466+ :type plot_on_map: bool
467+ :param invert_xaxis: Invert the x-axis
468+ :type invert_xaxis: bool
469+ :param invert_yaxis: Invert the y-axis
470+ :type invert_yaxis: bool
471+ :param opts: Option kwargs to apply to all plots (e.g., color, marker)
472+ :type opts: dict
473+ """
474+
475+ generate_colors = True
476+
477+ if 'color' in opts :
478+ generate_colors = False
479+
480+ for i , dataset in enumerate (self .datasets ):
481+ label = dataset .get_metadata_item_value (self .label_key )
482+
483+ if generate_colors :
484+ opts .update ({'color' : f'C{ i } ' })
398485
399- self . axs . append ( self . fig . add_subplot ( gs [ 0 , 0 ]))
400- self .axs . append ( self . fig . add_subplot ( gs [ 0 , 1 ], projection = projection ))
486+ if plot_on_map :
487+ projection = self .axs [ axs_idx ]. projection
401488
402- def plot_datasets (self , datasets , fig = None , axs = None , axs_idx = 0 , map_axs_idx = 1 , xcol = None , ycol = None , xidx = None , yidx = 0 , xlabel = None , ylabel = None , title = None , title_wrap = True , caption = None , label_key = None , invert_xaxis = False , invert_yaxis = False , show = True , opts = {}):
489+ if not projection :
490+ projection = ccrs .PlateCarree ()
491+
492+ opts .update ({'transform' : projection })
493+
494+ self .plot_data (self .axs [axs_idx ], dataset , self .xcol , self .ycol , label = label , invert_xaxis = invert_xaxis , invert_yaxis = invert_yaxis , opts = opts )
495+ else :
496+ self .plot_data (self .axs [axs_idx ], dataset , self .xcol , self .ycol , label = label , invert_xaxis = invert_xaxis , invert_yaxis = invert_yaxis , opts = opts )
497+ self .plot_site (self .axs [map_axs_idx ], dataset , site_key = self .label_key , opts = opts )
498+
499+ def plot_datasets (self , datasets , fig = None , axs = None , axs_idx = 0 , map_axs_idx = 1 , xcol = None , ycol = None , xidx = None , yidx = 0 , xlabel = None , ylabel = None , title = None , caption = None , label_key = None , invert_xaxis = False , invert_yaxis = False , plot_on_map = False , show = True , opts = {}):
403500 """
404501 Plot the data for the given datasets
405502
@@ -442,8 +539,6 @@ def plot_datasets(self, datasets, fig=None, axs=None, axs_idx=0, map_axs_idx=1,
442539 :type ylabel: str
443540 :param title: The figure title text
444541 :type title: str
445- :param title_wrap: Wrap the title text
446- :type title_wrap: bool
447542 :param caption: The figure caption text
448543 :type caption: str
449544 :param label_key: The key of a header item in the XCSV header to be
@@ -453,63 +548,20 @@ def plot_datasets(self, datasets, fig=None, axs=None, axs_idx=0, map_axs_idx=1,
453548 :type invert_xaxis: bool
454549 :param invert_yaxis: Invert the y-axis
455550 :type invert_yaxis: bool
551+ :param plot_on_map: Instead of plotting the data on a plot alongside
552+ the site map, show just a map and plot the data directly on the map.
553+ This requires the data to be coordinates
554+ :type plot_on_map: bool
456555 :param show: Show the plot
457556 :type show: bool
458557 :param opts: Option kwargs to apply to all plots (e.g., color, marker)
459558 :type opts: dict
460559 """
461-
462- if fig :
463- self .fig = fig
464-
465- if axs :
466- self .axs = axs
467-
468- if not self .fig or not self .axs :
469- self .setup_figure_and_axes ()
470-
471- self .datasets = datasets
472- self .xcol = xcol
473- self .ycol = ycol
474- generate_colors = True
475-
476- if not title :
477- title = datasets [0 ].get_metadata_item_value (self .DEFAULTS ['title_key' ])
478-
479- if not caption :
480- caption = datasets [0 ].get_metadata_item_value (self .DEFAULTS ['caption_key' ])
481-
482- if not label_key :
483- label_key = self .DEFAULTS ['label_key' ]
484-
485- if not xcol :
486- if xidx is not None :
487- self .xcol = datasets [0 ].data .iloc [:, xidx ].name
488-
489- if not ycol :
490- self .ycol = datasets [0 ].data .iloc [:, yidx ].name
491-
492- if not xlabel :
493- xlabel = self .xcol
494-
495- if not ylabel :
496- ylabel = self .ycol
497-
498- if 'color' in opts :
499- generate_colors = False
500-
501- self .fig .suptitle (title , wrap = title_wrap )
502- self .setup_data_plot (self .fig , self .axs [axs_idx ], caption = caption , xlabel = xlabel , ylabel = ylabel )
503- self .setup_site_plot (self .fig , self .axs [map_axs_idx ], self .get_site_plot_extent (datasets ))
504-
505- for i , dataset in enumerate (datasets ):
506- label = dataset .get_metadata_item_value (label_key )
507-
508- if generate_colors :
509- opts .update ({'color' : f'C{ i } ' })
510560
511- self .plot_data (self .fig , self .axs [axs_idx ], dataset , self .xcol , self .ycol , label = label , invert_xaxis = invert_xaxis , invert_yaxis = invert_yaxis , opts = opts )
512- self .plot_site (self .fig , self .axs [map_axs_idx ], dataset , site_key = label_key , opts = opts )
561+ self ._setup_fallback_figure_and_axes (fig , axs , plot_on_map )
562+ self ._store_figure_parameters (datasets , xcol , ycol , xidx , yidx , xlabel , ylabel , title , caption , label_key )
563+ self ._add_figure_annotations (axs_idx , map_axs_idx , plot_on_map )
564+ self ._plot_datasets (axs_idx , map_axs_idx , plot_on_map , invert_xaxis , invert_yaxis , opts )
513565
514566 if show :
515567 plt .show ()
0 commit comments