Source code for sec_interp.core.utils.parsing

from __future__ import annotations

"""Coordinate and string parsing utilities."""

import re
from typing import Any


[docs] def parse_strike(value: Any) -> float | None: """Parse a strike value from various formats into an azimuth (0-360). Supports numeric values, strings, and quadrant notation (e.g., "N 30 E", "S 45 W"). Args: value: The raw strike value (string, int, float, or None). Returns: Strike in azimuth degrees (0-360) or None if parsing fails. """ if value is None: return None # If already numeric, return directly try: return float(value) except (ValueError, TypeError): pass # Normalize value text = ( str(value) .replace("°", "") .replace("º", "") .replace("ø", "") # Support for alternative degree symbol .strip() .upper() ) # Regex for quadrant notation: N/S + angle + E/W # Supports integers and decimals for the angle match = re.match(r"([NS])\s*(\d+\.?\d*)\s*([EW])", text) if not match: return None # invalid notation d1, ang, d2 = match.groups() ang = float(ang) # Quadrant rules strike = 0 # Initialize to prevent NameError if d1 == "N" and d2 == "E": strike = ang elif d1 == "N" and d2 == "W": strike = 360 - ang elif d1 == "S" and d2 == "E": strike = 180 - ang elif d1 == "S" and d2 == "W": strike = 180 + ang return strike % 360
[docs] def parse_dip(value: Any) -> tuple[float | None, float | None]: """Parse a dip value from various formats. Supports numeric dip ("45") and field notation with direction ("45 NE", "22 SW"). Args: value: The raw dip value. Returns: A tuple of (dip_angle, dip_direction_azimuth). Values are None if parsing fails. """ if value is None: return None, None text = ( str(value) .replace("°", "") .replace("º", "") .replace("ø", "") # Support for alternative degree symbol .strip() .upper() ) # Case 1: numeric only (integer or decimal) numeric_only = re.match(r"^(\d+\.?\d*)$", text) if numeric_only: return float(text), None # Case 2: full dip + direction match = re.match(r"(\d+\.?\d*)\s*([NSEW]{1,2})", text) if not match: return None, None dip, cardinal = match.groups() dip = float(dip) dip_dir = cardinal_to_azimuth(cardinal) return dip, dip_dir
[docs] def cardinal_to_azimuth(text: str) -> float | None: """Convert a cardinal direction string to its equivalent azimuth. Supports: N, NE, E, SE, S, SW, W, NW. Args: text: The cardinal direction string. Returns: The azimuth in degrees (0-360), or None if invalid. """ table = { "N": 0, "NE": 45, "E": 90, "SE": 135, "S": 180, "SW": 225, "W": 270, "NW": 315, } return table.get(text)