示例#1
0
def create_parcelsfig_axis(spherical, land=True, projection=None, central_longitude=0):
    try:
        import matplotlib.pyplot as plt
    except:
        logger.info("Visualisation is not possible. Matplotlib not found.")
        return None, None, None, None  # creating axes was not possible

    if projection is not None and not spherical:
        raise RuntimeError('projection not accepted when Field doesn''t have geographic coordinates')

    if spherical:
        try:
            import cartopy
        except:
            logger.info("Visualisation of field with geographic coordinates is not possible. Cartopy not found.")
            return None, None, None, None  # creating axes was not possible

        projection = cartopy.crs.PlateCarree(central_longitude) if projection is None else projection
        fig, ax = plt.subplots(1, 1, subplot_kw={'projection': projection})
        try:  # gridlines not supported for all projections
            gl = ax.gridlines(crs=projection, draw_labels=True)
            gl.xlabels_top, gl.ylabels_right = (False, False)
            gl.xformatter = cartopy.mpl.gridliner.LONGITUDE_FORMATTER
            gl.yformatter = cartopy.mpl.gridliner.LATITUDE_FORMATTER
        except:
            pass

        if land:
            ax.coastlines()
    else:
        cartopy = None
        fig, ax = plt.subplots(1, 1)
        ax.grid()
    return plt, fig, ax, cartopy
示例#2
0
文件: rng.py 项目: subond/parcels
 def lib(self, compiler=GNUCompiler()):
     if self._lib is None:
         with open(self.src_file, 'w') as f:
             f.write(self.ccode)
         compiler.compile(self.src_file, self.lib_file, self.log_file)
         logger.info("Compiled %s ==> %s" % ("random", self.lib_file))
         self._lib = npct.load_library(self.lib_file, '.')
     return self._lib
示例#3
0
    def __init__(self, fieldset, ptype, pyfunc=None, funcname=None,
                 funccode=None, py_ast=None, funcvars=None):
        self.fieldset = fieldset
        self.ptype = ptype

        # Derive meta information from pyfunc, if not given
        self.funcname = funcname or pyfunc.__name__
        if pyfunc is AdvectionRK4_3D:
            logger.info('Note that positive vertical velocity is assumed DOWNWARD by AdvectionRK4_3D')
        if funcvars is not None:
            self.funcvars = funcvars
        elif hasattr(pyfunc, '__code__'):
            self.funcvars = list(pyfunc.__code__.co_varnames)
        else:
            self.funcvars = None
        self.funccode = funccode or inspect.getsource(pyfunc.__code__)
        # Parse AST if it is not provided explicitly
        self.py_ast = py_ast or parse(fix_indentation(self.funccode)).body[0]
        if pyfunc is None:
            # Extract user context by inspecting the call stack
            stack = inspect.stack()
            try:
                user_ctx = stack[-1][0].f_globals
                user_ctx['math'] = globals()['math']
                user_ctx['random'] = globals()['random']
                user_ctx['ErrorCode'] = globals()['ErrorCode']
            except:
                logger.warning("Could not access user context when merging kernels")
                user_ctx = globals()
            finally:
                del stack  # Remove cyclic references
            # Compile and generate Python function from AST
            py_mod = Module(body=[self.py_ast])
            exec(compile(py_mod, "<ast>", "exec"), user_ctx)
            self.pyfunc = user_ctx[self.funcname]
        else:
            self.pyfunc = pyfunc
        self.name = "%s%s" % (ptype.name, self.funcname)

        # Generate the kernel function and add the outer loop
        if self.ptype.uses_jit:
            kernelgen = KernelGenerator(fieldset, ptype)
            self.field_args = kernelgen.field_args
            kernel_ccode = kernelgen.generate(deepcopy(self.py_ast),
                                              self.funcvars)
            self.field_args = kernelgen.field_args
            self.const_args = kernelgen.const_args
            loopgen = LoopGenerator(fieldset, ptype)
            self.ccode = loopgen.generate(self.funcname, self.field_args, self.const_args,
                                          kernel_ccode)

            basename = path.join(get_cache_dir(), self._cache_key)
            self.src_file = "%s.c" % basename
            self.lib_file = "%s.%s" % (basename, 'dll' if platform == 'win32' else 'so')
            self.log_file = "%s.log" % basename
        self._lib = None
示例#4
0
    def write(self, filename):
        """Write FieldSet to NetCDF file using NEMO convention

        :param filename: Basename of the output fileset"""
        logger.info("Generating NEMO FieldSet output with basename: %s" % filename)

        self.U.write(filename, varname='vozocrtx')
        self.V.write(filename, varname='vomecrty')

        for v in self.fields:
            if (v.name is not 'U') and (v.name is not 'V'):
                v.write(filename)
示例#5
0
 def compile(self, compiler):
     """ Writes kernel code to file and compiles it."""
     with open(self.src_file, 'w') as f:
         f.write(self.ccode)
     compiler.compile(self.src_file, self.lib_file, self.log_file)
     logger.info("Compiled %s ==> %s" % (self.name, self.lib_file))
示例#6
0
    def show(self,
             with_particles=False,
             animation=False,
             show_time=0,
             vmin=None,
             vmax=None):
        """Method to 'show' a :class:`Field` using matplotlib

        :param with_particles: Boolean whether particles are also plotted on Field
        :param animation: Boolean whether result is a single plot, or an animation
        :param show_time: Time at which to show the Field (only in single-plot mode)
        :param vmin: minimum colour scale (only in single-plot mode)
        :param vmax: maximum colour scale (only in single-plot mode)
        """
        try:
            import matplotlib.pyplot as plt
            import matplotlib.animation as animation_plt
            from matplotlib import rc
        except:
            logger.info("Visualisation is not possible. Matplotlib not found.")
            return

        if with_particles or (not animation):
            idx = self.time_index(show_time)
            if self.time.size > 1:
                data = np.squeeze(
                    self.temporal_interpolate_fullfield(idx, show_time))
            else:
                data = np.squeeze(self.data)

            vmin = data.min() if vmin is None else vmin
            vmax = data.max() if vmax is None else vmax
            cs = plt.contourf(self.lon,
                              self.lat,
                              data,
                              levels=np.linspace(vmin, vmax, 256))
            cs.cmap.set_over('k')
            cs.cmap.set_under('w')
            cs.set_clim(vmin, vmax)
            plt.colorbar(cs)
            if not with_particles:
                plt.show()
        else:
            fig = plt.figure()
            ax = plt.axes(xlim=(self.lon[0], self.lon[-1]),
                          ylim=(self.lat[0], self.lat[-1]))

            def animate(i):
                data = np.squeeze(self.data[i, :, :])
                cont = ax.contourf(self.lon,
                                   self.lat,
                                   data,
                                   levels=np.linspace(data.min(), data.max(),
                                                      256))
                return cont

            rc('animation', html='html5')
            anim = animation_plt.FuncAnimation(fig,
                                               animate,
                                               frames=np.arange(
                                                   1, self.data.shape[0]),
                                               interval=100,
                                               blit=False)
            plt.close()
            return anim
def compute_curvilinearGrid_rotationAngles(mesh_filename,
                                           rotation_angles_filename,
                                           variables=None,
                                           dimensions=None):
    """Function that computes and writes in a netcdf file the rotation angles for vector fields written
       in curvilinear C grids to zonal/meridional directions. It follows the NEMO standards.
       The angles are not directly computed since it is unnecessary and more expensive, but the cosine and sine
       of the angles are given for both the U and the V grid.

       Two important comments must be pointed out:
       * The rotation file is only computed if it does not exist or if it is older than the mesh file.
         Otherwise, this function will be skipped, even if variables and dimensions arguments are modified.
       * Since the rotation angles for a node are computed using the position of the node and its neighbouring nodes,
         the grid of the rotation angles is smaller than the original mesh grid. First row and line of mesh grid do not
         exist in rotation file.

    :param mesh_filename: path to the mesh file which contains the coordinates of the U, V and F grids
    :param rotation_angles_filename: path of the rotation angles file to write
    :param variables: optional dictionary of the names for the `cosU`, `sinU`, `cosV` and `sinV` variables in the rotation ncfile.
    :param dimensions: optional dictionary of dictionaries. The main dictionary contains the keys `U`, `V` and `F`.
                       In each subdictionary, the keys `lon` and `lat` give the name of the dimensions in the mesh ncfile.
    """

    if path.isfile(rotation_angles_filename) and path.getmtime(
            rotation_angles_filename) > path.getmtime(mesh_filename):
        logger.info(
            "file '%s' not generated since it is newer than '%s'.\n      If you want to re-generate it, please remove existing file first."
            % (rotation_angles_filename, mesh_filename))
        return

    logger.info("Generating rotation angles fields in file: %s" %
                rotation_angles_filename)

    if variables is None:
        variables = {
            'cosU': 'cosU',
            'sinU': 'sinU',
            'cosV': 'cosV',
            'sinV': 'sinV'
        }
    if dimensions is None:
        dimensions = {
            'U': {
                'lon': 'glamu',
                'lat': 'gphiu'
            },
            'V': {
                'lon': 'glamv',
                'lat': 'gphiv'
            },
            'F': {
                'lon': 'glamf',
                'lat': 'gphif'
            }
        }

    dataset = xr.open_dataset(mesh_filename, decode_times=False)
    lonU = np.squeeze(getattr(dataset, dimensions['U']['lon']).values)
    latU = np.squeeze(getattr(dataset, dimensions['U']['lat']).values)
    lonV = np.squeeze(getattr(dataset, dimensions['V']['lon']).values)
    latV = np.squeeze(getattr(dataset, dimensions['V']['lat']).values)
    lonF = np.squeeze(getattr(dataset, dimensions['F']['lon']).values)
    latF = np.squeeze(getattr(dataset, dimensions['F']['lat']).values)
    dataset.close()

    rad = np.pi / 180.
    rpi = np.pi

    # The following code is the direct python transcription of the Fortran code of NEMO.
    # http://forge.ipsl.jussieu.fr/nemo/browser/branches/2015/nemo_v3_6_STABLE/NEMOGCM/NEMO/OPA_SRC/SBC/geo2ocean.F90

    zxnpu = -2. * np.cos(
        rad * lonU[1:, 1:]) * np.tan(rpi / 4. - rad * latU[1:, 1:] / 2.)
    zynpu = -2. * np.sin(
        rad * lonU[1:, 1:]) * np.tan(rpi / 4. - rad * latU[1:, 1:] / 2.)
    znnpu = zxnpu * zxnpu + zynpu * zynpu

    zxnpv = -2. * np.cos(
        rad * lonV[1:, 1:]) * np.tan(rpi / 4. - rad * latV[1:, 1:] / 2.)
    zynpv = -2. * np.sin(
        rad * lonV[1:, 1:]) * np.tan(rpi / 4. - rad * latV[1:, 1:] / 2.)
    znnpv = zxnpv * zxnpv + zynpv * zynpv

    zxffu = 2. * np.cos(rad*lonF[1:, 1:]) * np.tan(rpi/4. - rad*latF[1:, 1:]/2.) \
        - 2. * np.cos(rad*lonF[:-1, 1:]) * np.tan(rpi/4. - rad*latF[:-1, 1:]/2.)
    zyffu = 2. * np.sin(rad*lonF[1:, 1:]) * np.tan(rpi/4. - rad*latF[1:, 1:]/2.) \
        - 2. * np.sin(rad*lonF[:-1, 1:]) * np.tan(rpi/4. - rad*latF[:-1, 1:]/2.)
    znffu = np.sqrt(znnpu * (zxffu * zxffu + zyffu * zyffu))
    znffu = np.maximum(znffu, 1.e-14)

    zxffv = 2. * np.cos(rad*lonF[1:, 1:]) * np.tan(rpi/4. - rad*latF[1:, 1:]/2.) \
        - 2. * np.cos(rad*lonF[1:, :-1]) * np.tan(rpi/4. - rad*latF[1:, :-1]/2.)
    zyffv = 2. * np.sin(rad*lonF[1:, 1:]) * np.tan(rpi/4. - rad*latF[1:, 1:]/2.) \
        - 2. * np.sin(rad*lonF[1:, :-1]) * np.tan(rpi/4. - rad*latF[1:, :-1]/2.)
    znffv = np.sqrt(znnpv * (zxffv * zxffv + zyffv * zyffv))
    znffv = np.maximum(znffv, 1.e-14)

    gsinu = (zxnpu * zyffu - zynpu * zxffu) / znffu
    gcosu = (zxnpu * zxffu + zynpu * zyffu) / znffu
    gsinv = (zxnpv * zxffv + zynpv * zyffv) / znffv
    gcosv = -(zxnpv * zyffv - zynpv * zxffv) / znffv

    # ** netCDF4 writing, since xArray is bugged **
    lonU = lonU[1:, 1:]
    latU = latU[1:, 1:]
    lonV = lonV[1:, 1:]
    latV = latV[1:, 1:]

    subDataset = Dataset(rotation_angles_filename, 'w', format='NETCDF4')
    subDataset.source = 'parcels_compute_curvilinearGrid_rotationAngles'
    subDataset.createDimension('x', lonU.shape[1])
    subDataset.createDimension('y', lonU.shape[0])
    lonUVar = subDataset.createVariable(dimensions['U']['lon'], 'f8', (
        'y',
        'x',
    ))
    latUVar = subDataset.createVariable(dimensions['U']['lat'], 'f8', (
        'y',
        'x',
    ))
    lonUVar.valid_min = np.min(lonU)
    lonUVar.valid_max = np.max(lonU)
    lonUVar[:] = lonU
    latUVar[:] = latU
    lonVVar = subDataset.createVariable(dimensions['V']['lon'], 'f8', (
        'y',
        'x',
    ))
    latVVar = subDataset.createVariable(dimensions['V']['lat'], 'f8', (
        'y',
        'x',
    ))
    lonVVar.valid_min = np.min(lonV)
    lonVVar.valid_max = np.max(lonV)
    lonVVar[:] = lonV
    latVVar[:] = latV

    cosUVar = subDataset.createVariable(variables['cosU'], 'f8', (
        'y',
        'x',
    ))
    cosUVar[:] = gcosu
    sinUVar = subDataset.createVariable(variables['sinU'], 'f8', (
        'y',
        'x',
    ))
    sinUVar[:] = gsinu
    cosVVar = subDataset.createVariable(variables['cosV'], 'f8', (
        'y',
        'x',
    ))
    cosVVar[:] = gcosv
    sinVVar = subDataset.createVariable(variables['sinV'], 'f8', (
        'y',
        'x',
    ))
    sinVVar[:] = gsinv

    subDataset.close()
示例#8
0
    def show(self, particles=True, show_time=None, field=True, domain=None,
             land=False, vmin=None, vmax=None, savefile=None):
        """Method to 'show' a Parcels ParticleSet

        :param particles: Boolean whether to show particles
        :param show_time: Time at which to show the ParticleSet
        :param field: Field to plot under particles (either True, a Field object, or 'vector')
        :param domain: Four-vector (latN, latS, lonE, lonW) defining domain to show
        :param land: Boolean whether to show land (in field='vector' mode only)
        :param vmin: minimum colour scale (only in single-plot mode)
        :param vmax: maximum colour scale (only in single-plot mode)
        :param savefile: Name of a file to save the plot to
        """
        try:
            import matplotlib.pyplot as plt
        except:
            logger.info("Visualisation is not possible. Matplotlib not found.")
            return
        try:
            from mpl_toolkits.basemap import Basemap
        except:
            Basemap = None

        plon = np.array([p.lon for p in self])
        plat = np.array([p.lat for p in self])
        show_time = self[0].time if show_time is None else show_time
        if isinstance(show_time, datetime):
            show_time = (show_time - self.fieldset.U.time_origin).total_seconds()
        if isinstance(show_time, delta):
            show_time = show_time.total_seconds()
        if domain is not None:
            latN = nearest_index(self.fieldset.U.lat, domain[0])
            latS = nearest_index(self.fieldset.U.lat, domain[1])
            lonE = nearest_index(self.fieldset.U.lon, domain[2])
            lonW = nearest_index(self.fieldset.U.lon, domain[3])
        else:
            latN, latS, lonE, lonW = (-1, 0, -1, 0)
        if field is not 'vector':
            plt.ion()
            plt.clf()
            if particles:
                plt.plot(np.transpose(plon), np.transpose(plat), 'ko')
            if field is True:
                axes = plt.gca()
                axes.set_xlim([self.fieldset.U.lon[lonW], self.fieldset.U.lon[lonE]])
                axes.set_ylim([self.fieldset.U.lat[latS], self.fieldset.U.lat[latN]])
                namestr = ''
                time_origin = self.fieldset.U.time_origin
            else:
                if not isinstance(field, Field):
                    field = getattr(self.fieldset, field)
                field.show(with_particles=True, show_time=show_time, vmin=vmin, vmax=vmax)
                namestr = field.name
                time_origin = field.time_origin
            if time_origin is 0:
                timestr = ' after ' + str(delta(seconds=show_time)) + ' hours'
            else:
                timestr = ' on ' + str(time_origin + delta(seconds=show_time))
            xlbl = 'Zonal distance [m]' if type(self.fieldset.U.units) is UnitConverter else 'Longitude [degrees]'
            ylbl = 'Meridional distance [m]' if type(self.fieldset.U.units) is UnitConverter else 'Latitude [degrees]'
            plt.xlabel(xlbl)
            plt.ylabel(ylbl)
        elif Basemap is None:
            logger.info("Visualisation is not possible. Basemap not found.")
            time_origin = self.fieldset.U.time_origin
        else:
            time_origin = self.fieldset.U.time_origin
            (idx, periods) = self.fieldset.U.time_index(show_time)
            show_time -= periods*(self.fieldset.U.time[-1]-self.fieldset.U.time[0])
            U = np.array(self.fieldset.U.temporal_interpolate_fullfield(idx, show_time))
            V = np.array(self.fieldset.V.temporal_interpolate_fullfield(idx, show_time))
            lon = self.fieldset.U.lon
            lat = self.fieldset.U.lat
            lon = lon[lonW:lonE]
            lat = lat[latS:latN]
            U = U[latS:latN, lonW:lonE]
            V = V[latS:latN, lonW:lonE]

            # configuring plot
            lat_median = np.median(lat)
            lon_median = np.median(lon)
            plt.figure()
            m = Basemap(projection='merc', lat_0=lat_median, lon_0=lon_median,
                        resolution='h', area_thresh=100,
                        llcrnrlon=lon[0], llcrnrlat=lat[0],
                        urcrnrlon=lon[-1], urcrnrlat=lat[-1])
            if land:
                m.drawcoastlines()
                m.fillcontinents(color='burlywood')
            parallels = np.arange(lat[0], lat[-1], abs(lat[0]-lat[-1])/5)
            parallels = np.around(parallels, 2)
            m.drawparallels(parallels, labels=[1, 0, 0, 0])
            meridians = np.arange(lon[0], lon[-1], abs(lon[0]-lon[-1])/5)
            meridians = np.around(meridians, 2)
            m.drawmeridians(meridians, labels=[0, 0, 0, 1])

            # formating velocity data for quiver plotting
            U = np.array([U[y, x] for x in range(len(lon)) for y in range(len(lat))])
            V = np.array([V[y, x] for x in range(len(lon)) for y in range(len(lat))])
            speed = np.sqrt(U**2 + V**2)
            normU = U/speed
            normV = V/speed
            x = np.repeat(lon, len(lat))
            y = np.tile(lat, len(lon))

            # plotting velocity vector field
            vecs = m.quiver(x, y, normU, normV, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50, latlon=True)
            m.colorbar(vecs, "right", size="5%", pad="2%")
            # plotting particle data
            if particles:
                xs, ys = m(plon, plat)
                m.scatter(xs, ys, color='black')

        if time_origin is 0:
            timestr = ' after ' + str(delta(seconds=show_time)) + ' hours'
        else:
            timestr = ' on ' + str(time_origin + delta(seconds=show_time))

        if particles:
            if field:
                plt.title('Particles' + timestr)
            elif field is 'vector':
                plt.title('Particles and velocity field' + timestr)
            else:
                plt.title('Particles and '+namestr + timestr)
        else:
            if field is 'vector':
                plt.title('Velocity field' + timestr)
            else:
                plt.title(namestr + timestr)

        if savefile is None:
            plt.show()
            plt.pause(0.0001)
        else:
            plt.savefig(savefile)
            logger.info('Plot saved to '+savefile+'.png')
            plt.close()
示例#9
0
def plotfield(field, show_time=None, domain=None, projection=None, land=True,
              vmin=None, vmax=None, savefile=None, **kwargs):
    """Function to plot a Parcels Field

    :param show_time: Time at which to show the Field
    :param domain: Four-vector (latN, latS, lonE, lonW) defining domain to show
    :param projection: type of cartopy projection to use (default PlateCarree)
    :param land: Boolean whether to show land. This is ignored for flat meshes
    :param vmin: minimum colour scale (only in single-plot mode)
    :param vmax: maximum colour scale (only in single-plot mode)
    :param savefile: Name of a file to save the plot to
    :param animation: Boolean whether result is a single plot, or an animation
    """

    if type(field) is VectorField:
        spherical = True if field.U.grid.mesh == 'spherical' else False
        field = [field.U, field.V]
        plottype = 'vector'
    elif type(field) is Field:
        spherical = True if field.grid.mesh == 'spherical' else False
        field = [field]
        plottype = 'scalar'
    else:
        raise RuntimeError('field needs to be a Field or VectorField object')

    plt, fig, ax, cartopy = create_parcelsfig_axis(spherical, land, projection=projection)
    if plt is None:
        return None, None, None, None  # creating axes was not possible

    data = {}
    plotlon = {}
    plotlat = {}
    for i, fld in enumerate(field):
        show_time = fld.grid.time[0] if show_time is None else show_time
        if fld.grid.defer_load:
            fld.fieldset.computeTimeChunk(show_time, 1)
        (idx, periods) = fld.time_index(show_time)
        show_time -= periods * (fld.grid.time[-1] - fld.grid.time[0])

        latN, latS, lonE, lonW = parsedomain(domain, fld)
        if isinstance(fld.grid, CurvilinearGrid):
            plotlon[i] = fld.grid.lon[latS:latN, lonW:lonE]
            plotlat[i] = fld.grid.lat[latS:latN, lonW:lonE]
        else:
            plotlon[i] = fld.grid.lon[lonW:lonE]
            plotlat[i] = fld.grid.lat[latS:latN]
        if i > 0 and not np.allclose(plotlon[i], plotlon[0]):
            raise RuntimeError('VectorField needs to be on an A-grid for plotting')
        if fld.grid.time.size > 1:
            data[i] = np.squeeze(fld.temporal_interpolate_fullfield(idx, show_time))[latS:latN, lonW:lonE]
        else:
            data[i] = np.squeeze(fld.data)[latS:latN, lonW:lonE]

    if plottype is 'vector':
        spd = data[0] ** 2 + data[1] ** 2
        speed = np.sqrt(spd, where=spd > 0)
        vmin = speed.min() if vmin is None else vmin
        vmax = speed.max() if vmax is None else vmax
        if isinstance(field[0].grid, CurvilinearGrid):
            x, y = plotlon[0], plotlat[0]
        else:
            x, y = np.meshgrid(plotlon[0], plotlat[0])
        nonzerospd = speed != 0
        u, v = (np.zeros_like(data[0]) * np.nan, np.zeros_like(data[1]) * np.nan)
        np.place(u, nonzerospd, data[0][nonzerospd] / speed[nonzerospd])
        np.place(v, nonzerospd, data[1][nonzerospd] / speed[nonzerospd])
        if cartopy:
            cs = ax.quiver(x, y, u, v, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50, transform=cartopy.crs.PlateCarree())
        else:
            cs = ax.quiver(x, y, u, v, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50)
    else:
        vmin = data[0].min() if vmin is None else vmin
        vmax = data[0].max() if vmax is None else vmax
        if cartopy:
            cs = ax.pcolormesh(plotlon[0], plotlat[0], data[0], transform=cartopy.crs.PlateCarree())
        else:
            cs = ax.pcolormesh(plotlon[0], plotlat[0], data[0])

    if cartopy is None or projection is None:
        ax.set_xlim(np.nanmin(plotlon[0]), np.nanmax(plotlon[0]))
        ax.set_ylim(np.nanmin(plotlat[0]), np.nanmax(plotlat[0]))
    elif domain is not None:
        ax.set_extent([np.nanmin(plotlon[0]), np.nanmax(plotlon[0]), np.nanmin(plotlat[0]), np.nanmax(plotlat[0])])
    cs.cmap.set_over('k')
    cs.cmap.set_under('w')
    cs.set_clim(vmin, vmax)

    cartopy_colorbar(cs, plt, fig, ax)

    timestr = parsetimestr(field[0].grid.time_origin, show_time)
    titlestr = kwargs.pop('titlestr', '')
    if plottype is 'vector':
        ax.set_title(titlestr + 'Velocity field' + timestr)
    else:
        ax.set_title(titlestr + field[0].name + timestr)

    if not spherical:
        ax.set_xlabel('Zonal distance [m]')
        ax.set_ylabel('Meridional distance [m]')

    plt.draw()

    if savefile:
        plt.savefig(savefile)
        logger.info('Plot saved to ' + savefile + '.png')
        plt.close()

    return plt, fig, ax, cartopy
示例#10
0
def plotparticles(particles, with_particles=True, show_time=None, field=None, domain=None, projection=None,
                  land=True, vmin=None, vmax=None, savefile=None, animation=False):
    """Function to plot a Parcels ParticleSet

    :param show_time: Time at which to show the ParticleSet
    :param with_particles: Boolean whether particles are also plotted on Field
    :param field: Field to plot under particles (either None, a Field object, or 'vector')
    :param domain: Four-vector (latN, latS, lonE, lonW) defining domain to show
    :param projection: type of cartopy projection to use (default PlateCarree)
    :param land: Boolean whether to show land. This is ignored for flat meshes
    :param vmin: minimum colour scale (only in single-plot mode)
    :param vmax: maximum colour scale (only in single-plot mode)
    :param savefile: Name of a file to save the plot to
    :param animation: Boolean whether result is a single plot, or an animation
    """

    show_time = particles[0].time if show_time is None else show_time
    if isinstance(show_time, datetime):
        show_time = np.datetime64(show_time)
    if isinstance(show_time, np.datetime64):
        if not particles.time_origin:
            raise NotImplementedError(
                'If fieldset.time_origin is not a date, showtime cannot be a date in particleset.show()')
        show_time = (show_time - particles.time_origin) / np.timedelta64(1, 's')
    if isinstance(show_time, delta):
        show_time = show_time.total_seconds()
    if np.isnan(show_time):
        show_time, _ = particles.fieldset.gridset.dimrange('time_full')

    if field is None:
        spherical = True if particles.fieldset.U.grid.mesh == 'spherical' else False
        plt, fig, ax, cartopy = create_parcelsfig_axis(spherical, land, projection)
        if plt is None:
            return  # creating axes was not possible
        ax.set_title('Particles' + parsetimestr(particles.fieldset.U.grid.time_origin, show_time))
        latN, latS, lonE, lonW = parsedomain(domain, particles.fieldset.U)
        if cartopy is None or projection is None:
            if domain is not None:
                if isinstance(particles.fieldset.U.grid, CurvilinearGrid):
                    ax.set_xlim(particles.fieldset.U.grid.lon[latS, lonW], particles.fieldset.U.grid.lon[latN, lonE])
                    ax.set_ylim(particles.fieldset.U.grid.lat[latS, lonW], particles.fieldset.U.grid.lat[latN, lonE])
                else:
                    ax.set_xlim(particles.fieldset.U.grid.lon[lonW], particles.fieldset.U.grid.lon[lonE])
                    ax.set_ylim(particles.fieldset.U.grid.lat[latS], particles.fieldset.U.grid.lat[latN])
            else:
                ax.set_xlim(np.nanmin(particles.fieldset.U.grid.lon), np.nanmax(particles.fieldset.U.grid.lon))
                ax.set_ylim(np.nanmin(particles.fieldset.U.grid.lat), np.nanmax(particles.fieldset.U.grid.lat))
        elif domain is not None:
            if isinstance(particles.fieldset.U.grid, CurvilinearGrid):
                ax.set_extent([particles.fieldset.U.grid.lon[latS, lonW], particles.fieldset.U.grid.lon[latN, lonE],
                               particles.fieldset.U.grid.lat[latS, lonW], particles.fieldset.U.grid.lat[latN, lonE]])
            else:
                ax.set_extent([particles.fieldset.U.grid.lon[lonW], particles.fieldset.U.grid.lon[lonE],
                               particles.fieldset.U.grid.lat[latS], particles.fieldset.U.grid.lat[latN]])

    else:
        if field is 'vector':
            field = particles.fieldset.UV
        elif not isinstance(field, Field):
            field = getattr(particles.fieldset, field)

        plt, fig, ax, cartopy = plotfield(field=field, animation=animation, show_time=show_time, domain=domain,
                                          projection=projection, land=land, vmin=vmin, vmax=vmax, savefile=None,
                                          titlestr='Particles and ')
        if plt is None:
            return  # creating axes was not possible

    if with_particles:
        plon = np.array([p.lon for p in particles])
        plat = np.array([p.lat for p in particles])
        if cartopy:
            ax.scatter(plon, plat, s=20, color='black', zorder=20, transform=cartopy.crs.PlateCarree())
        else:
            ax.scatter(plon, plat, s=20, color='black', zorder=20)

    if animation:
        plt.draw()
        plt.pause(0.0001)
    elif savefile is None:
        plt.show()
    else:
        plt.savefig(savefile)
        logger.info('Plot saved to ' + savefile + '.png')
        plt.close()