def extract_z(gdf: gpd.geodataframe.GeoDataFrame, dem: Union[np.ndarray, rasterio.io.DatasetReader], inplace: bool = False, **kwargs) -> gpd.geodataframe.GeoDataFrame: """ Extracting altitude values from digital elevation model Args: gdf - gpd.geodataframe.GeoDataFrame containing x,y values dem - rasterio.io.DatasetReader containing the z values inplace - bool - default False -> copy of the current gdf is created Kwargs: extent - list containing the extent of the np.ndarray, must be provided in the same CRS as the gdf Return: gdf - gpd.geodataframe.GeoDataFrame containing x,y,z values obtained from a DEM """ # Input object must be a GeoDataFrame if not isinstance(gdf, gpd.geodataframe.GeoDataFrame): raise TypeError('Loaded object is not a GeoDataFrame') # Create deep copy of gdf if not inplace: gdf = gdf.copy(deep=True) # Input object must be a np.ndarray or a rasterio.io.DatasetReader if not isinstance(dem, (np.ndarray, rasterio.io.DatasetReader)): raise TypeError( 'Loaded object is not a np.ndarray or rasterio.io.DatasetReader') # The GeoDataFrame must not contain a Z-column if pd.Series(['Z']).isin(gdf.columns).all(): raise ValueError('Data already contains Z-values') # Extracting z values from a DEM loaded with Rasterio if isinstance(dem, rasterio.io.DatasetReader): try: if gdf.crs == dem.crs: if np.logical_not( pd.Series(['X', 'Y']).isin(gdf.columns).all()): gdf = extract_xy(gdf) gdf['Z'] = [ z[0] for z in dem.sample(gdf[['X', 'Y']].to_numpy()) ] else: crs_old = gdf.crs gdf = gdf.to_crs(crs=dem.crs) gdf = extract_xy(gdf) gdf['Z'] = [ z[0] for z in dem.sample(gdf[['X', 'Y']].to_numpy()) ] gdf = gdf.to_crs(crs=crs_old) del gdf['X'] del gdf['Y'] gdf = extract_xy(gdf) except IndexError: raise ValueError( 'One or more points are located outside the boundaries of the raster' ) # Extracting z values from a DEM as np.ndarray else: if np.logical_not(pd.Series(['X', 'Y']).isin(gdf.columns).all()): gdf = extract_xy(gdf) extent = kwargs.get('extent', None) assert extent is not None, 'Extent of array is needed to extract Z values' gdf['Z'] = [ sample(dem, extent, gdf[['X', 'Y']].values.tolist()[i]) for i, point in enumerate(gdf[['X', 'Y']].values.tolist()) ] # Convert dip and azimuth columns to floats if pd.Series(['dip']).isin(gdf.columns).all(): gdf['dip'] = gdf['dip'].astype(float) if pd.Series(['azimuth']).isin(gdf.columns).all(): gdf['azimuth'] = gdf['azimuth'].astype(float) # Convert formation column to string if pd.Series(['formation']).isin(gdf.columns).all(): gdf['formation'] = gdf['formation'].astype(str) return gdf
def extract_coordinates(gdf: gpd.geodataframe.GeoDataFrame, dem: Union[np.ndarray, rasterio.io.DatasetReader, type(None)] = None, inplace: bool = False, **kwargs) -> gpd.geodataframe.GeoDataFrame: """ Extract x,y and z coordinates from a GeoDataFrame Args: gdf - gpd.geodataframe.GeoDataFrame containing Points or LineStrings dem - rasterio.io.DatasetReader containing the z values Kwargs: extent - list containing the extent of the np.ndarray, must be provided in the same CRS as the gdf Return: gdf - gpd.geodataframe.GeoDataFrame containing x, y and z values """ # Input object must be a GeoDataFrame if not isinstance(gdf, gpd.geodataframe.GeoDataFrame): raise TypeError('Loaded object is not a GeoDataFrame') # Create deep copy of gdf if not inplace: gdf = gdf.copy(deep=True) # Checking if Z is in GeoDataFrame if np.logical_not(pd.Series(['Z']).isin(gdf.columns).all()): # Checking if dem is not None if dem is None: raise ValueError('DEM is missing') # Checking if DEM is of type np.ndarray or rasterio object if not isinstance(dem, (np.ndarray, rasterio.io.DatasetReader)): raise TypeError( 'Loaded object is not a np.ndarray or Rasterio object') extent = kwargs.get('extent', None) # Checking if X and Y column already exist in gdf if np.logical_not(pd.Series(['X', 'Y']).isin(gdf.columns).all()): if isinstance(dem, np.ndarray): gdf = extract_z(gdf, dem, extent=extent) # Extract XYZ values if dem is rasterio object else: # Extract XYZ values if CRSs are matching if gdf.crs == dem.crs: gdf = extract_z(gdf, dem) # Convert gdf before XYZ values extraction else: crs_old = gdf.crs gdf = gdf.to_crs(crs=dem.crs) gdf.rename(columns={'X': 'X1', 'Y': 'Y1'}) gdf = extract_z(extract_xy(gdf), dem) gdf = gdf.to_crs(crs=crs_old) del gdf['X'] del gdf['Y'] gdf.rename(columns={'X1': 'X', 'Y1': 'Y'}) else: # Extract XYZ values if dem is of type np.ndarray if isinstance(dem, np.ndarray): gdf = extract_z(extract_xy(gdf), dem, extent=extent) # Extract XYZ values if dem is rasterio object else: # Extract XYZ values if CRSs are matching if gdf.crs == dem.crs: gdf = extract_z(extract_xy(gdf), dem) # Convert gdf before XYZ values extraction else: crs_old = gdf.crs gdf = gdf.to_crs(crs=dem.crs) gdf = extract_z(extract_xy(gdf), dem) gdf = gdf.to_crs(crs=crs_old) del gdf['X'] del gdf['Y'] gdf = extract_xy(gdf) else: # Checking if X and Y column already exist in gdf if np.logical_not(pd.Series(['X', 'Y']).isin(gdf.columns).all()): gdf = extract_xy(gdf, inplace=inplace) # Convert dip and azimuth columns to floats if pd.Series(['dip']).isin(gdf.columns).all(): gdf['dip'] = gdf['dip'].astype(float) if pd.Series(['azimuth']).isin(gdf.columns).all(): gdf['azimuth'] = gdf['azimuth'].astype(float) # Convert formation column to string if pd.Series(['formation']).isin(gdf.columns).all(): gdf['formation'] = gdf['formation'].astype(str) return gdf