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))
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
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