2424
2525import collections
2626import datetime
27+ import functools
28+ import warnings
2729
2830import matplotlib .axes
2931import matplotlib .collections as mpl_collections
@@ -305,35 +307,89 @@ def _fixup_dates(coord, values):
305307 return values
306308
307309
308- def _draw_1d_from_points (draw_method_name , arg_func , cube , * args , ** kwargs ):
309- # NB. In the interests of clarity we use "u" to refer to the horizontal
310- # axes on the matplotlib plot.
310+ def _data_from_coord_or_cube (c ):
311+ if isinstance (c , iris .cube .Cube ):
312+ data = c .data
313+ elif isinstance (c , iris .coords .Coord ):
314+ data = _fixup_dates (c , c .points )
315+ else :
316+ raise TypeError ('Plot arguments must be cubes or coordinates.' )
317+ return data
318+
319+
320+ def _uv_from_u_object_v_object (u_object , v_object ):
321+ ndim_msg = 'Cube or coordinate must be 1-dimensional. Got {} dimensions.'
322+ if u_object is not None and u_object .ndim > 1 :
323+ raise ValueError (ndim_msg .format (u_object .ndim ))
324+ if v_object .ndim > 1 :
325+ raise ValueError (ndim_msg .format (v_object .ndim ))
326+ type_msg = 'Plot arguments must be cubes or coordinates.'
327+ v = _data_from_coord_or_cube (v_object )
328+ if u_object is None :
329+ u = np .arange (v .shape [0 ])
330+ else :
331+ u = _data_from_coord_or_cube (u_object )
332+ return u , v
311333
312- # get & remove the coords entry from kwargs
313- coords = kwargs .pop ('coords' , None )
314- mode = iris .coords .POINT_MODE
315- if coords is not None :
316- plot_defn = _get_plot_defn_custom_coords_picked (cube , coords , mode ,
317- ndims = 1 )
334+
335+ def _u_object_from_v_object (v_object ):
336+ u_object = None
337+ if isinstance (v_object , iris .cube .Cube ):
338+ plot_defn = _get_plot_defn (v_object , iris .coords .POINT_MODE , ndims = 1 )
339+ u_object , = plot_defn .coords
340+ return u_object
341+
342+
343+ def _get_plot_objects (args ):
344+ if len (args ) > 1 and isinstance (args [1 ],
345+ (iris .cube .Cube , iris .coords .Coord )):
346+ # two arguments
347+ u_object , v_object = args [:2 ]
348+ u , v = _uv_from_u_object_v_object (* args [:2 ])
349+ args = args [2 :]
350+ if len (u ) != len (v ):
351+ msg = "The x and y-axis objects are not compatible. They should " \
352+ "have equal sizes but got ({}: {}) and ({}: {})."
353+ raise ValueError (msg .format (u_object .name (), len (u ),
354+ v_object .name (), len (v )))
318355 else :
319- plot_defn = _get_plot_defn (cube , mode , ndims = 1 )
356+ # single argument
357+ v_object = args [0 ]
358+ u_object = _u_object_from_v_object (v_object )
359+ u , v = _uv_from_u_object_v_object (u_object , args [0 ])
360+ args = args [1 :]
361+ return u_object , v_object , u , v , args
320362
321- data = cube .data
322363
323- # Obtain U coordinates
324- u_coord , = plot_defn .coords
325- if u_coord :
326- u = u_coord .points
327- u = _fixup_dates (u_coord , u )
364+ def _draw_1d_from_points (draw_method_name , arg_func , * args , ** kwargs ):
365+ # NB. In the interests of clarity we use "u" to refer to the horizontal
366+ # axes on the matplotlib plot and "v" for the vertical axes.
367+
368+ # retrieve the objects that are plotted on the horizontal and vertical
369+ # axes (cubes or coordinates) and their respective values, along with the
370+ # argument tuple with these objects removed
371+ u_object , v_object , u , v , args = _get_plot_objects (args )
372+
373+ # if both u_object and v_object are coordinates then check if a map
374+ # should be drawn
375+ if isinstance (u_object , iris .coords .Coord ) and \
376+ isinstance (v_object , iris .coords .Coord ) and \
377+ _can_draw_map ([v_object , u_object ]):
378+ # Replace non-cartopy subplot/axes with a cartopy alternative and set
379+ # the transform keyword.
380+ draw_method , kwargs = _geoaxes_draw_method_and_kwargs (u_object ,
381+ v_object ,
382+ draw_method_name ,
383+ kwargs )
328384 else :
329- u = np .arange (data .shape [0 ])
385+ # just use a pyplot function to draw
386+ draw_method = getattr (plt , draw_method_name )
330387
331- draw_method = getattr (plt , draw_method_name )
332388 if arg_func is not None :
333- args , kwargs = arg_func (u , data , * args , ** kwargs )
389+ args , kwargs = arg_func (u , v , * args , ** kwargs )
334390 result = draw_method (* args , ** kwargs )
335391 else :
336- result = draw_method (u , data , * args , ** kwargs )
392+ result = draw_method (u , v , * args , ** kwargs )
337393
338394 return result
339395
@@ -362,6 +418,31 @@ def _get_cartopy_axes(cartopy_proj):
362418 return ax
363419
364420
421+ def _geoaxes_draw_method_and_kwargs (x_coord , y_coord , draw_method_name ,
422+ kwargs ):
423+ """
424+ Retrieve a GeoAxes draw method and appropriate keyword arguments for
425+ calling it given the coordinates and existing keywords.
426+
427+ """
428+ if x_coord .coord_system != y_coord .coord_system :
429+ raise ValueError ('The X and Y coordinates must have equal coordinate'
430+ ' systems.' )
431+ cs = x_coord .coord_system
432+ if cs is not None :
433+ cartopy_proj = cs .as_cartopy_projection ()
434+ else :
435+ cartopy_proj = cartopy .crs .PlateCarree ()
436+ ax = _get_cartopy_axes (cartopy_proj )
437+ draw_method = getattr (ax , draw_method_name )
438+ # Set the "from transform" keyword.
439+ new_kwargs = kwargs .copy ()
440+ assert 'transform' not in new_kwargs , 'Transform keyword is not allowed.'
441+ new_kwargs ['transform' ] = cartopy_proj
442+
443+ return draw_method , new_kwargs
444+
445+
365446def _map_common (draw_method_name , arg_func , mode , cube , plot_defn ,
366447 * args , ** kwargs ):
367448 """
@@ -410,24 +491,11 @@ def _map_common(draw_method_name, arg_func, mode, cube, plot_defn,
410491 x = np .append (x , x [:, 0 :1 ] + 360 * direction , axis = 1 )
411492 data = ma .concatenate ([data , data [:, 0 :1 ]], axis = 1 )
412493
413- # Replace non-cartopy subplot/axes with a cartopy alternative.
414- if x_coord .coord_system != y_coord .coord_system :
415- raise ValueError ('The X and Y coordinates must have equal coordinate'
416- ' systems.' )
417- cs = x_coord .coord_system
418- if cs :
419- cartopy_proj = cs .as_cartopy_projection ()
420- else :
421- cartopy_proj = cartopy .crs .PlateCarree ()
422- ax = _get_cartopy_axes (cartopy_proj )
423-
424- draw_method = getattr (ax , draw_method_name )
425-
426- # Set the "from transform" keyword.
427- # NB. While cartopy doesn't support spherical contours, just use the
428- # projection as the source CRS.
429- assert 'transform' not in kwargs , 'Transform keyword is not allowed.'
430- kwargs ['transform' ] = cartopy_proj
494+ # Replace non-cartopy subplot/axes with a cartopy alternative and set the
495+ # transform keyword.
496+ draw_method , kwargs = _geoaxes_draw_method_and_kwargs (x_coord , y_coord ,
497+ draw_method_name ,
498+ kwargs )
431499
432500 if arg_func is not None :
433501 new_args , kwargs = arg_func (x , y , data , * args , ** kwargs )
@@ -706,9 +774,67 @@ def points(cube, *args, **kwargs):
706774 * args , ** kwargs )
707775
708776
709- def plot (cube , * args , ** kwargs ):
777+ def _1d_coords_deprecation_handler (func ):
778+ """
779+ Manage the deprecation of the coords keyword argument to 1d plot
780+ functions.
781+
782+ """
783+ @functools .wraps (func )
784+ def _wrapper (* args , ** kwargs ):
785+ coords = kwargs .pop ('coords' , None )
786+ if coords is not None :
787+ # issue a deprecation warning and check to see if the old
788+ # interface should be mimicked for the deprecation period
789+ warnings .warn ('The coords keyword argument is deprecated.' ,
790+ stacklevel = 2 )
791+ if len (coords ) != 1 :
792+ msg = 'The list of coordinates given should have length 1 ' \
793+ 'but it has length {}.'
794+ raise ValueError (msg .format (len (coords )))
795+ if isinstance (args [0 ], iris .cube .Cube ):
796+ if len (args ) < 2 or not isinstance (args [1 ], (iris .cube .Cube ,
797+ iris .coords .Coord )):
798+ if isinstance (coords [0 ], basestring ):
799+ coord = args [0 ].coord (name = coords [0 ])
800+ else :
801+ coord = args [0 ].coord (coord = coords [0 ])
802+ if not args [0 ].coord_dims (coord ):
803+ raise ValueError ("The coordinate {!r} doesn't "
804+ "span a data dimension."
805+ "" .format (coord .name ()))
806+ args = (coord ,) + args
807+ return func (* args , ** kwargs )
808+ return _wrapper
809+
810+
811+ @_1d_coords_deprecation_handler
812+ def plot (* args , ** kwargs ):
710813 """
711- Draws a line plot based on the given Cube.
814+ Draws a line plot based on the given cube(s) or coordinate(s).
815+
816+ The first one or two arguments may be cubes or coordinates to plot.
817+ Each of the following is valid::
818+
819+ # plot a 1d cube against its dimension coordinate
820+ plot(cube)
821+
822+ # plot a 1d coordinate
823+ plot(coord)
824+
825+ # plot a 1d cube against a given 1d coordinate, with the cube
826+ # values on the y-axis and the coordinate on the x-axis
827+ plot(coord, cube)
828+
829+ # plot a 1d cube against a given 1d coordinate, with the cube
830+ # values on the x-axis and the coordinate on the y-axis
831+ plot(cube, coord)
832+
833+ # plot two 1d coordinates against one-another
834+ plot(coord1, coord2)
835+
836+ # plot two 1d cubes against one-another
837+ plot(cube1, cube2)
712838
713839 Kwargs:
714840
@@ -718,12 +844,17 @@ def plot(cube, *args, **kwargs):
718844 element is the horizontal axis of the plot and the second element is
719845 the vertical axis of the plot.
720846
721- See :func:`matplotlib.pyplot.plot` for details of other valid keyword
847+ .. deprecated:: 1.5
848+
849+ The plot coordinates can be specified explicitly as in the
850+ above examples, so this keyword is no longer needed.
851+
852+ See :func:`matplotlib.pyplot.plot` for details of valid keyword
722853 arguments.
723854
724855 """
725856 _plot_args = None
726- return _draw_1d_from_points ('plot' , _plot_args , cube , * args , ** kwargs )
857+ return _draw_1d_from_points ('plot' , _plot_args , * args , ** kwargs )
727858
728859
729860# Provide convenience show method from pyplot
0 commit comments