Source code for mplstereonet.utilities

import numpy as np
import re
from . import stereonet_math as smath

[docs]def parse_strike_dip(strike, dip): """ Parses strings of strike and dip and returns strike and dip measurements following the right-hand-rule. Dip directions are parsed, and if the measurement does not follow the right-hand-rule, the opposite end of the strike measurement is returned. Accepts either quadrant-formatted or azimuth-formatted strikes. For example, this would convert a strike of "N30E" and a dip of "45NW" to a strike of 210 and a dip of 45. Parameters ---------- strike : string A strike measurement. May be in azimuth or quadrant format. dip : string The dip angle and direction of a plane. Returns ------- azi : float Azimuth in degrees of the strike of the plane with dip direction indicated following the right-hand-rule. dip : float Dip of the plane in degrees. """ strike = parse_azimuth(strike) dip, direction = split_trailing_letters(dip) if direction is not None: expected_direc = strike + 90 if opposite_end(expected_direc, direction): strike += 180 if strike > 360: strike -= 360 return strike, dip
[docs]def parse_rake(strike, dip, rake): """ Parses strings of strike, dip, and rake and returns a strike, dip, and rake measurement following the right-hand-rule, with the "end" of the strike that the rake is measured from indicated by the sign of the rake (positive rakes correspond to the strike direction, negative rakes correspond to the opposite end). Accepts either quadrant-formatted or azimuth-formatted strikes. For example, this would convert a strike of "N30E", dip of "45NW", with a rake of "10NE" to a strike of 210, dip of 45, and rake of 170. Rake angles returned by this function will always be between 0 and 180 If no directions are specified, the measuriement is assumed to follow the usual right-hand-rule convention. Parameters ---------- strike : string A strike measurement. May be in azimuth or quadrant format. dip : string The dip angle and direction of a plane. rake : string The rake angle and direction that the rake is measured from. Returns ------- strike, dip, rake : floats Measurements of strike, dip, and rake following the conventions outlined above. """ strike, dip = parse_strike_dip(strike, dip) rake, direction = split_trailing_letters(rake) if direction is not None: if opposite_end(strike, direction): rake = -rake if rake < 0: rake += 180 elif rake > 180: rake -= 180 return strike, dip, rake
[docs]def parse_plunge_bearing(plunge, bearing): """ Parses strings of plunge and bearing and returns a consistent plunge and bearing measurement as floats. Plunge angles returned by this function will always be between 0 and 90. If no direction letter(s) is present, the plunge is assumed to be measured from the end specified by the bearing. If a direction letter(s) is present, the bearing will be switched to the opposite (180 degrees) end if the specified direction corresponds to the opposite end specified by the bearing. Parameters ---------- plunge : string A plunge measurement. bearing : string A bearing measurement. May be in azimuth or quadrant format. Returns ------- plunge, bearing: floats The plunge and bearing following the conventions outlined above. Examples --------- >>> parse_plunge_bearing("30NW", 160) ... (30, 340) """ bearing = parse_azimuth(bearing) plunge, direction = split_trailing_letters(plunge) if direction is not None: if opposite_end(bearing, direction): bearing +=180 if plunge < 0: bearing += 180 plunge = -plunge if plunge > 90: bearing += 180 plunge = 180 - plunge if bearing > 360: bearing -= 360 return plunge, bearing
def dip_direction2strike(azimuth): """ Converts a planar measurment of dip direction using the dip-azimuth convention into a strike using the right-hand-rule. Parameters ---------- azimuth : number or string The dip direction of the plane in degrees. This can be either a numerical azimuth in the 0-360 range or a string representing a quadrant measurement (e.g. N30W). Returns ------- strike : number The strike of the plane in degrees following the right-hand-rule. """ azimuth = parse_azimuth(azimuth) strike = azimuth - 90 if strike < 0: strike += 360 return strike def strike2dip_direction(strike): """ Converts a planar measurement of strike using the right-hand-rule into the dip direction (i.e. the direction that the plane dips). Parameters ---------- strike : number or string The strike direction of the plane in degrees. This can be either a numerical azimuth in the 0-360 range or a string representing a quadrant measurement (e.g. N30W). Returns ------- azimuth : number The dip direction of the plane in degrees (0-360). """ strike = parse_azimuth(strike) dip_direction = strike + 90 if dip_direction > 360: dip_direction -= 360 return dip_direction def opposite_end(azimuth, quadrant): direc = quadrantletter_to_azimuth(quadrant) dot = np.dot(_azimuth2vec(direc), _azimuth2vec(azimuth)) return dot < 0 def split_trailing_letters(item): # Match things like 45NW, 45E, 45WNW, etc letters = re.search(r'[NESWnesw]', item) if letters is None: return float(item), None else: measurement = item[:letters.start()] direction = item[letters.start():] return float(measurement), direction def circmean(azimuths): azimuths = np.radians(azimuths) x = np.cos(azimuths) y = np.sin(azimuths) return np.degrees(np.arctan2(y.mean(), x.mean())) def quadrantletter_to_azimuth(letters): letters = letters.strip() azimuth = {'N':0, 'S':180, 'E':90, 'W':270} azi = azimuth[letters[-1]] for letter in letters[1::-1]: azi = circmean([azi, azimuth[letter]]) return azi def _azimuth2vec(azi): azi = np.radians(azi) return np.cos(azi), np.sin(azi)
[docs]def parse_azimuth(azimuth): """ Parses an azimuth measurement in azimuth or quadrant format. Parameters ----------- azimuth : string or number An azimuth measurement in degrees or a quadrant measurement of azimuth. Returns ------- azi : float The azimuth in degrees clockwise from north (range: 0-360) See Also -------- parse_quadrant_measurement parse_strike_dip parse_plunge_bearing """ try: azimuth = float(azimuth) except ValueError: if not azimuth[0].isalpha(): raise ValueError('Ambiguous azimuth: {}'.format(azimuth)) azimuth = parse_quadrant_measurement(azimuth) return azimuth
[docs]def parse_quadrant_measurement(quad_azimuth): """ Parses a quadrant measurement of the form "AxxB", where A and B are cardinal directions and xx is an angle measured relative to those directions. In other words, it converts a measurement such as E30N into an azimuth of 60 degrees, or W10S into an azimuth of 260 degrees. For ambiguous quadrant measurements such as "N30S", a ValueError is raised. Parameters ----------- quad_azimuth : string An azimuth measurement in quadrant form. Returns ------- azi : float An azimuth in degrees clockwise from north. See Also -------- parse_azimuth """ def rotation_direction(first, second): return np.cross(_azimuth2vec(first), _azimuth2vec(second)) # Parse measurement quad_azimuth = quad_azimuth.strip() try: first_dir = quadrantletter_to_azimuth(quad_azimuth[0].upper()) sec_dir = quadrantletter_to_azimuth(quad_azimuth[-1].upper()) except KeyError: raise ValueError('{} is not a valid azimuth'.format(quad_azimuth)) angle = float(quad_azimuth[1:-1]) # Convert quadrant measurement into an azimuth direc = rotation_direction(first_dir, sec_dir) azi = first_dir + direc * angle # Catch ambiguous measurements such as N10S and raise an error if abs(direc) < 0.9: raise ValueError('{} is not a valid azimuth'.format(quad_azimuth)) # Ensure that 0 <= azi <= 360 if azi < 0: azi += 360 elif azi > 360: azi -= 360 return azi