def data_kind(data, x=None, y=None, z=None): """ Check what kind of data is provided to a module. Possible types: * a file name provided as 'data' * an xarray.DataArray provided as 'data' * a matrix provided as 'data' * 1D arrays x and y (and z, optionally) Arguments should be ``None`` if not used. If doesn't fit any of these categories (or fits more than one), will raise an exception. Parameters ---------- data : str, xarray.DataArray, 2d array, or None Data file name, xarray.DataArray or numpy array. x/y : 1d arrays or None x and y columns as numpy arrays. z : 1d array or None z column as numpy array. To be used optionally when x and y are given. Returns ------- kind : str One of: ``'file'``, ``'grid'``, ``'matrix'``, ``'vectors'``. Examples -------- >>> import numpy as np >>> import xarray as xr >>> data_kind(data=None, x=np.array([1, 2, 3]), y=np.array([4, 5, 6])) 'vectors' >>> data_kind(data=np.arange(10).reshape((5, 2)), x=None, y=None) 'matrix' >>> data_kind(data="my-data-file.txt", x=None, y=None) 'file' >>> data_kind(data=xr.DataArray(np.random.rand(4, 3))) 'grid' """ if data is None and x is None and y is None: raise GMTInvalidInput("No input data provided.") if data is not None and (x is not None or y is not None or z is not None): raise GMTInvalidInput("Too much data. Use either data or x and y.") if data is None and (x is None or y is None): raise GMTInvalidInput("Must provided both x and y.") if isinstance(data, str): kind = "file" elif isinstance(data, xr.DataArray): kind = "grid" elif data is not None: kind = "matrix" else: kind = "vectors" return kind
def _blockm(block_method, table, outfile, **kwargs): r""" Block average (x,y,z) data tables by mean or median estimation. Reads arbitrarily located (x,y,z) triples [or optionally weighted quadruples (x,y,z,w)] from a table and writes to the output a mean or median (depending on ``block_method``) position and value for every non-empty block in a grid region defined by the ``region`` and ``spacing`` parameters. Parameters ---------- block_method : str Name of the GMT module to call. Must be "blockmean" or "blockmedian". Returns ------- output : pandas.DataFrame or None Return type depends on whether the ``outfile`` parameter is set: - :class:`pandas.DataFrame` table with (x, y, z) columns if ``outfile`` is not set - None if ``outfile`` is set (filtered output will be stored in file set by ``outfile``) """ kind = data_kind(table) with GMTTempFile(suffix=".csv") as tmpfile: with Session() as lib: if kind == "matrix": if not hasattr(table, "values"): raise GMTInvalidInput( f"Unrecognized data type: {type(table)}") file_context = lib.virtualfile_from_matrix(table.values) elif kind == "file": if outfile is None: raise GMTInvalidInput("Please pass in a str to 'outfile'") file_context = dummy_context(table) else: raise GMTInvalidInput(f"Unrecognized data type: {type(table)}") with file_context as infile: if outfile is None: outfile = tmpfile.name arg_str = " ".join( [infile, build_arg_string(kwargs), "->" + outfile]) lib.call_module(module=block_method, args=arg_str) # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame result = pd.read_csv(tmpfile.name, sep="\t", names=table.columns) elif outfile != tmpfile.name: # return None if outfile set, output in outfile result = None return result
def gtype(self, value): if value in (0, 1): self._gtype = value else: raise GMTInvalidInput( f"Invalid coordinate system type: {value}, should be a boolean of " "either 0 for Cartesian or 1 for Geographic")
def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwargs): r""" Plot legends on maps. Makes legends that can be overlaid on maps. Reads specific legend-related information from an input file, or automatically creates legend entries from plotted symbols that have labels. Unless otherwise noted, annotations will be made using the primary annotation font and size in effect (i.e., FONT_ANNOT_PRIMARY). Full option list at :gmt-docs:`legend.html` {aliases} Parameters ---------- spec : None or str Either ``None`` [default] for using the automatically generated legend specification file, or a *filename* pointing to the legend specification file. {J} {R} position : str [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\ **+w**\ *width*\ [/*height*]\ [**+j**\ *justify*]\ [**+l**\ *spacing*]\ [**+o**\ *dx*\ [/*dy*]]. Defines the reference point on the map for the legend. By default, uses **JTR**\ +\ **jTR**\ +\ **o**\ *0.2c* which places the legend at the top-right corner inside the map frame, with a 0.2 cm offset. box : bool or str [**+c**\ *clearances*][**+g**\ *fill*][**+i**\ [[*gap*/]\ *pen*]]\ [**+p**\ [*pen*]][**+r**\ [*radius*]][**+s**\ [[*dx*/*dy*/][*shade*]]]. Without further arguments, draws a rectangular border around the legend using :gmt-term:`MAP_FRAME_PEN`. By default, uses **+g**\ white\ **+p**\ 1p which draws a box around the legend using a 1p black pen and adds a white background. {V} {XY} {c} {p} {t} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access if "D" not in kwargs: kwargs["D"] = position if "F" not in kwargs: kwargs["F"] = box with Session() as lib: if spec is None: specfile = "" elif data_kind(spec) == "file": specfile = spec else: raise GMTInvalidInput("Unrecognized data type: {}".format(type(spec))) arg_str = " ".join([specfile, build_arg_string(kwargs)]) lib.call_module("legend", arg_str)
def registration(self, value): if value in (0, 1): self._registration = value else: raise GMTInvalidInput( f"Invalid grid registration value: {value}, should be a boolean of " "either 0 for Gridline registration or 1 for Pixel registration" )
def show(self, dpi=300, width=500, method="static"): """ Display a preview of the figure. Inserts the preview in the Jupyter notebook output. You will need to have IPython installed for this to work. You should have it if you are using the notebook. If ``method='external'``, makes PDF preview instead and opens it in the default viewer for your operating system (falls back to the default web browser). Note that the external viewer does not block the current process, so this won't work in a script. Parameters ---------- dpi : int The image resolution (dots per inch). width : int Width of the figure shown in the notebook in pixels. Ignored if ``method='external'``. method : str How the figure will be displayed. Options are (1) ``'static'``: PNG preview (default); (2) ``'external'``: PDF preview in an external program. Returns ------- img : IPython.display.Image Only if ``method != 'external'``. """ # Module level variable to know which figures had their show method # called. Needed for the sphinx-gallery scraper. SHOWED_FIGURES.append(self) if method not in ["static", "external"]: raise GMTInvalidInput("Invalid show method '{}'.".format(method)) if method == "external": pdf = self._preview(fmt="pdf", dpi=dpi, anti_alias=False, as_bytes=False) launch_external_viewer(pdf) img = None elif method == "static": png = self._preview(fmt="png", dpi=dpi, anti_alias=True, as_bytes=True, transparent=True) if Image is None: raise GMTError(" ".join([ "Cannot find IPython.", "Make sure you have it installed", "or use 'method=\"external\"' to open in an external viewer.", ])) img = Image(data=png, width=width) return img
def grdfill(grid, **kwargs): r""" Fill blank areas from a grid file. Read a grid that presumably has unfilled holes that the user wants to fill in some fashion. Holes are identified by NaN values but this criteria can be changed. There are several different algorithms that can be used to replace the hole values. Full option list at :gmt-docs:`grdfill.html` {aliases} Parameters ---------- grid : str or xarray.DataArray The file name of the input grid or the grid loaded as a DataArray. outgrid : str or None The name of the output netCDF file with extension .nc to store the grid in. mode : str Specify the hole-filling algorithm to use. Choose from **c** for constant fill and append the constant value, **n** for nearest neighbor (and optionally append a search radius in pixels [default radius is :math:`r^2 = \sqrt{{ X^2 + Y^2 }}`, where (*X,Y*) are the node dimensions of the grid]), or **s** for bicubic spline (optionally append a *tension* parameter [Default is no tension]). {R} {V} Returns ------- ret: xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - :class:`xarray.DataArray` if ``outgrid`` is not set - None if ``outgrid`` is set (grid output will be stored in file set by ``outgrid``) """ if "A" not in kwargs and "L" not in kwargs: raise GMTInvalidInput( "At least parameter 'mode' or 'L' must be specified.") with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with file_context as infile: if "G" not in kwargs: # if outgrid is unset, output to tempfile kwargs.update({"G": tmpfile.name}) outgrid = kwargs["G"] arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module("grdfill", arg_str) return load_dataarray(outgrid) if outgrid == tmpfile.name else None
def grdlandmask(**kwargs): r""" Create a grid file with set values for land and water. Read the selected shoreline database and create a grid to specify which nodes in the specified grid are over land or over water. The nodes defined by the selected region and lattice spacing will be set according to one of two criteria: (1) land vs water, or (2) the more detailed (hierarchical) ocean vs land vs lake vs island vs pond. Full option list at :gmt-docs:`grdlandmask.html` {aliases} Parameters ---------- outgrid : str or None The name of the output netCDF file with extension .nc to store the grid in. {I} {R} {r} Returns ------- ret: xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - :class:`xarray.DataArray` if ``outgrid`` is not set - None if ``outgrid`` is set (grid output will be stored in file set by ``outgrid``) """ if "I" not in kwargs.keys() or "R" not in kwargs.keys(): raise GMTInvalidInput("Both 'region' and 'spacing' must be specified.") with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: if "G" not in kwargs.keys( ): # if outgrid is unset, output to tempfile kwargs.update({"G": tmpfile.name}) outgrid = kwargs["G"] arg_str = build_arg_string(kwargs) lib.call_module("grdlandmask", arg_str) if outgrid == tmpfile.name: # if user did not set outgrid, return DataArray with xr.open_dataarray(outgrid) as dataarray: result = dataarray.load() _ = result.gmt # load GMTDataArray accessor information else: result = None # if user sets an outgrid, return None return result
def new_module(*args, **kwargs): """ New module that parses and replaces the registered aliases. """ for arg, alias in aliases.items(): if alias in kwargs and arg in kwargs: raise GMTInvalidInput( f"Arguments in short-form ({arg}) and long-form ({alias}) can't coexist" ) if alias in kwargs: kwargs[arg] = kwargs.pop(alias) return module_func(*args, **kwargs)
def basemap(self, **kwargs): r""" Plot base maps and frames for the figure. Creates a basic or fancy basemap with axes, fill, and titles. Several map projections are available, and the user may specify separate tick-mark intervals for boundary annotation, ticking, and [optionally] gridlines. A simple map scale or directional rose may also be plotted. At least one of the parameters ``frame``, ``map_scale``, ``rose`` or ``compass`` must be specified. Full option list at :gmt-docs:`basemap.html` {aliases} Parameters ---------- {J} zscale/zsize : float or str Set z-axis scaling or z-axis size. {R} *Required if this is the first plot command.* {B} map_scale : str [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\ **+w**\ *length*. Draws a simple map scale centered on the reference point specified. rose : str Draws a map directional rose on the map at the location defined by the reference and anchor points. compass : str Draws a map magnetic rose on the map at the location defined by the reference and anchor points {U} {V} {XY} {c} {f} {p} {t} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access if not args_in_kwargs(args=["B", "L", "Td", "Tm", "c"], kwargs=kwargs): raise GMTInvalidInput( "At least one of frame, map_scale, compass, rose, or panel must be specified." ) with Session() as lib: lib.call_module("basemap", build_arg_string(kwargs))
def new_module(*args, **kwargs): """ New module instance that converts old parameters to new parameters. """ if oldname in kwargs: if newname in kwargs: raise GMTInvalidInput( f"Can't provide both '{newname}' and '{oldname}'.") msg = ( f"The '{oldname}' parameter has been deprecated since {deprecate_version}" f" and will be removed in {remove_version}." f" Please use '{newname}' instead.") warnings.warn(msg, category=FutureWarning, stacklevel=2) kwargs[newname] = kwargs.pop(oldname) return module_func(*args, **kwargs)
def new_module(*args, **kwargs): """ New module that parses and replaces the registered aliases. """ for short_param, long_alias in aliases.items(): if long_alias in kwargs and short_param in kwargs: raise GMTInvalidInput( f"Parameters in short-form ({short_param}) and " f"long-form ({long_alias}) can't coexist.") if long_alias in kwargs: kwargs[short_param] = kwargs.pop(long_alias) elif short_param in kwargs: msg = ( f"Short-form parameter ({short_param}) is not recommended. " f"Use long-form parameter '{long_alias}' instead.") warnings.warn(msg, category=SyntaxWarning, stacklevel=2) return module_func(*args, **kwargs)
def set_display(method=None): """ Set the display method. Parameters ---------- method : str or None The method to display an image. Choose from: - **external**: PDF preview in an external program [default] - **notebook**: PNG preview [default in Jupyter notebooks] - **none**: Disable image preview """ if method in ["notebook", "external", "none"]: SHOW_CONFIG["method"] = method elif method is not None: raise GMTInvalidInput( (f"Invalid display mode '{method}', " "should be either 'notebook', 'external' or 'none'."))
def load_sample_data(name): """ Load an example dataset from the GMT server. The data are downloaded to a cache directory (usually ``~/.gmt/cache``) the first time you invoke this function. Afterwards, it will load the data from the cache. So you'll need an internet connection the first time around. Parameters ---------- name : str Name of the dataset to load. Returns ------- :class:`pandas.DataFrame` or :class:`xarray.DataArray` Sample dataset loaded as a pandas.DataFrame for tabular data or xarray.DataArray for raster data. See Also -------- list_sample_data : Report datasets available for tests and documentation examples. """ names = list_sample_data() if name not in names: raise GMTInvalidInput(f"Invalid dataset name '{name}'.") load_func = { "bathymetry": load_sample_bathymetry, "fractures": load_fractures_compilation, "hotspots": load_hotspots, "japan_quakes": load_japan_quakes, "mars_shape": load_mars_shape, "ocean_ridge_points": load_ocean_ridge_points, "usgs_quakes": load_usgs_quakes, } data = load_func[name](suppress_warning=True) return data
def grdinfo(grid, **kwargs): """ Get information about a grid. Can read the grid from a file or given as an xarray.DataArray grid. Full option list at :gmt-docs:`grdinfo.html` Parameters ---------- grid : str or xarray.DataArray The file name of the input grid or the grid loaded as a DataArray. {V} Returns ------- info : str A string with information about the grid. """ kind = data_kind(grid, None, None) with GMTTempFile() as outfile: with Session() as lib: if kind == "file": file_context = dummy_context(grid) elif kind == "grid": file_context = lib.virtualfile_from_grid(grid) else: raise GMTInvalidInput("Unrecognized data type: {}".format( type(grid))) with file_context as infile: arg_str = " ".join( [infile, build_arg_string(kwargs), "->" + outfile.name]) lib.call_module("grdinfo", arg_str) result = outfile.read() return result
def contour(self, x=None, y=None, z=None, data=None, **kwargs): r""" Contour table data by direct triangulation. Takes a matrix, (x,y,z) pairs, or a file name as input and plots lines, polygons, or symbols at those locations on a map. Must provide either ``data`` or ``x``/``y``/``z``. Full option list at :gmt-docs:`contour.html` {aliases} Parameters ---------- x/y/z : 1d arrays Arrays of x and y coordinates and values z of the data points. data : str or 2d array Either a data file name or a 2d numpy array with the tabular data. {J} {R} annotation : str or int Specify or disable annotated contour levels, modifies annotated contours specified in ``interval``. - Specify a fixed annotation interval *annot_int* or a single annotation level +\ *annot_int*. {B} levels : str or int Specify the contour lines to generate. - The filename of a CPT file where the color boundaries will be used as contour levels. - The filename of a 2 (or 3) column file containing the contour levels (col 1), (**C**)ontour or (**A**)nnotate (col 2), and optional angle (col 3) - A fixed contour interval *cont_int* or a single contour with +\ *cont_int* D : str Dump contour coordinates. E : str Network information. label_placement : str Placement of labels. I : bool Color the triangles using CPT. triangular_mesh_pen : str Pen to draw the underlying triangulation [Default is none]. no_clip : bool Do NOT clip contours or image at the boundaries [Default will clip to fit inside region]. Q : float or str [*cut*][**+z**]. Do not draw contours with less than cut number of points. skip : bool or str [**p**\|\ **t**]. Skip input points outside region. {W} label : str Add a legend entry for the contour being plotted. Normally, the annotated contour is selected for the legend. You can select the regular contour instead, or both of them, by considering the label to be of the format [*annotcontlabel*][/*contlabel*]. If either label contains a slash (/) character then use ``|`` as the separator for the two labels instead. {V} {XY} {c} {p} {t} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access kind = data_kind(data, x, y, z) if kind == "vectors" and z is None: raise GMTInvalidInput("Must provided both x, y, and z.") with Session() as lib: # Choose how data will be passed in to the module if kind == "file": file_context = dummy_context(data) elif kind == "matrix": file_context = lib.virtualfile_from_matrix(data) elif kind == "vectors": file_context = lib.virtualfile_from_vectors(x, y, z) with file_context as fname: arg_str = " ".join([fname, build_arg_string(kwargs)]) lib.call_module("contour", arg_str)
def kwargs_to_strings(**conversions): """ Decorator to convert given keyword arguments to strings. The strings are what GMT expects from command line arguments. Boolean arguments and None are not converted and will be processed in the ``build_arg_string`` function. You can also specify other conversions to specific arguments. Conversions available: * 'sequence': transforms a sequence (list, tuple) into a ``'/'`` separated string * 'sequence_comma': transforms a sequence into a ``','`` separated string * 'sequence_plus': transforms a sequence into a ``'+'`` separated string * 'sequence_space': transforms a sequence into a ``' '`` separated string Parameters ---------- conversions : keyword arguments Keyword arguments specifying other kinds of conversions that should be performed. The keyword is the name of the argument and the value is the conversion type (see list above). Examples -------- >>> @kwargs_to_strings( ... R="sequence", i="sequence_comma", files="sequence_space" ... ) ... def module(*args, **kwargs): ... "A module that prints the arguments it received" ... print("{", end="") ... print( ... ", ".join( ... "'{}': {}".format(k, repr(kwargs[k])) ... for k in sorted(kwargs) ... ), ... end="", ... ) ... print("}") ... if args: ... print("args:", " ".join("{}".format(x) for x in args)) >>> module(R=[1, 2, 3, 4]) {'R': '1/2/3/4'} >>> # It's already a string, do nothing >>> module(R="5/6/7/8") {'R': '5/6/7/8'} >>> module(P=True) {'P': True} >>> module(P=False) {'P': False} >>> module(P=None) {'P': None} >>> module(i=[1, 2]) {'i': '1,2'} >>> module(files=["data1.txt", "data2.txt"]) {'files': 'data1.txt data2.txt'} >>> # Other non-boolean arguments are passed along as they are >>> module(123, bla=(1, 2, 3), foo=True, A=False, i=(5, 6)) {'A': False, 'bla': (1, 2, 3), 'foo': True, 'i': '5,6'} args: 123 >>> import datetime >>> module( ... R=[ ... np.datetime64("2010-01-01T16:00:00"), ... datetime.datetime(2020, 1, 1, 12, 23, 45), ... ] ... ) {'R': '2010-01-01T16:00:00/2020-01-01T12:23:45.000000'} >>> import pandas as pd >>> import xarray as xr >>> module( ... R=[ ... xr.DataArray(data=np.datetime64("2005-01-01T08:00:00")), ... pd.Timestamp("2015-01-01T12:00:00.123456789"), ... ] ... ) {'R': '2005-01-01T08:00:00.000000000/2015-01-01T12:00:00.123456'} """ valid_conversions = [ "sequence", "sequence_comma", "sequence_plus", "sequence_space", ] for arg, fmt in conversions.items(): if fmt not in valid_conversions: raise GMTInvalidInput( "Invalid conversion type '{}' for argument '{}'.".format( fmt, arg)) separators = { "sequence": "/", "sequence_comma": ",", "sequence_plus": "+", "sequence_space": " ", } # Make the actual decorator function def converter(module_func): """ The decorator that creates our new function with the conversions. """ @functools.wraps(module_func) def new_module(*args, **kwargs): """ New module instance that converts the arguments first. """ for arg, fmt in conversions.items(): if arg in kwargs: value = kwargs[arg] issequence = fmt in separators if issequence and is_nonstr_iter(value): for index, item in enumerate(value): try: # check if there is a space " " when converting # a pandas.Timestamp/xr.DataArray to a string. # If so, use np.datetime_as_string instead. assert " " not in str(item) except AssertionError: # convert datetime-like item to ISO 8601 # string format like YYYY-MM-DDThh:mm:ss.ffffff value[index] = np.datetime_as_string( np.asarray(item, dtype=np.datetime64)) kwargs[arg] = separators[fmt].join(f"{item}" for item in value) # Execute the original function and return its output return module_func(*args, **kwargs) return new_module return converter
def grdfilter(grid, **kwargs): r""" Filter a grid in the space (or time) domain. Filter a grid file in the time domain using one of the selected convolution or non-convolution isotropic or rectangular filters and compute distances using Cartesian or Spherical geometries. The output grid file can optionally be generated as a sub-region of the input (via ``region``) and/or with new increment (via ``spacing``) or registration (via ``toggle``). In this way, one may have "extra space" in the input data so that the edges will not be used and the output can be within one half-width of the input edges. If the filter is low-pass, then the output may be less frequently sampled than the input. Full option list at :gmt-docs:`grdfilter.html` {aliases} Parameters ---------- grid : str or xarray.DataArray The file name of the input grid or the grid loaded as a DataArray. outgrid : str or None The name of the output netCDF file with extension .nc to store the grid in. filter : str **b**\|\ **c**\|\ **g**\|\ **o**\|\ **m**\|\ **p**\|\ **h**\ *xwidth*\ [/*width2*\][*modifiers*]. Name of filter type you which to apply, followed by the width: b: Box Car c: Cosine Arch g: Gaussian o: Operator m: Median p: Maximum Likelihood probability h: histogram distance : str Distance *flag* tells how grid (x,y) relates to filter width as follows: p: grid (px,py) with *width* an odd number of pixels; Cartesian distances. 0: grid (x,y) same units as *width*, Cartesian distances. 1: grid (x,y) in degrees, *width* in kilometers, Cartesian distances. 2: grid (x,y) in degrees, *width* in km, dx scaled by cos(middle y), Cartesian distances. The above options are fastest because they allow weight matrix to be computed only once. The next three options are slower because they recompute weights for each latitude. 3: grid (x,y) in degrees, *width* in km, dx scaled by cosine(y), Cartesian distance calculation. 4: grid (x,y) in degrees, *width* in km, Spherical distance calculation. 5: grid (x,y) in Mercator ``projection='m1'`` img units, *width* in km, Spherical distance calculation. spacing : str *xinc*\[\ *unit*\][**+e**\|\ **n**] [/*yinc*\ [*unit*][**+e**\|\ **n**]]. *xinc* [and optionally *yinc*] is the grid spacing. nans : str or float **i**\|\ **p**\|\ **r**. Determine how NaN-values in the input grid affects the filtered output. {R} toggle : bool Toggle the node registration for the output grid so as to become the opposite of the input grid. [Default gives the same registration as the input grid]. {V} Returns ------- ret: xarray.DataArray or None Return type depends on whether the ``outgrid`` parameter is set: - :class:`xarray.DataArray` if ``outgrid`` is not set - None if ``outgrid`` is set (grid output will be stored in file set by ``outgrid``) Examples -------- >>> import os >>> import pygmt >>> # Apply a filter of 600km (full width) to the @earth_relief_30m file >>> # and return a filtered field (saved as netcdf) >>> pygmt.grdfilter( ... grid="@earth_relief_30m", ... filter="m600", ... distance="4", ... region=[150, 250, 10, 40], ... spacing=0.5, ... outgrid="filtered_pacific.nc", ... ) >>> os.remove("filtered_pacific.nc") # cleanup file >>> # Apply a gaussian smoothing filter of 600 km in the input data array, >>> # and returns a filtered data array with the smoothed field. >>> grid = pygmt.datasets.load_earth_relief() >>> smooth_field = pygmt.grdfilter(grid=grid, filter="g600", distance="4") """ kind = data_kind(grid) with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: if kind == "file": file_context = dummy_context(grid) elif kind == "grid": file_context = lib.virtualfile_from_grid(grid) else: raise GMTInvalidInput("Unrecognized data type: {}".format( type(grid))) with file_context as infile: if "G" not in kwargs.keys( ): # if outgrid is unset, output to tempfile kwargs.update({"G": tmpfile.name}) outgrid = kwargs["G"] arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module("grdfilter", arg_str) if outgrid == tmpfile.name: # if user did not set outgrid, return DataArray with xr.open_dataarray(outgrid) as dataarray: result = dataarray.load() _ = result.gmt # load GMTDataArray accessor information else: result = None # if user sets an outgrid, return None return result
def grdtrack(points, grid, newcolname=None, outfile=None, **kwargs): r""" Sample grids at specified (x,y) locations. Reads one or more grid files and a table (from file or an array input; but see ``profile`` for exception) with (x,y) [or (lon,lat)] positions in the first two columns (more columns may be present). It interpolates the grid(s) at the positions in the table and writes out the table with the interpolated values added as (one or more) new columns. Alternatively (``crossprofile``), the input is considered to be line-segments and we create orthogonal cross-profiles at each data point or with an equidistant separation and sample the grid(s) along these profiles. A bicubic [Default], bilinear, B-spline or nearest-neighbor interpolation is used, requiring boundary conditions at the limits of the region (see ``interpolation``; Default uses "natural" conditions (second partial derivative normal to edge is zero) unless the grid is automatically recognized as periodic.) Full option list at :gmt-docs:`grdtrack.html` {aliases} Parameters ---------- points : str or {table-like} Pass in either a file name to an ASCII data table, a 2D {table-classes}. grid : xarray.DataArray or str Gridded array from which to sample values from, or a filename (netcdf format). newcolname : str Required if ``points`` is a :class:`pandas.DataFrame`. The name for the new column in the track :class:`pandas.DataFrame` table where the sampled values will be placed. outfile : str The file name for the output ASCII file. resample : str **f**\|\ **p**\|\ **m**\|\ **r**\|\ **R**\ [**+l**] For track resampling (if ``crossprofile`` or ``profile`` are set) we can select how this is to be performed. Append **f** to keep original points, but add intermediate points if needed [Default], **m** as **f**, but first follow meridian (along y) then parallel (along x), **p** as **f**, but first follow parallel (along y) then meridian (along x), **r** to resample at equidistant locations; input points are not necessarily included in the output, and **R** as **r**, but adjust given spacing to fit the track length exactly. Finally, append **+l** if geographic distances should be measured along rhumb lines (loxodromes) instead of great circles. Ignored unless ``crossprofile`` is used. crossprofile : str *length*/\ *ds*\ [*/spacing*][**+a**\|\ **+v**][**l**\|\ **r**]. Use input line segments to create an equidistant and (optionally) equally-spaced set of crossing profiles along which we sample the grid(s) [Default simply samples the grid(s) at the input locations]. Specify two length scales that control how the sampling is done: *length* sets the full length of each cross-profile, while *ds* is the sampling spacing along each cross-profile. Optionally, append **/**\ *spacing* for an equidistant spacing between cross-profiles [Default erects cross-profiles at the input coordinates]; see ``resample`` for how resampling the input track is controlled. By default, all cross-profiles have the same direction (left to right as we look in the direction of the input line segment). Append **+a** to alternate the direction of cross-profiles, or **v** to enforce either a "west-to-east" or "south-to-north" view. By default the entire profiles are output. Choose to only output the left or right halves of the profiles by appending **+l** or **+r**, respectively. Append suitable units to *length*; it sets the unit used for *ds* [and *spacing*] (See :gmt-docs:`Units <grdtrack.html#units>`). The default unit for geographic grids is meter while Cartesian grids implies the user unit. The output columns will be *lon*, *lat*, *dist*, *azimuth*, *z1*, *z2*, ..., *zn* (The *zi* are the sampled values for each of the *n* grids). dfile : str In concert with ``crossprofile`` we can save the (possibly resampled) original lines to *dfile* [Default only saves the cross-profiles]. The columns will be *lon*, *lat*, *dist*, *azimuth*, *z1*, *z2*, ... (sampled value for each grid). profile : str *line*\ [,\ *line*,...][**+a**\ *az*][**+c**][**+d**][**+g**]\ [**+i**\ *inc*][**+l**\ *length*][**+n**\ *np*][**+o**\ *az*]\ [**+r**\ *radius*]. Instead of reading input track coordinates, specify profiles via coordinates and modifiers. The format of each *line* is *start*/*stop*, where *start* or *stop* are either *lon*/*lat* (*x*/*y* for Cartesian data) or a 2-character XY key that uses the :gmt-docs:`text <text.html>`-style justification format to specify a point on the map as [LCR][BMT]. Each line will be a separate segment unless **+c** is used which will connect segments with shared joints into a single segment. In addition to line coordinates, you can use Z-, Z+ to mean the global minimum and maximum locations in the grid (only available if a single grid is given via **outfile**). You may append **+i**\ *inc* to set the sampling interval; if not given then we default to half the minimum grid interval. For a *line* along parallels or meridians you can add **+g** to report degrees of longitude or latitude instead of great circle distances starting at zero. Instead of two coordinates you can specify an origin and one of **+a**, **+o**, or **+r**. The **+a** sets the azimuth of a profile of given length starting at the given origin, while **+o** centers the profile on the origin; both require **+l**. For circular sampling specify **+r** to define a circle of given radius centered on the origin; this option requires either **+n** or **+i**. The **+n**\ *np* modifier sets the desired number of points, while **+l**\ *length* gives the total length of the profile. Append **+d** to output the along-track distances after the coordinates. **Note**: No track file will be read. Also note that only one distance unit can be chosen. Giving different units will result in an error. If no units are specified we default to great circle distances in km (if geographic). If working with geographic data you can use ``distcalc`` to control distance calculation mode [Default is Great Circle]. **Note**: If ``crossprofile`` is set and *spacing* is given then that sampling scheme overrules any modifier set in ``profile``. critical : str [**+b**][**+n**][**+r**][**+z**\ *z0*]. Find critical points along each cross-profile as a function of along-track distance. Requires ``crossprofile`` and a single input grid (*z*). We examine each cross-profile generated and report (*dist*, *lonc*, *latc*, *distc*, *azimuthc*, *zc*) at the center peak of maximum *z* value, (*lonl*, *latl*, *distl*) and (*lonr*, *latr*, *distr*) at the first and last non-NaN point whose *z*-value exceeds *z0*, respectively, and the *width* based on the two extreme points found. Here, *dist* is the distance along the original input ``points`` and the other 12 output columns are a function of that distance. When searching for the center peak and the extreme first and last values that exceed the threshold we assume the profile is positive up. If we instead are looking for a trough then you must use **+n** to temporarily flip the profile to positive. The threshold *z0* value is always given as >= 0; use **+z** to change it [Default is 0]. Alternatively, use **+b** to determine the balance point and standard deviation of the profile; this is the weighted mean and weighted standard deviation of the distances, with *z* acting as the weight. Finally, use **+r** to obtain the weighted rms about the cross-track center (*distc* == 0). **Note**: We round the exact results to the nearest distance nodes along the cross-profiles. We write 13 output columns per track: *dist, lonc, latc, distc, azimuthc, zc, lonl, latl, distl, lonr, latr, distr, width*. {R} no_skip : bool Do *not* skip points that fall outside the domain of the grid(s) [Default only output points within grid domain]. stack : str or list *method*/*modifiers*. In conjunction with ``crossprofile``, compute a single stacked profile from all profiles across each segment. Choose how stacking should be computed [Default method is **a**]: - **a** = mean (average) - **m** = median - **p** = mode (maximum likelihood) - **l** = lower - **L** = lower but only consider positive values - **u** = upper - **U** = upper but only consider negative values. The *modifiers* control the output; choose one or more among these choices: - **+a** : Append stacked values to all cross-profiles. - **+d** : Append stack deviations to all cross-profiles. - **+r** : Append data residuals (data - stack) to all cross-profiles. - **+s**\ [*file*] : Save stacked profile to *file* [Default filename is grdtrack_stacked_profile.txt]. - **+c**\ *fact* : Compute envelope on stacked profile as ±\ *fact* \*\ *deviation* [Default fact value is 2]. Notes: 1. Deviations depend on *method* and are st.dev (**a**), L1 scale, i.e., 1.4826 \* median absolute deviation (MAD) (for **m** and **p**), or half-range (upper-lower)/2. 2. The stacked profile file contains a leading column plus groups of 4-6 columns, with one group for each sampled grid. The leading column holds cross distance, while the first four columns in a group hold stacked value, deviation, min value, and max value, respectively. If *method* is one of **a**\|\ **m**\|\ **p** then we also write the lower and upper confidence bounds (see **+c**). When one or more of **+a**, **+d**, and **+r** are used then we also append the stacking results to the end of each row, for all cross-profiles. The order is always stacked value (**+a**), followed by deviations (**+d**) and finally residuals (**+r**). When more than one grid is sampled this sequence of 1-3 columns is repeated for each grid. radius : bool or int or float or str [*radius*][**+e**\|\ **p**]. To be used with normal grid sampling, and limited to a single, non-IMG grid. If the nearest node to the input point is NaN, search outwards until we find the nearest non-NaN node and report that value instead. Optionally specify a search radius which limits the consideration to points within this distance from the input point. To report the location of the nearest node and its distance from the input point, append **+e**. The default unit for geographic grid distances is spherical degrees. Use *radius* to change the unit and give *radius* = 0 if you do not want to limit the radius search. To instead replace the input point with the coordinates of the nearest node, append **+p**. {V} z_only : bool Only write out the sampled z-values [Default writes all columns]. {a} {b} {d} {e} {f} {g} {h} {i} {j} {n} {o} {s} {w} Returns ------- track: pandas.DataFrame or None Return type depends on whether the ``outfile`` parameter is set: - :class:`pandas.DataFrame` table with (x, y, ..., newcolname) if ``outfile`` is not set - None if ``outfile`` is set (track output will be stored in file set by ``outfile``) Example ------- >>> import pygmt >>> # Load a grid of @earth_relief_30m data, with an x-range of -118 to >>> # -107, and a y-range of -49 to -42 >>> grid = pygmt.datasets.load_earth_relief( ... resolution="30m", region=[-118, -107, -49, -42] ... ) >>> # Load a pandas dataframe with ocean ridge points >>> points = pygmt.datasets.load_sample_data(name="ocean_ridge_points") >>> # Create a pandas dataframe from an input grid and set of points >>> # The output dataframe adds a column named "bathymetry" >>> output_dataframe = pygmt.grdtrack( ... points=points, grid=grid, newcolname="bathymetry" ... ) """ if hasattr(points, "columns") and newcolname is None: raise GMTInvalidInput("Please pass in a str to 'newcolname'") with GMTTempFile(suffix=".csv") as tmpfile: with Session() as lib: # Choose how data will be passed into the module table_context = lib.virtualfile_from_data(check_kind="vector", data=points) # Store the xarray.DataArray grid in virtualfile grid_context = lib.virtualfile_from_data(check_kind="raster", data=grid) # Run grdtrack on the temporary (csv) points table # and (netcdf) grid virtualfile with table_context as csvfile: with grid_context as grdfile: kwargs.update({"G": grdfile}) if outfile is None: # Output to tmpfile if outfile is not set outfile = tmpfile.name lib.call_module( module="grdtrack", args=build_arg_string(kwargs, infile=csvfile, outfile=outfile), ) # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame try: column_names = points.columns.to_list() + [newcolname] result = pd.read_csv(tmpfile.name, sep="\t", names=column_names) except AttributeError: # 'str' object has no attribute 'columns' result = pd.read_csv(tmpfile.name, sep="\t", header=None, comment=">") elif outfile != tmpfile.name: # return None if outfile set, output in outfile result = None return result
def load_earth_age(resolution="01d", region=None, registration=None): r""" Load Earth seafloor crustal ages in various resolutions. The grids are downloaded to a user data directory (usually ``~/.gmt/server/earth/earth_age/``) the first time you invoke this function. Afterwards, it will load the grid from the data directory. So you'll need an internet connection the first time around. These grids can also be accessed by passing in the file name **@earth_age**\_\ *res*\[_\ *reg*] to any grid plotting/processing function. *res* is the grid resolution (see below), and *reg* is grid registration type (**p** for pixel registration or **g** for gridline registration). Refer to :gmt-docs:`datasets/remote-data.html#global-earth-seafloor-crustal-age-grids` for more details. Parameters ---------- resolution : str The grid resolution. The suffix ``d`` and ``m`` stand for arc-degree, arc-minute and arc-second. It can be ``'01d'``, ``'30m'``, ``'20m'``, ``'15m'``, ``'10m'``, ``'06m'``, ``'05m'``, ``'04m'``, ``'03m'``, ``'02m'``, or ``'01m'``. region : str or list The subregion of the grid to load, in the forms of a list [*xmin*, *xmax*, *ymin*, *ymax*] or a string *xmin/xmax/ymin/ymax*. Required for grids with resolutions higher than 5 arc-minute (i.e., ``05m``). registration : str Grid registration type. Either ``pixel`` for pixel registration or ``gridline`` for gridline registration. Default is ``None``, where a pixel-registered grid is returned unless only the gridline-registered grid is available. Returns ------- grid : :class:`xarray.DataArray` The Earth seafloor crustal age grid. Coordinates are latitude and longitude in degrees. Age is in millions of years (Myr). Notes ----- The :class:`xarray.DataArray` grid doesn't support slice operation, for Earth seafloor crustal age with resolutions of 5 arc-minutes or higher, which are stored as smaller tiles. """ # earth seafloor crust age data stored as single grids for low resolutions non_tiled_resolutions = ["01d", "30m", "20m", "15m", "10m", "06m"] # earth seafloor crust age data stored as tiles for high resolutions tiled_resolutions = ["05m", "04m", "03m", "02m", "01m"] if registration in ("pixel", "gridline", None): # If None, let GMT decide on Pixel/Gridline type reg = f"_{registration[0]}" if registration else "" else: raise GMTInvalidInput( f"Invalid grid registration: '{registration}', should be either " "'pixel', 'gridline' or None. Default is None, where a " "pixel-registered grid is returned unless only the " "gridline-registered grid is available.") if resolution not in non_tiled_resolutions + tiled_resolutions: raise GMTInvalidInput( f"Invalid Earth relief resolution '{resolution}'.") # Choose earth relief data prefix earth_age_prefix = "earth_age_" # different ways to load tiled and non-tiled earth relief data # Known issue: tiled grids don't support slice operation # See https://github.com/GenericMappingTools/pygmt/issues/524 if region is None: if resolution not in non_tiled_resolutions: raise GMTInvalidInput( f"'region' is required for Earth age resolution '{resolution}'." ) fname = which(f"@earth_age_{resolution}{reg}", download="a") grid = load_dataarray(fname, engine="netcdf4") else: grid = grdcut(f"@{earth_age_prefix}{resolution}{reg}", region=region) # Add some metadata to the grid grid.name = "seafloor_age" grid.attrs["long_name"] = "age of seafloor crust" grid.attrs["units"] = "Myr" grid.attrs["vertical_datum"] = "EMG96" grid.attrs["horizontal_datum"] = "WGS84" # Remove the actual range because it gets outdated when indexing the grid, # which causes problems when exporting it to netCDF for usage on the # command-line. grid.attrs.pop("actual_range") for coord in grid.coords: grid[coord].attrs.pop("actual_range") return grid
def grdview(self, grid, **kwargs): r""" Create 3-D perspective image or surface mesh from a grid. Reads a 2-D grid file and produces a 3-D perspective plot by drawing a mesh, painting a colored/gray-shaded surface made up of polygons, or by scanline conversion of these polygons to a raster image. Options include draping a data set on top of a surface, plotting of contours on top of the surface, and apply artificial illumination based on intensities provided in a separate grid file. Full option list at :gmt-docs:`grdview.html` {aliases} Parameters ---------- grid : str or xarray.DataArray The file name of the input relief grid or the grid loaded as a DataArray. region : str or list *xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]. Specify the :doc:`region </tutorials/basics/regions>` of interest. When used with ``perspective``, optionally append */zmin/zmax* to indicate the range to use for the 3-D axes [Default is the region in the input grid]. {J} zscale/zsize : float or str Set z-axis scaling or z-axis size. {B} cmap : str The name of the color palette table to use. drapegrid : str or xarray.DataArray The file name or a DataArray of the image grid to be draped on top of the relief provided by grid. [Default determines colors from grid]. Note that ``zscale`` and ``plane`` always refers to the grid. The drapegrid only provides the information pertaining to colors, which (if drapegrid is a grid) will be looked-up via the CPT (see ``cmap``). plane : float or str *level*\ [**+g**\ *fill*]. Draws a plane at this z-level. If the optional color is provided via the **+g** modifier, and the projection is not oblique, the frontal facade between the plane and the data perimeter is colored. surftype : str Specifies cover type of the grid. Select one of following settings: - **m** - mesh plot [Default]. - **mx** or **my** - waterfall plots (row or column profiles). - **s** - surface plot, and optionally append **m** to have mesh lines drawn on top of the surface. - **i** - image plot. - **c** - Same as **i** but will make nodes with z = NaN transparent. For any of these choices, you may force a monochrome image by appending the modifier **+m**. contourpen : str Draw contour lines on top of surface or mesh (not image). Append pen attributes used for the contours. meshpen : str Sets the pen attributes used for the mesh. You must also select ``surftype`` of **m** or **sm** for meshlines to be drawn. facadepen :str Sets the pen attributes used for the facade. You must also select ``plane`` for the facade outline to be drawn. shading : str Provide the name of a grid file with intensities in the (-1,+1) range, or a constant intensity to apply everywhere (affects the ambient light). Alternatively, derive an intensity grid from the input data grid reliefgrid via a call to ``grdgradient``; append **+a**\ *azimuth*, **+n**\ *args*, and **+m**\ *ambient* to specify azimuth, intensity, and ambient arguments for that method, or just give **+d** to select the default arguments [Default is **+a**\ -45\ **+nt**\ 1\ **+m**\ 0]. {V} {XY} {c} {f} {n} {p} {t} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access with Session() as lib: file_context = lib.virtualfile_from_data(check_kind="raster", data=grid) with contextlib.ExitStack() as stack: if kwargs.get("G") is not None: # deal with kwargs["G"] if drapegrid is xr.DataArray drapegrid = kwargs["G"] if data_kind(drapegrid) in ("file", "grid"): if data_kind(drapegrid) == "grid": drape_context = lib.virtualfile_from_grid(drapegrid) kwargs["G"] = stack.enter_context(drape_context) else: raise GMTInvalidInput( f"Unrecognized data type for drapegrid: {type(drapegrid)}" ) fname = stack.enter_context(file_context) lib.call_module(module="grdview", args=build_arg_string(kwargs, infile=fname))
def savefig(self, fname, transparent=False, crop=True, anti_alias=True, show=False, **kwargs): """ Save the figure to a file. This method implements a matplotlib-like interface for :meth:`pygmt.Figure.psconvert`. Supported formats: PNG (``.png``), JPEG (``.jpg``), PDF (``.pdf``), BMP (``.bmp``), TIFF (``.tif``), EPS (``.eps``), and KML (``.kml``). The KML output generates a companion PNG file. You can pass in any keyword arguments that :meth:`pygmt.Figure.psconvert` accepts. Parameters ---------- fname : str The desired figure file name, including the extension. See the list of supported formats and their extensions above. transparent : bool If True, will use a transparent background for the figure. Only valid for PNG format. crop : bool If True, will crop the figure canvas (page) to the plot area. anti_alias: bool If True, will use anti aliasing when creating raster images (PNG, JPG, TIFF). More specifically, it passes arguments ``t2`` and ``g2`` to the ``anti_aliasing`` parameter of :meth:`pygmt.Figure.psconvert`. Ignored if creating vector graphics. show: bool If True, will open the figure in an external viewer. dpi : int Set raster resolution in dpi. Default is 720 for PDF, 300 for others. """ # All supported formats fmts = dict(png="g", pdf="f", jpg="j", bmp="b", eps="e", tif="t", kml="g") prefix, ext = os.path.splitext(fname) ext = ext[1:] # Remove the . if ext not in fmts: raise GMTInvalidInput("Unknown extension '.{}'".format(ext)) fmt = fmts[ext] if transparent: if fmt != "g": raise GMTInvalidInput( "Transparency unavailable for '{}', only for png.".format( ext)) fmt = fmt.upper() if anti_alias: kwargs["Qt"] = 2 kwargs["Qg"] = 2 if ext == "kml": kwargs["W"] = "+k" self.psconvert(prefix=prefix, fmt=fmt, crop=crop, **kwargs) if show: launch_external_viewer(fname)
def blockmedian(table, outfile=None, **kwargs): r""" Block average (x,y,z) data tables by median estimation. Reads arbitrarily located (x,y,z) triples [or optionally weighted quadruples (x,y,z,w)] from a table and writes to the output a median position and value for every non-empty block in a grid region defined by the region and spacing arguments. Full option list at :gmt-docs:`blockmedian.html` {aliases} Parameters ---------- table : pandas.DataFrame or str Either a pandas dataframe with (x, y, z) or (longitude, latitude, elevation) values in the first three columns, or a file name to an ASCII data table. spacing : str *xinc*\[\ *unit*\][**+e**\|\ **n**] [/*yinc*\ [*unit*][**+e**\|\ **n**]]. *xinc* [and optionally *yinc*] is the grid spacing. region : str or list *xmin/xmax/ymin/ymax*\[\ **+r**\][**+u**\ *unit*]. Specify the region of interest. outfile : str Required if ``table`` is a file. The file name for the output ASCII file. {V} Returns ------- output : pandas.DataFrame or None Return type depends on whether the ``outfile`` parameter is set: - :class:`pandas.DataFrame` table with (x, y, z) columns if ``outfile`` is not set - None if ``outfile`` is set (filtered output will be stored in file set by ``outfile``) """ kind = data_kind(table) with GMTTempFile(suffix=".csv") as tmpfile: with Session() as lib: if kind == "matrix": if not hasattr(table, "values"): raise GMTInvalidInput( f"Unrecognized data type: {type(table)}") file_context = lib.virtualfile_from_matrix(table.values) elif kind == "file": if outfile is None: raise GMTInvalidInput("Please pass in a str to 'outfile'") file_context = dummy_context(table) else: raise GMTInvalidInput(f"Unrecognized data type: {type(table)}") with file_context as infile: if outfile is None: outfile = tmpfile.name arg_str = " ".join( [infile, build_arg_string(kwargs), "->" + outfile]) lib.call_module(module="blockmedian", args=arg_str) # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame result = pd.read_csv(tmpfile.name, sep="\t", names=table.columns) elif outfile != tmpfile.name: # return None if outfile set, output in outfile result = None return result
def plot3d(self, data=None, x=None, y=None, z=None, size=None, direction=None, **kwargs): r""" Plot lines, polygons, and symbols in 3-D. Takes a matrix, (x,y,z) triplets, or a file name as input and plots lines, polygons, or symbols at those locations in 3-D. Must provide either ``data`` or ``x``/``y``/``z``. If providing data through ``x/y/z``, ``color`` can be a 1d array that will be mapped to a colormap. If a symbol is selected and no symbol size given, then plot3d will interpret the fourth column of the input data as symbol size. Symbols whose size is <= 0 are skipped. If no symbols are specified then the symbol code (see ``style`` below) must be present as last column in the input. If ``style`` is not used, a line connecting the data points will be drawn instead. To explicitly close polygons, use ``close``. Select a fill with ``color``. If ``color`` is set, ``pen`` will control whether the polygon outline is drawn or not. If a symbol is selected, ``color`` and ``pen`` determines the fill and outline/no outline, respectively. Full option list at :gmt-docs:`plot3d.html` {aliases} Parameters ---------- data : str or {table-like} Either a data file name, a 2d {table-classes}. Optionally, use parameter ``incols`` to specify which columns are x, y, z, color, and size, respectively. x/y/z : float or 1d arrays The x, y, and z coordinates, or arrays of x, y and z coordinates of the data points size : 1d array The size of the data points in units specified in ``style``. Only valid if using ``x``/``y``/``z``. direction : list of two 1d arrays If plotting vectors (using ``style='V'`` or ``style='v'``), then should be a list of two 1d arrays with the vector directions. These can be angle and length, azimuth and length, or x and y components, depending on the style options chosen. {J} zscale/zsize : float or str Set z-axis scaling or z-axis size. {R} straight_line : bool or str [**m**\|\ **p**\|\ **x**\|\ **y**]. By default, geographic line segments are drawn as great circle arcs. To draw them as straight lines, use ``straight_line``. Alternatively, add **m** to draw the line by first following a meridian, then a parallel. Or append **p** to start following a parallel, then a meridian. (This can be practical to draw a line along parallels, for example). For Cartesian data, points are simply connected, unless you append **x** or **y** to draw stair-case curves that whose first move is along *x* or *y*, respectively. **Note**: The ``straight_line`` parameter requires constant *z*-coordinates. {B} {CPT} offset : str *dx*/*dy*\ [/*dz*]. Offset the plot symbol or line locations by the given amounts *dx*/*dy*\ [/*dz*] [Default is no offset]. {G} *color* can be a 1d array, but it is only valid if using ``x``/``y`` and ``cmap=True`` is also required. intensity : float or bool or 1d array Provide an *intensity* value (nominally in the -1 to +1 range) to modulate the fill color by simulating illumination. If using ``intensity=True``, we will instead read *intensity* from the first data column after the symbol parameters (if given). *intensity* can also be a 1d array to set varying intensity for symbols, but it is only valid for ``x``/``y``/``z``. close : str [**+b**\|\ **d**\|\ **D**][**+xl**\|\ **r**\|\ *x0*]\ [**+yl**\|\ **r**\|\ *y0*][**+p**\ *pen*]. Force closed polygons. Full documentation is at :gmt-docs:`plot3d.html#l`. no_clip : bool or str [**c**\|\ **r**]. Do NOT clip symbols that fall outside map border [Default plots points whose coordinates are strictly inside the map border only]. This parameter does not apply to lines and polygons which are always clipped to the map region. For periodic (360-longitude) maps we must plot all symbols twice in case they are clipped by the repeating boundary. ``no_clip=True`` will turn off clipping and not plot repeating symbols. Use ``no_clip="r"`` to turn off clipping but retain the plotting of such repeating symbols, or use ``no_clip="c"`` to retain clipping but turn off plotting of repeating symbols. no_sort : bool Turn off the automatic sorting of items based on their distance from the viewer. The default is to sort the items so that items in the foreground are plotted after items in the background. style : str Plot symbols. Full documentation is at :gmt-docs:`plot3d.html#s`. {U} {V} {W} {XY} zvalue : str *value*\|\ *file*. Instead of specifying a symbol or polygon fill and outline color via ``color`` and ``pen``, give both a *value* via **zvalue** and a color lookup table via ``cmap``. Alternatively, give the name of a *file* with one z-value (read from the last column) for each polygon in the input data. To apply it to the fill color, use ``color='+z'``. To apply it to the pen color, append **+z** to ``pen``. {a} {b} {c} {d} {e} {f} {g} {h} {i} {l} {p} {t} *transparency* can also be a 1d array to set varying transparency for symbols, but this option is only valid if using x/y/z. {w} """ # pylint: disable=too-many-locals kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access kind = data_kind(data, x, y, z) extra_arrays = [] if kwargs.get("S") is not None and kwargs["S"][ 0] in "vV" and direction is not None: extra_arrays.extend(direction) elif ( kwargs.get("S") is None and kind == "geojson" and data.geom_type.isin(["Point", "MultiPoint"]).all() ): # checking if the geometry of a geoDataFrame is Point or MultiPoint kwargs["S"] = "u0.2c" elif kwargs.get("S") is None and kind == "file" and str(data).endswith( ".gmt"): # checking that the data is a file path to set default style try: with open(which(data), mode="r", encoding="utf8") as file: line = file.readline() if "@GMULTIPOINT" in line or "@GPOINT" in line: # if the file is gmt style and geometry is set to Point kwargs["S"] = "u0.2c" except FileNotFoundError: pass if kwargs.get("G") is not None and is_nonstr_iter(kwargs["G"]): if kind != "vectors": raise GMTInvalidInput( "Can't use arrays for color if data is matrix or file.") extra_arrays.append(kwargs["G"]) del kwargs["G"] if size is not None: if kind != "vectors": raise GMTInvalidInput( "Can't use arrays for 'size' if data is a matrix or a file.") extra_arrays.append(size) for flag in ["I", "t"]: if kwargs.get(flag) is not None and is_nonstr_iter(kwargs[flag]): if kind != "vectors": raise GMTInvalidInput( f"Can't use arrays for {plot3d.aliases[flag]} if data is matrix or file." ) extra_arrays.append(kwargs[flag]) kwargs[flag] = "" with Session() as lib: # Choose how data will be passed in to the module file_context = lib.virtualfile_from_data( check_kind="vector", data=data, x=x, y=y, z=z, extra_arrays=extra_arrays, required_z=True, ) with file_context as fname: lib.call_module(module="plot3d", args=build_arg_string(kwargs, infile=fname))
def info(table, **kwargs): r""" Get information about data tables. Reads from files and finds the extreme values in each of the columns reported as min/max pairs. It recognizes NaNs and will print warnings if the number of columns vary from record to record. As an option, it will find the extent of the first two columns rounded up and down to the nearest multiple of the supplied increments given by ``spacing``. Such output will be in a numpy.ndarray form [*w*, *e*, *s*, *n*], which can be used directly as the ``region`` parameter for other modules (hence only *dx* and *dy* are needed). If the ``per_column`` parameter is combined with ``spacing``, then the numpy.ndarray output will be rounded up/down for as many columns as there are increments provided in ``spacing``. A similar parameter ``nearest_multiple`` will provide a numpy.ndarray in the form of [*zmin*, *zmax*, *dz*] for makecpt. Full option list at :gmt-docs:`gmtinfo.html` {aliases} Parameters ---------- table : str or np.ndarray or pandas.DataFrame or xarray.Dataset Pass in either a file name to an ASCII data table, a 1D/2D numpy array, a pandas dataframe, or an xarray dataset made up of 1D xarray.DataArray data variables. per_column : bool Report the min/max values per column in separate columns. spacing : str [**b**\|\ **p**\|\ **f**\|\ **s**]\ *dx*\[/*dy*\[/*dz*...]]. Report the min/max of the first n columns to the nearest multiple of the provided increments and output results in the form ``[w, e, s, n]``. nearest_multiple : str **dz**\[\ **+c**\ *col*]. Report the min/max of the first (0'th) column to the nearest multiple of dz and output this in the form ``[zmin, zmax, dz]``. {V} Returns ------- output : np.ndarray or str Return type depends on whether any of the ``per_column``, ``spacing``, or ``nearest_multiple`` parameters are set. - :class:`numpy.ndarray` if either of the above parameters are used. - str if none of the above parameters are used. """ kind = data_kind(table) with Session() as lib: if kind == "file": file_context = dummy_context(table) elif kind == "matrix": try: # pandas.DataFrame and xarray.Dataset types arrays = [array for _, array in table.items()] except AttributeError: # Python lists, tuples, and numpy ndarray types arrays = np.atleast_2d(np.asanyarray(table).T) file_context = lib.virtualfile_from_vectors(*arrays) else: raise GMTInvalidInput(f"Unrecognized data type: {type(table)}") with GMTTempFile() as tmpfile: with file_context as fname: arg_str = " ".join( [fname, build_arg_string(kwargs), "->" + tmpfile.name]) lib.call_module("info", arg_str) result = tmpfile.read() if any(arg in kwargs for arg in ["C", "I", "T"]): # Converts certain output types into a numpy array # instead of a raw string that is less useful. if result.startswith(("-R", "-T")): # e.g. -R0/1/2/3 or -T0/9/1 result = result[2:].replace("/", " ") result = np.loadtxt(result.splitlines()) return result
def surface(x=None, y=None, z=None, data=None, **kwargs): r""" Grids table data using adjustable tension continuous curvature splines. Surface reads randomly-spaced (x,y,z) triples and produces gridded values z(x,y) by solving: .. math:: (1 - t)\nabla^2(z)+t\nabla(z) = 0 where :math:`t` is a tension factor between 0 and 1, and :math:`\nabla` indicates the Laplacian operator. Takes a matrix, xyz triples, or a file name as input. Must provide either ``data`` or ``x``, ``y``, and ``z``. Full option list at :gmt-docs:`surface.html` {aliases} Parameters ---------- x/y/z : 1d arrays Arrays of x and y coordinates and values z of the data points. data : str or 2d array Either a data file name or a 2d numpy array with the tabular data. spacing : str *xinc*\[\ *unit*\][**+e**\|\ **n**]\ [/*yinc*\ [*unit*][**+e**\|\ **n**]]. *xinc* [and optionally *yinc*] is the grid spacing. region : str or list *xmin/xmax/ymin/ymax*\[**+r**][**+u**\ *unit*]. Specify the region of interest. outfile : str Optional. The file name for the output netcdf file with extension .nc to store the grid in. {V} {a} {f} {r} Returns ------- ret: xarray.DataArray or None Return type depends on whether the ``outfile`` parameter is set: - :class:`xarray.DataArray`: if ``outfile`` is not set - None if ``outfile`` is set (grid output will be stored in file set by ``outfile``) """ kind = data_kind(data, x, y, z) if kind == "vectors" and z is None: raise GMTInvalidInput("Must provide z with x and y.") with GMTTempFile(suffix=".nc") as tmpfile: with Session() as lib: if kind == "file": file_context = dummy_context(data) elif kind == "matrix": file_context = lib.virtualfile_from_matrix(data) elif kind == "vectors": file_context = lib.virtualfile_from_vectors(x, y, z) else: raise GMTInvalidInput("Unrecognized data type: {}".format( type(data))) with file_context as infile: if "G" not in kwargs.keys( ): # if outfile is unset, output to tmpfile kwargs.update({"G": tmpfile.name}) outfile = kwargs["G"] arg_str = " ".join([infile, build_arg_string(kwargs)]) lib.call_module(module="surface", args=arg_str) if outfile == tmpfile.name: # if user did not set outfile, return DataArray with xr.open_dataarray(outfile) as dataarray: result = dataarray.load() _ = result.gmt # load GMTDataArray accessor information elif outfile != tmpfile.name: # if user sets an outfile, return None result = None return result
def grdtrack(points, grid, newcolname=None, outfile=None, **kwargs): """ Sample grids at specified (x,y) locations. Grdtrack reads one or more grid files and a table with (x,y) [or (lon,lat)] positions in the first two columns (more columns may be present). It interpolates the grid(s) at the positions in the table and writes out the table with the interpolated values added as (one or more) new columns. A bicubic [Default], bilinear, B-spline or nearest-neighbor interpolation is used, requiring boundary conditions at the limits of the region (see *interpolation*; Default uses “natural” conditions (second partial derivative normal to edge is zero) unless the grid is automatically recognized as periodic.) Full option list at :gmt-docs:`grdtrack.html` {aliases} Parameters ---------- points : pandas.DataFrame or str Either a table with (x, y) or (lon, lat) values in the first two columns, or a filename (e.g. csv, txt format). More columns may be present. grid : xarray.DataArray or str Gridded array from which to sample values from, or a filename (netcdf format). newcolname : str Required if 'points' is a pandas.DataFrame. The name for the new column in the track pandas.DataFrame table where the sampled values will be placed. outfile : str Required if 'points' is a file. The file name for the output ASCII file. {V} {n} Returns ------- track: pandas.DataFrame or None Return type depends on whether the outfile parameter is set: - pandas.DataFrame table with (x, y, ..., newcolname) if outfile is not set - None if outfile is set (track output will be stored in outfile) """ with GMTTempFile(suffix=".csv") as tmpfile: with Session() as lib: # Store the pandas.DataFrame points table in virtualfile if data_kind(points) == "matrix": if newcolname is None: raise GMTInvalidInput( "Please pass in a str to 'newcolname'") table_context = lib.virtualfile_from_matrix(points.values) elif data_kind(points) == "file": if outfile is None: raise GMTInvalidInput("Please pass in a str to 'outfile'") table_context = dummy_context(points) else: raise GMTInvalidInput(f"Unrecognized data type {type(points)}") # Store the xarray.DataArray grid in virtualfile if data_kind(grid) == "grid": grid_context = lib.virtualfile_from_grid(grid) elif data_kind(grid) == "file": grid_context = dummy_context(grid) else: raise GMTInvalidInput(f"Unrecognized data type {type(grid)}") # Run grdtrack on the temporary (csv) points table # and (netcdf) grid virtualfile with table_context as csvfile: with grid_context as grdfile: kwargs.update({"G": grdfile}) if outfile is None: # Output to tmpfile if outfile is not set outfile = tmpfile.name arg_str = " ".join( [csvfile, build_arg_string(kwargs), "->" + outfile]) lib.call_module(module="grdtrack", args=arg_str) # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if user did not set outfile, return pd.DataFrame column_names = points.columns.to_list() + [newcolname] result = pd.read_csv(tmpfile.name, sep="\t", names=column_names) elif outfile != tmpfile.name: # return None if outfile set, output in outfile result = None return result
def plot(self, x=None, y=None, data=None, size=None, direction=None, **kwargs): r""" Plot lines, polygons, and symbols in 2-D. Takes a matrix, (x,y) pairs, or a file name as input and plots lines, polygons, or symbols at those locations on a map. Must provide either ``data`` or ``x``/``y``. If providing data through ``x``/``y``, ``color`` can be a 1d array that will be mapped to a colormap. If a symbol is selected and no symbol size given, then plot will interpret the third column of the input data as symbol size. Symbols whose size is <= 0 are skipped. If no symbols are specified then the symbol code (see ``style`` below) must be present as last column in the input. If ``style`` is not used, a line connecting the data points will be drawn instead. To explicitly close polygons, use ``close``. Select a fill with ``color``. If ``color`` is set, ``pen`` will control whether the polygon outline is drawn or not. If a symbol is selected, ``color`` and ``pen`` determines the fill and outline/no outline, respectively. Full parameter list at :gmt-docs:`plot.html` {aliases} Parameters ---------- x/y : float or 1d arrays The x and y coordinates, or arrays of x and y coordinates of the data points data : str or {table-like} Pass in either a file name to an ASCII data table, a 2D {table-classes}. Use parameter ``columns`` to choose which columns are x, y, color, and size, respectively. size : 1d array The size of the data points in units specified using ``style``. Only valid if using ``x``/``y``. direction : list of two 1d arrays If plotting vectors (using ``style='V'`` or ``style='v'``), then should be a list of two 1d arrays with the vector directions. These can be angle and length, azimuth and length, or x and y components, depending on the style options chosen. {J} {R} straight_line : bool or str [**m**\|\ **p**\|\ **x**\|\ **y**]. By default, geographic line segments are drawn as great circle arcs. To draw them as straight lines, use ``straight_line``. Alternatively, add **m** to draw the line by first following a meridian, then a parallel. Or append **p** to start following a parallel, then a meridian. (This can be practical to draw a line along parallels, for example). For Cartesian data, points are simply connected, unless you append **x** or **y** to draw stair-case curves that whose first move is along *x* or *y*, respectively. {B} {CPT} offset : str *dx*/*dy*. Offset the plot symbol or line locations by the given amounts *dx/dy* [Default is no offset]. If *dy* is not given it is set equal to *dx*. error_bar : bool or str [**+b**\|\ **d**\|\ **D**][**+xl**\|\ **r**\|\ *x0*]\ [**+yl**\|\ **r**\|\ *y0*][**+p**\ *pen*]. Draw symmetrical error bars. Full documentation is at :gmt-docs:`plot.html#e`. connection : str [**c**\|\ **n**\|\ **r**]\ [**a**\|\ **f**\|\ **s**\|\ **r**\|\ *refpoint*]. Alter the way points are connected (by specifying a *scheme*) and data are grouped (by specifying a *method*). Append one of three line connection schemes: - **c** : Draw continuous line segments for each group [Default]. - **r** : Draw line segments from a reference point reset for each group. - **n** : Draw networks of line segments between all points in each group. Optionally, append the one of four segmentation methods to define the group: - **a** : Ignore all segment headers, i.e., let all points belong to a single group, and set group reference point to the very first point of the first file. - **f** : Consider all data in each file to be a single separate group and reset the group reference point to the first point of each group. - **s** : Segment headers are honored so each segment is a group; the group reference point is reset to the first point of each incoming segment [Default]. - **r** : Same as **s**, but the group reference point is reset after each record to the previous point (this method is only available with the ``connection='r'`` scheme). Instead of the codes **a**\|\ **f**\|\ **s**\|\ **r** you may append the coordinates of a *refpoint* which will serve as a fixed external reference point for all groups. {G} *color* can be a 1d array, but it is only valid if using ``x``/``y`` and ``cmap=True`` is also required. intensity : float or bool or 1d array Provide an *intensity* value (nominally in the -1 to +1 range) to modulate the fill color by simulating illumination. If using ``intensity=True``, we will instead read *intensity* from the first data column after the symbol parameters (if given). *intensity* can also be a 1d array to set varying intensity for symbols, but it is only valid for ``x``/``y`` pairs. close : str [**+b**\|\ **d**\|\ **D**][**+xl**\|\ **r**\|\ *x0*]\ [**+yl**\|\ **r**\|\ *y0*][**+p**\ *pen*]. Force closed polygons. Full documentation is at :gmt-docs:`plot.html#l`. no_clip : bool or str [**c**\|\ **r**]. Do NOT clip symbols that fall outside map border [Default plots points whose coordinates are strictly inside the map border only]. The parameter does not apply to lines and polygons which are always clipped to the map region. For periodic (360-longitude) maps we must plot all symbols twice in case they are clipped by the repeating boundary. ``no_clip=True`` will turn off clipping and not plot repeating symbols. Use ``no_clip="r"`` to turn off clipping but retain the plotting of such repeating symbols, or use ``no_clip="c"`` to retain clipping but turn off plotting of repeating symbols. style : str Plot symbols (including vectors, pie slices, fronts, decorated or quoted lines). {W} {U} {V} {XY} zvalue : str *value*\|\ *file*. Instead of specifying a symbol or polygon fill and outline color via ``color`` and ``pen``, give both a *value* via ``zvalue`` and a color lookup table via ``cmap``. Alternatively, give the name of a *file* with one z-value (read from the last column) for each polygon in the input data. To apply it to the fill color, use ``color='+z'``. To apply it to the pen color, append **+z** to ``pen``. {a} {c} {f} {i} label : str Add a legend entry for the symbol or line being plotted. {p} {t} *transparency* can also be a 1d array to set varying transparency for symbols, but this option is only valid if using x/y. """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access kind = data_kind(data, x, y) extra_arrays = [] if "S" in kwargs and kwargs["S"][0] in "vV" and direction is not None: extra_arrays.extend(direction) if "G" in kwargs and not isinstance(kwargs["G"], str): if kind != "vectors": raise GMTInvalidInput( "Can't use arrays for color if data is matrix or file." ) extra_arrays.append(kwargs["G"]) del kwargs["G"] if size is not None: if kind != "vectors": raise GMTInvalidInput( "Can't use arrays for 'size' if data is a matrix or file." ) extra_arrays.append(size) for flag in ["I", "t"]: if flag in kwargs and is_nonstr_iter(kwargs[flag]): if kind != "vectors": raise GMTInvalidInput( f"Can't use arrays for {plot.aliases[flag]} if data is matrix or file." ) extra_arrays.append(kwargs[flag]) kwargs[flag] = "" with Session() as lib: # Choose how data will be passed in to the module file_context = lib.virtualfile_from_data( check_kind="vector", data=data, x=x, y=y, extra_arrays=extra_arrays ) with file_context as fname: arg_str = " ".join([fname, build_arg_string(kwargs)]) lib.call_module("plot", arg_str)
def load_earth_relief(resolution="01d", region=None, registration=None, use_srtm=False): r""" Load Earth relief grids (topography and bathymetry) in various resolutions. The grids are downloaded to a user data directory (usually ``~/.gmt/server/earth/earth_relief/``) the first time you invoke this function. Afterwards, it will load the grid from the data directory. So you'll need an internet connection the first time around. These grids can also be accessed by passing in the file name **@earth_relief**\_\ *res*\[_\ *reg*] to any grid plotting/processing function. *res* is the grid resolution (see below), and *reg* is grid registration type (**p** for pixel registration or **g** for gridline registration). Refer to :gmt-docs:`datasets/remote-data.html#global-earth-relief-grids` for more details. Parameters ---------- resolution : str The grid resolution. The suffix ``d``, ``m`` and ``s`` stand for arc-degree, arc-minute and arc-second. It can be ``'01d'``, ``'30m'``, ``'20m'``, ``'15m'``, ``'10m'``, ``'06m'``, ``'05m'``, ``'04m'``, ``'03m'``, ``'02m'``, ``'01m'``, ``'30s'``, ``'15s'``, ``'03s'``, or ``'01s'``. region : str or list The subregion of the grid to load, in the forms of a list [*xmin*, *xmax*, *ymin*, *ymax*] or a string *xmin/xmax/ymin/ymax*. Required for Earth relief grids with resolutions higher than 5 arc-minute (i.e., ``05m``). registration : str Grid registration type. Either ``pixel`` for pixel registration or ``gridline`` for gridline registration. Default is ``None``, where a pixel-registered grid is returned unless only the gridline-registered grid is available. use_srtm : bool By default, the land-only SRTM tiles from NASA are used to generate the ``'03s'`` and ``'01s'`` grids, and the missing ocean values are filled by up-sampling the SRTM15+V2.1 tiles which have a resolution of 15 arc-second (i.e., ``'15s'``). If True, will only load the original land-only SRTM tiles. Returns ------- grid : :class:`xarray.DataArray` The Earth relief grid. Coordinates are latitude and longitude in degrees. Relief is in meters. Notes ----- The :class:`xarray.DataArray` grid doesn't support slice operation, for Earth relief data with resolutions higher than "05m", which are stored as smaller tiles. Examples -------- >>> # load the default grid (pixel-registered 01d grid) >>> grid = load_earth_relief() >>> # load the 30m grid with "gridline" registration >>> grid = load_earth_relief("30m", registration="gridline") >>> # load high-resolution grid for a specific region >>> grid = load_earth_relief( ... "05m", region=[120, 160, 30, 60], registration="gridline" ... ) >>> # load the original 3 arc-second land-only SRTM tiles from NASA >>> grid = load_earth_relief( ... "03s", ... region=[135, 136, 35, 36], ... registration="gridline", ... use_srtm=True, ... ) """ # earth relief data stored as single grids for low resolutions non_tiled_resolutions = ["01d", "30m", "20m", "15m", "10m", "06m"] # earth relief data stored as tiles for high resolutions tiled_resolutions = [ "05m", "04m", "03m", "02m", "01m", "30s", "15s", "03s", "01s" ] # resolutions of original land-only SRTM tiles from NASA land_only_srtm_resolutions = ["03s", "01s"] if registration in ("pixel", "gridline", None): # If None, let GMT decide on Pixel/Gridline type reg = f"_{registration[0]}" if registration else "" else: raise GMTInvalidInput( f"Invalid grid registration: '{registration}', should be either " "'pixel', 'gridline' or None. Default is None, where a " "pixel-registered grid is returned unless only the " "gridline-registered grid is available.") if resolution not in non_tiled_resolutions + tiled_resolutions: raise GMTInvalidInput( f"Invalid Earth relief resolution '{resolution}'.") # Check combination of resolution and registration. if (resolution == "15s" and registration == "gridline") or (resolution in ("03s", "01s") and registration == "pixel"): raise GMTInvalidInput( f"{registration}-registered Earth relief data for " f"resolution '{resolution}' is not supported.") # Choose earth relief data prefix earth_relief_prefix = "earth_relief_" if use_srtm and resolution in land_only_srtm_resolutions: earth_relief_prefix = "srtm_relief_" # different ways to load tiled and non-tiled earth relief data # Known issue: tiled grids don't support slice operation # See https://github.com/GenericMappingTools/pygmt/issues/524 if region is None: if resolution not in non_tiled_resolutions: raise GMTInvalidInput( f"'region' is required for Earth relief resolution '{resolution}'." ) fname = which(f"@earth_relief_{resolution}{reg}", download="a") with xr.open_dataarray(fname) as dataarray: grid = dataarray.load() _ = grid.gmt # load GMTDataArray accessor information else: grid = grdcut(f"@{earth_relief_prefix}{resolution}{reg}", region=region) # Add some metadata to the grid grid.name = "elevation" grid.attrs["long_name"] = "elevation relative to the geoid" grid.attrs["units"] = "meters" grid.attrs["vertical_datum"] = "EMG96" grid.attrs["horizontal_datum"] = "WGS84" # Remove the actual range because it gets outdated when indexing the grid, # which causes problems when exporting it to netCDF for usage on the # command-line. grid.attrs.pop("actual_range") for coord in grid.coords: grid[coord].attrs.pop("actual_range") return grid
def x2sys_cross(tracks=None, outfile=None, **kwargs): r""" Calculate crossovers between track data files. Determines all intersections between ("external cross-overs") or within ("internal cross-overs") tracks (Cartesian or geographic), and report the time, position, distance along track, heading and speed along each track segment, and the crossover error (COE) and mean values for all observables. By default, :meth:`pygmt.x2sys_cross` will look for both external and internal COEs. As an option, you may choose to project all data using one of the map projections prior to calculating the COE. Full option list at :gmt-docs:`supplements/x2sys/x2sys_cross.html` {aliases} Parameters ---------- tracks : pandas.DataFrame or str or list A table or a list of tables with (x, y) or (lon, lat) values in the first two columns. Track(s) can be provided as pandas DataFrame tables or file names. Supported file formats are ASCII, native binary, or COARDS netCDF 1-D data. More columns may also be present. If the filenames are missing their file extension, we will append the suffix specified for this TAG. Track files will be searched for first in the current directory and second in all directories listed in $X2SYS_HOME/TAG/TAG_paths.txt (if it exists). [If $X2SYS_HOME is not set it will default to $GMT_SHAREDIR/x2sys]. (Note: MGD77 files will also be looked for via $MGD77_HOME/mgd77_paths.txt and .gmt files will be searched for via $GMT_SHAREDIR/mgg/gmtfile_paths). outfile : str Optional. The file name for the output ASCII txt file to store the table in. tag : str Specify the x2sys TAG which identifies the attributes of this data type. combitable : str Only process the pair-combinations found in the file *combitable* [Default process all possible combinations among the specified files]. The file *combitable* is created by :gmt-docs:`x2sys_get's -L option <supplements/x2sys/x2sys_get.html#l>`. runtimes : bool or str Compute and append the processing run-time for each pair to the progress message (use ``runtimes=True``). Pass in a filename (e.g. ``runtimes="file.txt"``) to save these run-times to file. The idea here is to use the knowledge of run-times to split the main process in a number of sub-processes that can each be launched in a different processor of your multi-core machine. See the MATLAB function `split_file4coes.m <https://github.com/GenericMappingTools/gmt/blob/master/src/x2sys/>`_. override : bool or str **S**\|\ **N**. Control how geographic coordinates are handled (Cartesian data are unaffected). By default, we determine if the data are closer to one pole than the other, and then we use a cylindrical polar conversion to avoid problems with longitude jumps. You can turn this off entirely with ``override`` and then the calculations uses the original data (we have protections against longitude jumps). However, you can force the selection of the pole for the projection by appending **S** or **N** for the south or north pole, respectively. The conversion is used because the algorithm used to find crossovers is inherently a Cartesian algorithm that can run into trouble with data that has large longitudinal range at higher latitudes. interpolation : str **l**\|\ **a**\|\ **c**. Sets the interpolation mode for estimating values at the crossover. Choose among: - **l** - Linear interpolation [Default]. - **a** - Akima spline interpolation. - **c** - Cubic spline interpolation. coe : str Use **e** for external COEs only, and **i** for internal COEs only [Default is all COEs]. {R} speed : str or list **l**\|\ **u**\|\ **h**\ *speed*. Defines window of track speeds. If speeds are outside this window we do not calculate a COE. Specify: - **l** sets lower speed [Default is 0]. - **u** sets upper speed [Default is infinity]. - **h** does not limit the speed but sets a lower speed below which headings will not be computed (i.e., set to NaN) [Default calculates headings regardless of speed]. For example, you can use ``speed=["l0", "u10", "h5"]`` to set a lower speed of 0, upper speed of 10, and disable heading calculations for speeds below 5. {V} numpoints : int Give the maximum number of data points on either side of the crossover to use in the spline interpolation [Default is 3]. trackvalues : bool Report the values of each track at the crossover [Default reports the crossover value and the mean value]. Returns ------- crossover_errors : :class:`pandas.DataFrame` or None Table containing crossover error information. Return type depends on whether the ``outfile`` parameter is set: - :class:`pandas.DataFrame` with (x, y, ..., etc) if ``outfile`` is not set - None if ``outfile`` is set (track output will be stored in the set in ``outfile``) """ with Session() as lib: file_contexts = [] for track in tracks: kind = data_kind(track) if kind == "file": file_contexts.append(dummy_context(track)) elif kind == "matrix": # find suffix (-E) of trackfiles used (e.g. xyz, csv, etc) from # $X2SYS_HOME/TAGNAME/TAGNAME.tag file lastline = (Path( os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag").read_text().strip().split("\n")[-1] ) # e.g. "-Dxyz -Etsv -I1/1" for item in sorted( lastline.split()): # sort list alphabetically if item.startswith( ("-E", "-D")): # prefer -Etsv over -Dxyz suffix = item[ 2:] # e.g. tsv (1st choice) or xyz (2nd choice) # Save pandas.DataFrame track data to temporary file file_contexts.append( tempfile_from_dftrack(track=track, suffix=suffix)) else: raise GMTInvalidInput(f"Unrecognized data type: {type(track)}") with GMTTempFile(suffix=".txt") as tmpfile: with contextlib.ExitStack() as stack: fnames = [stack.enter_context(c) for c in file_contexts] if outfile is None: outfile = tmpfile.name arg_str = " ".join( [*fnames, build_arg_string(kwargs), "->" + outfile]) lib.call_module(module="x2sys_cross", args=arg_str) # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if outfile isn't set, return pd.DataFrame # Read the tab-separated ASCII table table = pd.read_csv( tmpfile.name, sep="\t", header=2, # Column names are on 2nd row comment=">", # Skip the 3rd row with a ">" parse_dates=[2, 3], # Datetimes on 3rd and 4th column ) # Remove the "# " from "# x" in the first column table = table.rename( columns={table.columns[0]: table.columns[0][2:]}) elif outfile != tmpfile.name: # if outfile is set, output in outfile only table = None return table