예제 #1
0
def _str_catalog_to_df(path):
    # if applied to directory, recurse
    path = str(path)  # convert possible path object to str
    if isdir(path):
        df = pd.concat(list(apply_to_files_or_skip(_str_catalog_to_df, path)))
        df.reset_index(drop=True, inplace=True)
        return df
    # else try to read single file
    funcs = (obspy.read_events, pd.read_csv)
    return events_to_df(read_file(path, funcs=funcs))
예제 #2
0
def apply_topo(
    grid: Grid,
    topo_points: Union[str, pd.DataFrame],
    air: float = 0.343,
    method: str = "nearest",
    conversion: Optional[Conversion] = None,
    conversion_kwargs: Optional[dict] = None,
    tolerance: float = 1e-6,
    buffer: int = 0,
    topo_label: str = "TOPO",
) -> Grid:
    """
    Function for applying a topographic surface to the model

    Parameters
    ----------
    grid : Grid
        Grid on which to apply the layers
    topo_points : pandas DataFrame or path to csv or dxf file
        Input containing the topography data
    air : float, optional
        Value to assign to the "air" blocks (blocks above the topography).
        Default is 0.343 km/s.
    method : str, optional
        Method used by scipy's griddata to interpolate the topography grid.
        Acceptable values include: "nearest" (default), "linear", and "cubic"
    conversion : list-like or callable, optional
        See obsplus.conversions.convert_coords
    conversion_kwargs : dict, optional
        See obsplus.conversions.convert_coords
    tolerance : float, optional
        Should be small relative to the grid size. Deals with those pesky
        rounding errors. (Default=1e-6)
    buffer : int, optional
        Number of cells above the topography to extend the "rock" values
        (i.e., the values that are below the topography) (default=0)
    topo_label : str, optional
        Label to assign the 2D grid that is created from topo_points
        (default="TOPO")

    Returns
    -------
    topo : Grid
        2D grid created from topo_points

    Notes
    -----
    If the input is a CSV file or pandas DataFrame, the following columns are
    required: ["x", "y", "z"]. If the input is a dxf file, the dxf should not
    contain any data other than the topography (in the form of LWPOLYLINEs,
    LINEs, POLYLINES, POINTS, and/or 3DFACEs) and must have the elevation data
    stored in the entities (Z coordinates shouldn't be 0.0).\n
    It should be noted that bizarre results may occur if the topo data does
    not extend beyond the grid region.\n
    """
    # Do some basic error checking and read in the topo data
    if isinstance(topo_points, pd.DataFrame):
        topo = topo_points
        if not {"x", "y", "z"}.issubset(topo.columns):
            raise KeyError(
                "topo_points must contain the following columns: ['x', 'y', 'z']"
            )
    elif isinstance(topo_points, str):
        if not os.path.isfile(topo_points):
            raise OSError(f"topo file does not exist: {topo_points}")
        topo = read_file(topo_points, funcs=(pd.read_csv, _read_topo_dxf))
        if not {"x", "y", "z"}.issubset(topo.columns):
            raise IOError(f"{topo_points} is not a valid topo file")
    else:
        raise TypeError("An invalid topo_points was provided to apply_topo")
    if not {"x", "y", "z"}.issubset(topo.columns):
        raise KeyError(
            "topo_points must contain the following columns: ['x', 'y', 'z']")

    if method not in ["nearest", "linear", "cubic"]:
        raise ValueError(f"Unsupported interpolation format: {method}")

    # Apply a coordinate conversion, if necessary
    if conversion:
        topo = convert_coords(
            topo,
            conversion,
            x_in="x",
            y_in="y",
            z_in="z",
            x_out="x",
            y_out="y",
            z_out="z",
            conversion_kwargs=conversion_kwargs,
        )

    # Create a 2D grid space for the topo map
    grid_x, grid_y = np.mgrid[grid.grid_points[0][0]:grid.
                              grid_points[0][-1]:grid.header["num_gps"][0] *
                              1j, grid.grid_points[1][0]:grid.
                              grid_points[1][-1]:grid.header["num_gps"][1] *
                              1j, ]
    # Interpolate over the grid
    nearest_grid = griddata(
        np.array(topo[["x", "y"]]),
        np.array(topo["z"]),
        (grid_x, grid_y),
        method="nearest",
    )
    if method == "nearest":
        topo_grid = nearest_grid
    else:
        topo_grid = griddata(
            np.array(topo[["x", "y"]]),
            np.array(topo["z"]),
            (grid_x, grid_y),
            method=method,
        )
        mask = np.isnan(topo_grid)
        topo_grid[mask] = nearest_grid[mask]
    topo_grid = topo_grid - tolerance
    topo_grid = np.ndarray.astype(topo_grid, np.float64)
    topo = Grid(
        base_name=grid.base_name + "_topo",
        gtype=topo_label,
        origin=grid.header["origin"][0:2],
        num_gps=grid.header["num_gps"][0:2],
        spacing=grid.header["spacing"][0:2],
    )
    topo.values = topo_grid
    # Overlay the topo grid on the velocity grid
    elevs = grid.grid_map[:][:][2]
    # Optionally add extra cells above the topo layer to deal with the ways
    # some programs interpolate between cells
    mask = np.array([
        (elevs[:, :, i] > topo_grid + buffer * grid.header["spacing"][2])
        for i in range(elevs.shape[2])
    ])
    mask = np.swapaxes(mask, 0, 1)
    mask = np.swapaxes(mask, 1, 2)
    grid.values[mask] = air
    return topo
예제 #3
0
def apply_rectangles(
    grid: Grid,
    rectangles: Union[pd.DataFrame, str],
    tol: float = 1e-6,
    conversion: Optional[Conversion] = None,
    conversion_kwargs: Optional[dict] = None,
) -> None:
    """
    Function for perturbing grid values in rectangular regions

    Parameters
    ----------
    grid : Grid
        Grid on which to apply the layers
    rectangles : pandas DataFrame
        List of velocity perturbations to apply. Required columns
        include ["DELTA", "XMIN", "XMAX", "YMIN", "YMAX"] where "DELTA" is
        some percentage of the current grid value
    tol : float
        Value to add to rectangle coordinates to prevent rounding
        errors. This value should be small relative to the grid
        spacing.
    conversion : list-like or callable, optional
        See obsplus.conversions.convert_coords
    conversion_kwargs : dict, optional
        See obsplus.conversions.convert_coords
    """
    if isinstance(rectangles, str):
        path = rectangles
        if not os.path.isfile(path):
            raise OSError(f"rectangles file does not exist: {path}")
        rectangles = read_file(path)
        cols = {"delta", "xmin", "ymin", "zmin", "xmax", "ymax", "zmax"}
        if not cols.issubset(rectangles.columns):
            raise IOError(f"{path} is not a valid rectangles file")
    elif isinstance(rectangles, pd.DataFrame):
        pass
    else:
        raise TypeError("rectangles must be a pandas DataFrame")
    if conversion:
        for num, r in rectangles.iterrows():
            rectangles.loc[num, ["xmin", "ymin", "zmin"]] = convert_coords(
                r[["xmin", "ymin", "zmin"]],
                conversion=conversion,
                conversion_kwargs=conversion_kwargs,
            )
            rectangles.loc[num, ["xmax", "ymax", "zmax"]] = convert_coords(
                r[["xmax", "ymax", "zmax"]],
                conversion=conversion,
                conversion_kwargs=conversion_kwargs,
            )
    v = grid.values
    gmap = grid.grid_map
    for num, zone in rectangles.iterrows():
        # apply the perturbation
        delta = 0.01 * zone.delta
        xmask = (gmap[0] >= zone.xmin - tol) & (gmap[0] <= zone.xmax + tol)
        ymask = (gmap[1] >= zone.ymin - tol) & (gmap[1] <= zone.ymax + tol)
        zmask = (gmap[2] >= zone.zmin - tol) & (gmap[2] <= zone.zmax + tol)
        v[xmask & ymask & zmask] = v[xmask & ymask & zmask] * delta
    return