def calculate_orientation(gdf: gpd.geodataframe.GeoDataFrame) -> gpd.geodataframe.GeoDataFrame: """ Calculating the orientations from linestrings Args: gdf: GeoDataFrame containing the orientation LineStrings Return: gdf: GeoDataFrame containing the orientation values """ gdf = calculate_orientations_new(gdf) gdf['length'] = gdf.geometry.length gdf['dip'] = np.rad2deg(np.arctan(gdf['dZ'] / gdf['length'])) gdf = vector.extract_xy(gdf, reset_index=False) x = [] y = [] formation = [] dip = [] azimuth = [] for i in range(0, len(gdf), 2): x.append(np.abs(gdf.iloc[i + 1]['X'] + gdf.iloc[i]['X']) / 2) y.append(np.abs(gdf.iloc[i + 1]['Y'] + gdf.iloc[i]['Y']) / 2) formation.append(gdf.iloc[i]['formation']) dip.append(gdf.iloc[i]['dip']) azimuth.append(gdf.iloc[i]['azimuth']) df = pd.DataFrame(data=[x, y, formation, dip, azimuth]).transpose() df.columns = ['X', 'Y', 'formation', 'dip', 'azimuth'] gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.X, df.Y), crs='EPSG:4647') gdf['polarity'] = 1 return gdf
def test_sample_from_rasterio2(gdf, dem): from gemgis.raster import sample_from_rasterio from gemgis.vector import extract_xy extent = [0, 972.0, 0, 1069.0] gdf = extract_xy(gdf)
def test_sample_from_rasterio2(gdf, dem): from gemgis.raster import sample_from_rasterio from gemgis.vector import extract_xy gdf = extract_xy(gdf) point_x = gdf['X'].tolist()[0] point_y = gdf['Y'].tolist()[0] samples = sample_from_rasterio(dem, point_x, point_y) assert isinstance(samples, float) assert samples == 364.994873046875
def to_section_dict(gdf: gpd.geodataframe.GeoDataFrame, section_column: str = 'section_name', resolution=None) -> dict: """ Converting custom sections stored in shape files to GemPy section_dicts Args: gdf - gpd.geodataframe.GeoDataFrame containing the points or lines of custom sections section_column - string containing the name of the column containing the section names resolution - list containing the x,y resolution of the custom section Return: section_dict containing the section names, coordinates and resolution """ if resolution is None: resolution = [100, 80] # Checking if gdf is of type GeoDataFrame if not isinstance(gdf, gpd.geodataframe.GeoDataFrame): raise TypeError('gdf must be of type GeoDataFrame') # Checking if the section_column is of type string if not isinstance(section_column, str): raise TypeError('Name for section_column must be of type string') # Checking if resolution is of type list if not isinstance(resolution, list): raise TypeError('resolution must be of type list') # Checking if X and Y values are in column if not {'X', 'Y'}.issubset(gdf.columns): gdf = vector.extract_xy(gdf) if len(resolution) != 2: raise ValueError('resolution list must be of length two') # Extracting Section names section_names = gdf[section_column].unique() # Create section dicts for Point Shape Files if all(gdf.geom_type == "Point"): section_dict = {i: ([gdf[gdf[section_column] == i].X.iloc[0], gdf[gdf[section_column] == i].Y.iloc[0]], [gdf[gdf[section_column] == i].X.iloc[1], gdf[gdf[section_column] == i].Y.iloc[1]], resolution) for i in section_names} # Create section dicts for Line Shape Files else: section_dict = {i: ([gdf[gdf[section_column] == i].X.iloc[0], gdf[gdf[section_column] == i].Y.iloc[0]], [gdf[gdf[section_column] == i].X.iloc[1], gdf[gdf[section_column] == i].Y.iloc[1]], resolution) for i in section_names} return section_dict
def plot_contours_3d(contours: gpd.geodataframe.GeoDataFrame, plotter: pv.Plotter, color: str = 'red', add_to_z: Union[int, float] = 0): """ Plotting the dem in 3D with pv Args: contours: GeoDataFrame containing the contour information plotter: name of the PyVista plotter color: string for the color of the contour lines add_to_z: int of float value to add to the height of points """ if not isinstance(contours, gpd.geodataframe.GeoDataFrame): raise TypeError('Line Object must be of type GeoDataFrame') # Checking if the plotter is of type pv plotter if not isinstance(plotter, pv.Plotter): raise TypeError('Plotter must be of type pv.Plotter') # Checking if the color is of type string if not isinstance(color, str): raise TypeError('Color must be of type string') # Checking if additional Z value is of type int or float if not isinstance(add_to_z, (int, float)): raise TypeError('Add_to_z must be of type int or float') # Checking if Z values are in gdf if not {'Z'}.issubset(contours.columns): raise ValueError('Z-values not defined') # If XY coordinates not in gdf, extract X,Y values if not {'X', 'Y'}.issubset(contours.columns): contours = extract_xy(contours, reset_index=False) # Create list of points and plot them try: for j in contours.index.unique(): point_list = [[ contours.loc[j].iloc[i].X, contours.loc[j].iloc[i].Y, contours.loc[j].iloc[i].Z + add_to_z ] for i in range(len(contours.loc[j]))] vertices = np.array(point_list) plotter.add_lines(vertices, color=color) except AttributeError: raise AttributeError('X and Y coordinates of countours are missing')
def test_sample_from_rasterio(gdf, dem): from gemgis.raster import sample_from_rasterio from gemgis.vector import extract_xy gdf = extract_xy(gdf) point_x = gdf['X'].tolist()[:5] point_y = gdf['Y'].tolist()[:5] samples = sample_from_rasterio(dem, point_x, point_y) assert isinstance(samples, list) assert len(samples) == 5 assert isinstance(samples[0], float) assert samples[0] == 364.994873046875 assert samples[1] == 400.3435974121094 assert samples[2] == 459.54931640625 assert samples[3] == 525.6910400390625 assert samples[4] == 597.6325073242188
def test_sample_from_array(gdf, dem): from gemgis.raster import sample_from_array from gemgis.vector import extract_xy extent = [0, 972.0, 0, 1069.0] gdf = extract_xy(gdf) point_x = gdf['X'].tolist()[:5] point_y = gdf['Y'].tolist()[:5] samples = sample_from_array(dem.read(1), extent, point_x, point_y) assert isinstance(samples, np.ndarray) assert len(samples) == 5 assert isinstance(samples[0], np.float32) assert samples[0] == np.float32(366.61255) assert samples[1] == np.float32(402.09912) assert samples[2] == np.float32(460.6181) assert samples[3] == np.float32(529.0156) assert samples[4] == np.float32(597.6325)
def interpolate_strike_lines(gdf, increment): """ Args: gdf: increment: Returns: """ gdf_out = gpd.GeoDataFrame() gdf = vector.extract_xy(gdf).sort_values(by='id') for i in range(len(gdf['id'].unique().tolist()) - 1): diff = gdf.loc[gdf.index.unique().values.tolist()[i]]['Z'].values.tolist()[0] - \ gdf.loc[gdf.index.unique().values.tolist()[i + 1]]['Z'].values.tolist()[0] if np.abs(diff) > increment: gdf_strike = pd.concat([ gdf.loc[gdf.index.unique().values.tolist()[i]], gdf.loc[gdf.index.unique().values.tolist()[i + 1]] ]) lines = calculate_lines(gdf_strike, increment) gdf_new = pd.concat([ gdf.loc[gdf.index.unique().values.tolist()[i]], lines, gdf.loc[gdf.index.unique().values.tolist()[i + 1]] ]) gdf_out = gdf_out.append(gdf_new, ignore_index=True) else: gdf_new = pd.concat([ gdf.loc[gdf.index.unique().values.tolist()[i]], gdf.loc[gdf.index.unique().values.tolist()[i + 1]] ]) gdf_out = gdf_out.append(gdf_new, ignore_index=True) gdf_out = gdf_out.sort_values(by=['Y']).drop_duplicates('geometry') gdf_out['id'] = np.arange(1, len(gdf_out['id'].values.tolist()) + 1).tolist() return gdf_out
def calculate_lines(gdf, increment): """ Args: gdf: increment: Returns: """ num = calculate_number_of_isopoints(gdf, increment) gdf = gdf.sort_values(by=['Z', 'X']) minval = min(gdf.sort_values(by='Z')['Z'].unique().tolist()) maxval = max(gdf.sort_values(by='Z')['Z'].unique().tolist()) pointsx = [] pointsy = [] for i in range(len(gdf[gdf['Z'] == minval])): index = get_nearest_neighbor( np.array(gdf[gdf['Z'] == minval][['X', 'Y']].values.tolist()), np.array([ gdf[gdf['Z'] == minval]['X'].values.tolist()[i], gdf[gdf['Z'] == minval]['Y'].values.tolist()[i] ])) x1 = gdf[gdf['Z'] == minval]['X'].tolist()[i] y1 = gdf[gdf['Z'] == minval]['Y'].tolist()[i] x2 = gdf[gdf['Z'] == maxval]['X'].tolist()[index] y2 = gdf[gdf['Z'] == maxval]['Y'].tolist()[index] for j in range(num): pointx = ((j + 1) / (num + 1) * x2 + (1 - (j + 1) / (num + 1)) * x1) pointy = ((j + 1) / (num + 1) * y2 + (1 - (j + 1) / (num + 1)) * y1) pointsx.append(pointx) pointsy.append(pointy) ls_list = [] heights = [] for i in range(0, int(len(pointsx) / 2)): ls = LineString([ Point(pointsx[i], pointsy[i]), Point(pointsx[i + num], pointsy[i + num]) ]) ls_list.append(ls) heights.append(minval + i * increment + increment) heights.append(minval + i * increment + increment) lines = gpd.GeoDataFrame(gpd.GeoSeries(ls_list)) lines['geometry'] = ls_list lines = vector.extract_xy(lines) del lines[0] lines['formation'] = gdf['formation'].unique().tolist()[0] lines['Z'] = heights lines['id'] = heights return lines
def calculate_orientations(gdf: gpd.geodataframe.GeoDataFrame) -> pd.DataFrame: """ Calculating orientation values from strike lines based on eigenvector analysis Args: gdf: GeoDataFrame containing the intersections of layer boundaries with topographic contour lines Return: orientations: DataFrame containing the extracted orientation values and a midpoint location of the strike lines """ # Checking if gdf is of type GeoDataFrame if not isinstance(gdf, gpd.geodataframe.GeoDataFrame): raise TypeError('gdf must be of type GeoDataFrame') # Checking if X and Y values are in column if np.logical_not(pd.Series(['formation', 'Z']).isin(gdf.columns).all()): raise ValueError('formation or Z column missing in GeoDataFrame') if any(gdf['id'].apply(lambda x: x == None)): raise ValueError('IDs must not be None') # Extract XY coordinates gdf_new = vector.extract_xy(gdf, inplace=False) # Create empty lists orientations = [] xlist = [] ylist = [] zlist = [] if len(gdf_new['id'].unique()) == 2: # Get values for height gdf_new_array = gdf_new[['X', 'Y', 'Z']].values.tolist() points = gdf_new_array # Calculates eigenvector of points C = np.cov(gdf_new_array, rowvar=False) normal_vector = np.linalg.eigh(C)[1][:, 0] x, y, z = normal_vector # Convert vector to dip and azimuth sign_z = 1 if z > 0 else -1 dip = np.degrees(np.arctan2(np.sqrt(x * x + y * y), abs(z))) azimuth = (np.degrees(np.arctan2(sign_z * x, sign_z * y)) % 360) orient = [dip, azimuth] # Append values to list orientations.append(orient) xlist.append( sum([points[i][0] for i in range(len(points))]) / len(points)) ylist.append( sum([points[i][1] for i in range(len(points))]) / len(points)) zlist.append( sum([points[i][2] for i in range(len(points))]) / len(points)) else: # Extract orientations for i in range(len(gdf_new['id'].unique()) - 1): # Get values for the first and second height gdf_new1 = gdf_new[gdf_new['id'] == i + 1 + (gdf_new['id'].unique()[0] - 1)] gdf_new2 = gdf_new[gdf_new['id'] == i + 2 + (gdf_new['id'].unique()[0] - 1)] # Convert coordinates to lists gdf_new1_array = gdf_new1[['X', 'Y', 'Z']].values.tolist() gdf_new2_array = gdf_new2[['X', 'Y', 'Z']].values.tolist() # Merge lists of points points = gdf_new1_array + gdf_new2_array # Calculates eigenvector of points C = np.cov(points, rowvar=False) normal_vector = np.linalg.eigh(C)[1][:, 0] x, y, z = normal_vector # Convert vector to dip and azimuth sign_z = 1 if z > 0 else -1 dip = np.degrees(np.arctan2(np.sqrt(x * x + y * y), abs(z))) azimuth = (np.degrees(np.arctan2(sign_z * x, sign_z * y)) % 360) orient = [dip, azimuth] # Append values to list orientations.append(orient) xlist.append( sum([points[i][0] for i in range(len(points))]) / len(points)) ylist.append( sum([points[i][1] for i in range(len(points))]) / len(points)) zlist.append( sum([points[i][2] for i in range(len(points))]) / len(points)) # Create DataFrame orientations = pd.DataFrame(data=[ xlist, ylist, zlist, [i[0] for i in orientations], [i[1] for i in orientations] ]).transpose() # Rename columns orientations.columns = ['X', 'Y', 'Z', 'dip', 'azimuth'] # Add polarity column orientations['polarity'] = 1 # Add formation name orientations['formation'] = gdf['formation'].unique()[0] return orientations
def interpolate_strike_lines(gdf: gpd.geodataframe.GeoDataFrame, increment: Union[float, int], **kwargs) \ -> gpd.geodataframe.GeoDataFrame: """ Interpolating strike lines to calculate orientations Args: gdf: GeoDataFrame containing existing strike lines increment: increment between the strike lines Kwargs: xcol: str/name of X column ycol: str/name of X column zcol: str/name of Z column Returns: gdf_out: GeoDataFrame containing the existing and interpolated strike lines """ # Checking if gdf is of type GeoDataFrame if not isinstance(gdf, gpd.geodataframe.GeoDataFrame): raise TypeError('gdf must be of type GeoDataFrame') # Checking if the increment is of type float or int if not isinstance(increment, (float, int)): raise TypeError('The increment must be provided as float or int') # Getting the name of the Z column xcol = kwargs.get('xcol', 'X') # Getting the name of the Y column ycol = kwargs.get('zcol', 'Y') # Getting the name of the Z column zcol = kwargs.get('zcol', 'Z') # Create empty GeoDataFrame gdf_out = gpd.GeoDataFrame() # Extract vertices from gdf gdf = vector.extract_xy(gdf, drop_id=False, reset_index=False).sort_values(by='id') # Interpolate strike lines for i in range(len(gdf['id'].unique().tolist()) - 1): # Calculate distance between two strike lines in the original gdf diff = gdf.loc[gdf.index.unique().values.tolist()[i]][zcol].values.tolist()[0] - \ gdf.loc[gdf.index.unique().values.tolist()[i + 1]][zcol].values.tolist()[0] # If the distance is larger than the increment, interpolate strike lines if np.abs(diff) > increment: gdf_strike = pd.concat( [gdf.loc[gdf.index.unique().values.tolist()[i]], gdf.loc[gdf.index.unique().values.tolist()[i + 1]]]) # Calculate strike lines lines = calculate_lines(gdf_strike, increment) # Append interpolated lines to gdf that will be returned gdf_new = pd.concat( [gdf.loc[gdf.index.unique().values.tolist()[i]], lines, gdf.loc[gdf.index.unique().values.tolist()[i + 1]]]) gdf_out = gdf_out.append(gdf_new, ignore_index=True) # If the distance is equal to the increment, append line to the gdf that will be returned else: gdf_new = pd.concat( [gdf.loc[gdf.index.unique().values.tolist()[i]], gdf.loc[gdf.index.unique().values.tolist()[i + 1]]]) gdf_out = gdf_out.append(gdf_new, ignore_index=True) # Drop duplicates gdf_out = gdf_out.sort_values(by=['Y']).drop_duplicates('geometry') # Redefine ID column with interpolated strike lines gdf_out['id'] = np.arange(1, len(gdf_out['id'].values.tolist()) + 1).tolist() return gdf_out
def calculate_lines(gdf: Union[gpd.geodataframe.GeoDataFrame, pd.DataFrame], increment: Union[float, int], **kwargs): """ Function to interpolate strike lines Args: gdf: GeoDataFrame/DataFrame containing existing strike lines increment: increment between the strike lines Kwargs: xcol: str/name of X column ycol: str/name of X column zcol: str/name of Z column Returns: lines: GeoDataFrame with interpolated strike lines """ # Checking if gdf is of type GeoDataFrame if not isinstance(gdf, (gpd.geodataframe.GeoDataFrame, pd.DataFrame)): raise TypeError('gdf must be of type GeoDataFrame') # Checking that all geometries are valid if not all(gdf.geometry.is_valid): raise ValueError('Not all Shapely Objects are valid objects') # Checking if the increment is of type float or int if not isinstance(increment, (float, int)): raise TypeError('The increment must be provided as float or int') # Getting the name of the Z column xcol = kwargs.get('xcol', 'X') # Getting the name of the Y column ycol = kwargs.get('zcol', 'Y') # Getting the name of the Z column zcol = kwargs.get('zcol', 'Z') # Checking that the Z column is in the GeoDataFrame if not pd.Series([xcol, zcol]).isin(gdf.columns).all(): raise ValueError('Provide names of X,Z columns as kwarg as X,Z columns could not be recognized') # Calculating number of isopoints num = calculate_number_of_isopoints(gdf, increment, zcol=zcol) # Sorting values gdf = gdf.sort_values(by=[zcol, xcol]) # Getting lists of min and max values minval = min(gdf.sort_values(by=zcol)[zcol].unique().tolist()) maxval = max(gdf.sort_values(by=zcol)[zcol].unique().tolist()) # Creating empty list for x and y values pointsx = [] pointsy = [] # Calculating vertices of lines for i in range(len(gdf[gdf[zcol] == minval])): # Getting index for nearest neighbor index = get_nearest_neighbor(np.array(gdf[gdf[zcol] == minval][[xcol, ycol]].values.tolist()), np.array([gdf[gdf[zcol] == minval][xcol].values.tolist()[i], gdf[gdf[zcol] == minval][ycol].values.tolist()[i]])) # Creating x and y points from existing gdf x1 = gdf[gdf['Z'] == minval][xcol].tolist()[i] y1 = gdf[gdf['Z'] == minval][ycol].tolist()[i] x2 = gdf[gdf['Z'] == maxval][xcol].tolist()[index] y2 = gdf[gdf['Z'] == maxval][ycol].tolist()[index] # Calculating vertices of lines for j in range(num): # Calculating vertices pointx = ((j + 1) / (num + 1) * x2 + (1 - (j + 1) / (num + 1)) * x1) pointy = ((j + 1) / (num + 1) * y2 + (1 - (j + 1) / (num + 1)) * y1) # Append vertices to list pointsx.append(pointx) pointsy.append(pointy) # Create empty lists ls_list = [] heights = [] # Create linestring from point lists for i in range(0, int(len(pointsx) / 2)): # Creating linestrings ls = LineString([Point(pointsx[i], pointsy[i]), Point(pointsx[i + num], pointsy[i + num])]) # Appending line strings ls_list.append(ls) heights.append(minval + i * increment + increment) heights.append(minval + i * increment + increment) # Creating GeoDataFrame lines = gpd.GeoDataFrame(gpd.GeoSeries(ls_list), crs=gdf.crs) # Setting geometry column of GeoDataFrame lines['geometry'] = ls_list # Extracting X and Y coordinate and deleting first entry lines = vector.extract_xy(lines) del lines[0] # Adding formation and height information to GeoDataFrame lines['formation'] = gdf['formation'].unique().tolist()[0] lines['Z'] = heights lines['id'] = heights return lines