# sec_interp.core.utils package

sec_interp.core.utils.calculate_apparent_dip(true_strike: float, true_dip: float, line_azimuth: float) → float

  Convert true dip to apparent dip in section plane.

  The apparent dip is the inclination of a plane measured in a direction not perpendicular to the strike. In a vertical cross-section, the apparent dip depends on the angle between the strike of the plane and the azimuth of the cross-section line.

  Formula:

    tan(apparent_dip) = tan(true_dip) * sin(alpha)

    Where alpha is the angle between the strike of the plane and the direction of the cross-section (section azimuth). alpha = strike - section_azimuth

  Parameters:

    - true_strike – Strike of the geological plane (0-360 degrees).

    - true_dip – True dip of the geological plane (0-90 degrees).

    - line_azimuth – Azimuth of the cross-section line (0-360 degrees).

  Returns:

    Apparent dip in degrees. Positive values indicate dip, negative values might occur depending on quadrant but are typically normalized.

sec_interp.core.utils.calculate_bounds(topo_data: list[tuple[float, float]], geol_data: list[GeologySegment] | None = None) → dict[str, float]

  Calculate the bounding box for all profile data with padding.

  Calculates the minimum and maximum distance and elevation across topography and optional geological segments.

  Parameters:

    - topo_data – List of (distance, elevation) tuples for topography.

    - geol_data – Optional list of geological segments.

  Returns:

    Bounds containing ‘min_d’, ‘max_d’, ‘min_e’, ‘max_e’ with 5% padding.

sec_interp.core.utils.calculate_drillhole_trajectory(collar_point: Any, collar_z: float, survey_data: list[tuple[float, float, float]], section_azimuth: float, densify_step: float = 1.0, total_depth: float = 0.0) → list[tuple[float, float, float, float, float, float]]

  Calculate the 3D trajectory of a drillhole using survey data.

  Calculates X, Y, Z positions along the hole using minimum curvature or tangential approximation based on survey readings (depth, azimuth, dip).

  Parameters:

    - collar_point – Starting point (QgsPointXY or tuple).

    - collar_z – Starting elevation.

    - survey_data – List of (depth, azimuth, inclination) tuples.

    - section_azimuth – Azimuth of the section line (for relative calcs).

    - densify_step – Distance between interpolated points in meters.

    - total_depth – Total depth of the hole.

  Returns:

    List of (depth, x, y, z, 0.0, 0.0) trajectory points.

sec_interp.core.utils.calculate_interval(data_range: float) → float

  Calculate a ‘nice’ interval for axis labels based on the data range.

  Parameters:

    data_range – The total range of data values (e.g., max_d - min_d).

  Returns:

    A human-readable interval (e.g., 1, 2, 5, 10, etc.) for grid lines.

sec_interp.core.utils.calculate_line_azimuth(line_geom: qgis.core.QgsGeometry) → float

  Calculate the azimuth (compass bearing) of a line geometry.

  Calculates the azimuth based on the first two vertices of the line. Returns 0 for points or single-vertex lines.

  Parameters:

    line_geom – The QGIS line geometry.

  Returns:

    Azimuth in degrees (0-360).

sec_interp.core.utils.calculate_step_size(geom: qgis.core.QgsGeometry, raster_lyr: Any) → float

  Calculate step size based on slope and raster resolution.

  Deprecated since version Use: densify_line_by_interval() instead for better precision and simpler code. This function is kept for backward compatibility but may be removed in future versions.

  Ensures that sampling occurs at approximately one pixel intervals, accounting for the slope of the line relative to the raster grid.

  Parameters:

    - geom – The geometry to sample along.

    - raster_lyr – The raster layer being sampled.

  Returns:

    Calculated step size in map units.

sec_interp.core.utils.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.

  Parameters:

    text – The cardinal direction string.

  Returns:

    The azimuth in degrees (0-360), or None if invalid.

sec_interp.core.utils.create_buffer_geometry(geometry: qgis.core.QgsGeometry, crs: qgis.core.QgsCoordinateReferenceSystem, distance: float, segments: int = 5) → qgis.core.QgsGeometry

  Create a buffer around a geometry.

  Parameters:

    - geometry – Input geometry.

    - crs – Coordinate Reference System of the geometry.

    - distance – Buffer distance in layer units.

    - segments – Number of segments for the buffer approximation.

  Returns:

    The buffered geometry.

sec_interp.core.utils.create_coordinate_transform(bounds: dict[str, float], view_w: int, view_h: int, margin: int, vert_exag: float = 1.0) → Callable[[float, float], tuple[float, float]]

  Create a coordinate transformation function for screen projection.

  Returns a function that transforms data coordinates (distance, elevation) to pixel coordinates (x, y) based on the provided view dimensions.

  Parameters:

    - bounds – Dictionary with ‘min_d’, ‘max_d’, ‘min_e’, ‘max_e’.

    - view_w – Screen/view width in pixels.

    - view_h – Screen/view height in pixels.

    - margin – Plot margin in pixels.

    - vert_exag – Vertical exaggeration multiplier (default 1.0).

  Returns:

    A function transform(dist, elev) -> (x, y) converting data to pixels.

sec_interp.core.utils.create_distance_area(crs: qgis.core.QgsCoordinateReferenceSystem) → qgis.core.QgsDistanceArea

  Create and configure a QgsDistanceArea object.

  Configures the distance area with the provided CRS, project transform context, and associated ellipsoid for geodesic calculations.

  Parameters:

    crs – The Coordinate Reference System to use.

  Returns:

    The configured distance calculation object.

sec_interp.core.utils.create_memory_layer(layer_name: str, layer_type: str, crs: qgis.core.QgsCoordinateReferenceSystem, fields: list[qgis.core.QgsField]) → qgis.core.QgsVectorLayer

  Create a temporary memory (scratch) layer.

  Parameters:

    - layer_name – Name for the layer.

    - layer_type – QGIS geometry type string (e.g., ‘Point’, ‘LineString’).

    - crs – Coordinate reference system.

    - fields – List of fields for the layer.

  Returns:

    The created memory layer.

sec_interp.core.utils.create_shapefile_writer(*args, **kwargs) → qgis.core.QgsVectorFileWriter

  Delegate to create_vector_writer (deprecated shim).

sec_interp.core.utils.densify_line_by_interval(geometry: qgis.core.QgsGeometry, interval: float) → qgis.core.QgsGeometry

  Densify a line geometry by a specific distance interval.

  Adds intermediate vertices to ensure segments are no longer than the interval.

  Parameters:

    - geometry – Line geometry to densify.

    - interval – Maximum distance between vertices (in CRS units).

  Returns:

    The densified QgsGeometry.

sec_interp.core.utils.extract_feature_attributes(feature: Any) → dict[str, Any]

  Extract attributes from a QgsFeature into a pure Python dictionary.

  Ensures all values are converted to standard Python primitives to avoid threading issues with QVariant objects in QGIS 4/Qt6.

  Parameters:

    feature – The QgsFeature object.

  Returns:

    A dictionary mapping field names to sanitized attribute values.

sec_interp.core.utils.filter_features_by_buffer(features_layer: QgsVectorLayer, buffer_geometry: QgsGeometry, buffer_crs: QgsCoordinateReferenceSystem | None = None) → list[QgsFeature]

  Filter features that intersect with buffer using spatial index.

  Parameters:

    - features_layer – Layer containing features to filter.

    - buffer_geometry – Buffer geometry to use for spatial filter.

    - buffer_crs – CRS of the buffer geometry (optional).

  Returns:

    List of QgsFeature objects that intersect the query buffer.

sec_interp.core.utils.get_line_start_point(geometry: qgis.core.QgsGeometry) → qgis.core.QgsPointXY

  Get the start point of a line geometry.

  Handles both singlepart and multipart line geometries.

  Parameters:

    geometry – The line geometry.

  Returns:

    The first vertex of the first part of the geometry.

sec_interp.core.utils.get_line_vertices(geometry: qgis.core.QgsGeometry) → list[qgis.core.QgsPointXY]

  Extract vertices specifically from a line or multiline geometry.

  Parameters:

    geometry – A QGIS geometry of type LineGeometry.

  Returns:

    A flat list of vertices.

  Raises:

    ValueError – If the geometry is null, not a line, or contains no vertices.

sec_interp.core.utils.interpolate_elevation(topo_data: list[tuple[float, float]], distance: float) → float

  Interpolate elevation at a given distance along the topography profile.

  Uses linear interpolation between the two nearest sampled points.

  Parameters:

    - topo_data – List of (distance, elevation) tuples.

    - distance – Horizontal distance along the section.

  Returns:

    The interpolated elevation value (Z) at that distance.

sec_interp.core.utils.interpolate_intervals_on_trajectory(trajectory: list[tuple], intervals: list[tuple[float, float, Any]], buffer_width: float) → list[tuple[Any, list[tuple[float, float]], list[tuple[float, float, float]], list[tuple[float, float, float]]]]

  Interpolate interval attributes along drillhole trajectory.

  Ensures that even short intervals have at least two points by interpolating at the exact from/to depths on the trajectory segments.

  Parameters:

    - trajectory – List of (depth, x, y, z, dist_along, offset, nx, ny) tuples.

    - intervals – List of (from_depth, to_depth, attribute) tuples.

    - buffer_width – Maximum perpendicular offset to include a point.

  Returns:

    List of tuples (attribute, points_2d, points_3d, points_3d_proj).

sec_interp.core.utils.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”). Also supports finding the dip part in combined strings (e.g. “N30E, 45NW”).

  Parameters:

    value – The raw dip value.

  Returns:

    A tuple of (dip_angle, dip_direction_azimuth). Values are None if parsing fails.

sec_interp.core.utils.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”). Also handles combined strike/dip strings by splitting them.

  Parameters:

    value – The raw strike value (string, int, float, or None).

  Returns:

    Strike in azimuth degrees (0-360) or None if parsing fails.

sec_interp.core.utils.prepare_profile_context(line_lyr: qgis.core.QgsVectorLayer) → tuple[qgis.core.QgsGeometry, qgis.core.QgsPointXY, qgis.core.QgsDistanceArea]

  Prepare a common context for profile calculation operations.

  Parameters:

    line_lyr – The cross-section line vector layer.

  Returns:

    - line_geom: The geometry of the section line.

    - line_start: The starting point of the line.

    - distance_area: Fully configured geodesic distance object.

  Return type:

    A tuple containing

  Raises:

    ValueError – If the input layer is empty or has invalid geometry.

sec_interp.core.utils.project_trajectory_to_section(trajectory: list[tuple], line_geom: qgis.core.QgsGeometry, line_start: Any, distance_area: qgis.core.QgsDistanceArea) → list[tuple[float, float, float, float, float, float, float, float]]

  Project drillhole trajectory points onto the section line.

  Calculates the 2D projection (distance along section) and the offset (distance from section) for each point in a 3D trajectory.

  Parameters:

    - trajectory – List of 3D trajectory points (depth, x, y, z, …).

    - line_geom – Section line geometry.

    - line_start – Reference start point for distance calculation.

    - distance_area – Distance calculation utility.

  Returns:

    List of (depth, x, y, z, dist_along, offset, proj_x, proj_y) tuples.

sec_interp.core.utils.sample_elevation_along_line(geometry: QgsGeometry, raster_layer: QgsRasterLayer, band_number: int, distance_area: QgsDistanceArea, reference_point: QgsPointXY | None = None, interval: float | None = None) → list[QgsPointXY]

  Sample elevation values along a line geometry from a raster layer.

  Densifies the line at raster resolution and samples the elevation at each vertex.

  Parameters:

    - geometry – The line geometry to sample along.

    - raster_layer – The source DEM raster layer.

    - band_number – The raster band index to sample.

    - distance_area – Object for geodesic distance calculations.

    - reference_point – Optional start point for distance measurements.

    - interval – Optional sampling interval. If None, uses raster resolution.

  Returns:

    A list of QgsPointXY objects where x is horizontal distance and y is elevation.

sec_interp.core.utils.sample_point_elevation(raster_layer: qgis.core.QgsRasterLayer, point: qgis.core.QgsPointXY, band_number: int = 1) → float

  Sample elevation from a raster layer at a specific 2D coordinate.

  Parameters:

    - raster_layer – The DEM raster layer.

    - point – The coordinate point (X, Y) to sample.

    - band_number – The raster band index (default 1).

  Returns:

    The sampled elevation value (Z) or 0.0 if the point is out of bounds.

## Subpackages

- sec_interp.core.utils.geometry_utils package
  - Submodules
    - sec_interp.core.utils.geometry_utils.extraction module
      - `extract_all_vertices()`
      - `extract_lines_from_geometry()`
      - `get_line_vertices()`
    - sec_interp.core.utils.geometry_utils.filtering module
      - `filter_features_by_buffer()`
    - sec_interp.core.utils.geometry_utils.measurement module
      - `calculate_polyline_metrics()`
    - sec_interp.core.utils.geometry_utils.optimization module
      - `PreviewOptimizer`
    - sec_interp.core.utils.geometry_utils.processing module
      - `calculate_segment_range()`
      - `create_buffer_geometry()`
      - `create_memory_layer()`
      - `densify_line_by_interval()`
      - `interpolate_segment_points()`
      - `run_geometry_operation()`

## Submodules

- sec_interp.core.utils.drillhole module
  - `calculate_drillhole_trajectory()`
  - `interpolate_intervals_on_trajectory()`
  - `project_trajectory_to_section()`
- sec_interp.core.utils.geology module
  - `calculate_apparent_dip()`
- sec_interp.core.utils.geometry module
  - `create_buffer_geometry()`
  - `create_memory_layer()`
  - `densify_line_by_interval()`
  - `extract_all_vertices()`
  - `filter_features_by_buffer()`
  - `get_line_vertices()`
  - `run_geometry_operation()`
- sec_interp.core.utils.i18n module
  - `TranslatableMixin`
    - `TranslatableMixin.tr()`
- sec_interp.core.utils.io module
  - `create_shapefile_writer()`
  - `create_vector_writer()`
- sec_interp.core.utils.metadata_reader module
  - `clear_metadata_cache()`
  - `read_plugin_metadata()`
- sec_interp.core.utils.parsing module
  - `cardinal_to_azimuth()`
  - `extract_feature_attributes()`
  - `parse_dip()`
  - `parse_strike()`
- sec_interp.core.utils.qgis module
  - `LayerResolver`
    - `LayerResolver.clear_cache()`
    - `LayerResolver.invalidate()`
    - `LayerResolver.resolve()`
  - `resolve_layer()`
- sec_interp.core.utils.qt6_compat module
  - `apply_qt6_patches()`
- sec_interp.core.utils.rendering module
  - `calculate_bounds()`
  - `calculate_interval()`
  - `create_coordinate_transform()`
- sec_interp.core.utils.resource_manager module
  - `ResourceManager`
    - `ResourceManager.cleanup_layer()`
  - `temporary_file()`
  - `temporary_memory_layer()`
- sec_interp.core.utils.safe_loader module
  - `SafeLoader`
    - `SafeLoader.get_class()`
    - `SafeLoader.lazy_load()`
    - `SafeLoader.safe_import()`
- sec_interp.core.utils.sampling module
  - `interpolate_elevation()`
  - `prepare_profile_context()`
  - `sample_elevation_along_line()`
  - `sample_point_elevation()`
- sec_interp.core.utils.spatial module
  - `calculate_line_azimuth()`
  - `calculate_step_size()`
  - `create_distance_area()`
  - `get_line_start_point()`