Traverse Demo#

Demonstration of Traverse services in Dips Python API.

This script demonstrates ITraverseServices methods including: - AddTraverse: Create a new traverse with discontinuity data - GetTraverseList: List all traverses - DeleteTraverse: Remove a traverse

Code Snippet: TraverseDemo.py#
"""
Demonstration of Traverse services in Dips Python API.

This script demonstrates ITraverseServices methods including:
- AddTraverse: Create a new traverse with discontinuity data
- GetTraverseList: List all traverses
- DeleteTraverse: Remove a traverse
"""

import math
import random
from dips import DipsApp
from dips import DipsAPI_pb2
from dips import BuiltInDataFormatters

import dips.OrientationDataSetVal
import dips.DiscontinuityDataVal
import dips.SurveyDataVal
import dips.TrendPlungeVal
import dips.AngleDataVal
import dips.CustomColumnCollectionVal
import dips.FullDataFormatVal
import dips.CustomRowRawDataVal


# =============================================================================
# TRAVERSE CREATION HELPERS
# =============================================================================

def create_spot_mapping_traverse(model, name: str, num_poles: int):
    """Create a spot mapping traverse with random poles."""
    ods = model.GetDefaultOrientationDataSet()
    ods.name = name
    ods.orientation_data_type = DipsAPI_pb2.eOrientationDataType.SpotMapping
    ods.discontinuity_orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    
    # Set units
    ods.traverse_elevation_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.traverse_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_persistence_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    
    # Generate random poles
    for _ in range(num_poles):
        discontinuity = dips.DiscontinuityDataVal.DiscontinuityDataVal()
        discontinuity.orientation1.angle_radians = math.radians(random.uniform(0, 360))
        discontinuity.orientation2.angle_radians = math.radians(random.uniform(0, 90))
        discontinuity.quantity = 1.0
        ods.discontinuity_list.append(discontinuity)
    
    return ods


def create_linear_scanline_traverse(model, name: str, trend: float, plunge: float, depth: float, num_discontinuities: int):
    """Create a linear scanline traverse."""
    ods = model.GetDefaultOrientationDataSet()
    ods.name = name
    ods.orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    ods.orientation_data_type = DipsAPI_pb2.eOrientationDataType.LinearScanline
    ods.discontinuity_orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    
    # Set traverse orientation
    ods.orient1.angle_radians = math.radians(trend)
    ods.orient2.angle_radians = math.radians(plunge)
    ods.depth = depth

    # Set collar location
    ods.local_position.x = 0
    ods.local_position.y = 0
    ods.local_position.z = 0
    
    # Set units
    ods.traverse_elevation_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.traverse_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.traverse_depth_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_distance_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_persistence_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    
    # Generate discontinuities along the traverse
    cumulative_distance = 0.0
    for _ in range(num_discontinuities):
        discontinuity = dips.DiscontinuityDataVal.DiscontinuityDataVal()
        discontinuity.orientation1.angle_radians = math.radians(random.uniform(0, 360))
        discontinuity.orientation2.angle_radians = math.radians(random.uniform(0, 90))
        discontinuity.quantity = random.uniform(0.5, 2.0)
        
        cumulative_distance += random.uniform(0.5, 3.0)
        discontinuity.distance = cumulative_distance
        
        ods.discontinuity_list.append(discontinuity)
    
    return ods

def create_curved_borehole_traverse(model, name: str, collar_trend: float, collar_plunge: float, 
                                     depth: float, num_discontinuities: int):
    """Create a curved borehole traverse with survey data.
    
    Survey data defines how the borehole curves along its length.
    Each survey point specifies the orientation at a given distance down the hole.
    """
    ods = model.GetDefaultOrientationDataSet()
    ods.name = name
    ods.orientation_convention = DipsAPI_pb2.eOrientationConvention.TrendPlungeOrientation
    ods.orientation_data_type = DipsAPI_pb2.eOrientationDataType.CurvedBoreholeOrientedCore
    ods.orient1.angle_radians = math.radians(180)
    ods.discontinuity_orientation_convention = DipsAPI_pb2.eOrientationConvention.AlphaBetaOrientation

    # Set collar location
    ods.local_position.x = 0
    ods.local_position.y = 0
    ods.local_position.z = 0
    
    # Set units
    ods.traverse_elevation_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.traverse_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.survey_distance_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_distance_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_xyz_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    ods.discontinuity_persistence_unit = BuiltInDataFormatters.LengthMeterDataFormatter
    
    # Create survey data points that define how the borehole curves
    # The borehole starts at collar_trend/collar_plunge and gradually changes
    num_surveys = 5
    survey_interval = depth / num_surveys
    
    current_trend = collar_trend
    current_plunge = collar_plunge
    
    for i in range(num_surveys + 1):
        survey = dips.SurveyDataVal.SurveyDataVal()
        survey.distance = i * survey_interval
        survey.orientation = create_trend_plunge(current_trend, current_plunge)
        ods.survey_list.append(survey)
        
        # Gradually curve the borehole (simulate deviation)
        current_trend += random.uniform(-5, 5)  # Small trend deviation
        current_plunge += random.uniform(0, 3)  # Tends to steepen slightly
        current_plunge = min(current_plunge, 90)  # Cap at vertical
    
    # Generate discontinuities along the traverse
    cumulative_distance = 0.0
    for _ in range(num_discontinuities):
        discontinuity = dips.DiscontinuityDataVal.DiscontinuityDataVal()
        discontinuity.orientation1.angle_radians = math.radians(random.uniform(0, 90))
        discontinuity.orientation2.angle_radians = math.radians(random.uniform(0, 360))
        discontinuity.quantity = random.uniform(0.5, 2.0)
        
        cumulative_distance += random.uniform(1.0, 5.0)
        if cumulative_distance > depth:
            cumulative_distance = depth - 0.1
        discontinuity.distance = cumulative_distance
        
        ods.discontinuity_list.append(discontinuity)
    
    return ods

def create_trend_plunge(trend_deg: float, plunge_deg: float):
    """Create a TrendPlunge from degrees."""
    tp = dips.TrendPlungeVal.TrendPlungeVal()
    tp.trend = dips.AngleDataVal.AngleDataVal()
    tp.trend.angle_radians = math.radians(trend_deg)
    tp.plunge = dips.AngleDataVal.AngleDataVal()
    tp.plunge.angle_radians = math.radians(plunge_deg)
    return tp

# =============================================================================
# TRAVERSE DEMONSTRATIONS
# =============================================================================

def demonstrate_add_spot_mapping_traverse(model):
    """Demonstrate adding a spot mapping traverse."""
    print("\n" + "=" * 60)
    print("ADD SPOT MAPPING TRAVERSE")
    print("=" * 60)
    
    try:
        ods = create_spot_mapping_traverse(model, "Spot Mapping Demo", 20)
        result = model.AddTraverse(ods)
        print(f"  ✓ AddTraverse: '{ods.name}'")
        print(f"    Type: Spot Mapping")
        print(f"    Discontinuities: {len(ods.discontinuity_list)}")
        return result
    except Exception as e:
        print(f"  ✗ AddTraverse: {e}")
        return None


def demonstrate_add_linear_scanline_traverse(model):
    """Demonstrate adding a linear scanline traverse."""
    print("\n" + "=" * 60)
    print("ADD LINEAR SCANLINE TRAVERSE")
    print("=" * 60)
    
    try:
        ods = create_linear_scanline_traverse(model, "Linear Scanline Demo", 45, 90, 50.0, 25)
        result = model.AddTraverse(ods)
        print(f"  ✓ AddTraverse: '{ods.name}'")
        print(f"    Type: Linear Scanline")
        print(f"    Orientation: Trend=45°, Plunge=90°")
        print(f"    Depth: 50m")
        print(f"    Discontinuities: {len(ods.discontinuity_list)}")
        return result
    except Exception as e:
        print(f"  ✗ AddTraverse: {e}")
        return None


def demonstrate_add_curved_borehole_traverse(model):
    """Demonstrate adding a curved borehole traverse with survey data."""
    print("\n" + "=" * 60)
    print("ADD CURVED BOREHOLE TRAVERSE (with Survey Data)")
    print("=" * 60)
    
    try:
        ods = create_curved_borehole_traverse(
            model, 
            name="Curved Borehole Demo", 
            collar_trend=120,
            collar_plunge=70,
            depth=100.0, 
            num_discontinuities=30
        )
        result = model.AddTraverse(ods)
        print(f"  ✓ AddTraverse: '{ods.name}'")
        print(f"    Type: Curved Borehole (Survey Data)")
        print(f"    Collar Orientation: Trend=120°, Plunge=70°")
        print(f"    Depth: 100m")
        print(f"    Survey Points: {len(ods.survey_list)}")
        print(f"    Discontinuities: {len(ods.discontinuity_list)}")
        return result
    except Exception as e:
        print(f"  ✗ AddTraverse: {e}")
        return None


def demonstrate_get_traverse_list(model):
    """Demonstrate listing all traverses."""
    print("\n" + "=" * 60)
    print("GET TRAVERSE LIST")
    print("=" * 60)
    
    try:
        traverses = model.GetTraverseList()
        print(f"  ✓ GetTraverseList: {len(traverses)} traverse(s) found")
        
        for i, traverse_ref in enumerate(traverses):
            traverse_value = traverse_ref.GetValue()
            data_type = DipsAPI_pb2.eOrientationDataType.Name(traverse_value.orientation_data_type)
            print(f"    [{i+1}] Name: '{traverse_value.name}'")
            print(f"        Type: {data_type}")
            print(f"        Discontinuities: {len(traverse_value.discontinuity_list)}")
        
        return traverses
    except Exception as e:
        print(f"  ✗ GetTraverseList: {e}")
        return []


def demonstrate_delete_traverse(model, traverse_ref):
    """Demonstrate deleting a traverse."""
    print("\n" + "=" * 60)
    print("DELETE TRAVERSE")
    print("=" * 60)
    
    if traverse_ref is None:
        print("  - DeleteTraverse: No traverse to delete")
        return
    
    try:
        traverse_value = traverse_ref.GetValue()
        traverse_name = traverse_value.name
        model.DeleteTraverse(traverse_ref)
        print(f"  ✓ DeleteTraverse: '{traverse_name}'")
    except Exception as e:
        print(f"  ✗ DeleteTraverse: {e}")


def demonstrate_multiple_traverses(model):
    """Demonstrate creating multiple traverses."""
    print("\n" + "=" * 60)
    print("MULTIPLE TRAVERSE EXAMPLES")
    print("=" * 60)
    
    created_traverses = []
    
    # Create spot mapping traverse
    try:
        ods = create_spot_mapping_traverse(model, "Outcrop Survey", 30)
        result = model.AddTraverse(ods)
        created_traverses.append(result)
        print(f"  ✓ Created: 'Outcrop Survey' (Spot Mapping, 30 poles)")
    except Exception as e:
        print(f"  ✗ Failed: {e}")
    
    # Create linear scanline traverses at different orientations
    scanlines = [
        ("Borehole BH-1", 0, 90, 100.0, 40),      # Vertical borehole
        ("Scanline SL-1", 90, 0, 25.0, 15),       # Horizontal E-W
        ("Scanline SL-2", 0, 0, 30.0, 20),        # Horizontal N-S
    ]
    
    for name, trend, plunge, depth, count in scanlines:
        try:
            ods = create_linear_scanline_traverse(model, name, trend, plunge, depth, count)
            result = model.AddTraverse(ods)
            created_traverses.append(result)
            print(f"  ✓ Created: '{name}' (Linear, {count} discontinuities)")
        except Exception as e:
            print(f"  ✗ Failed to create '{name}': {e}")
    
    # Create curved borehole traverses with survey data
    curved_boreholes = [
        ("Curved BH-1", 45, 75, 150.0, 50),    # NE-trending, steep
        ("Curved BH-2", 225, 60, 120.0, 35),   # SW-trending, moderately steep
    ]
    
    for name, trend, plunge, depth, count in curved_boreholes:
        try:
            ods = create_curved_borehole_traverse(model, name, trend, plunge, depth, count)
            result = model.AddTraverse(ods)
            created_traverses.append(result)
            print(f"  ✓ Created: '{name}' (Curved, {len(ods.survey_list)} surveys, {count} discontinuities)")
        except Exception as e:
            print(f"  ✗ Failed to create '{name}': {e}")
    
    return created_traverses


def demonstrate_cleanup(model, traverses_to_delete):
    """Clean up created traverses."""
    print("\n" + "=" * 60)
    print("CLEANUP")
    print("=" * 60)
    
    for traverse_ref in traverses_to_delete:
        try:
            traverse_value = traverse_ref.GetValue()
            model.DeleteTraverse(traverse_ref)
            print(f"  ✓ Deleted: '{traverse_value.name}'")
        except Exception as e:
            print(f"  ✗ Delete failed: {e}")


# =============================================================================
# MAIN
# =============================================================================

def main():
    """Main demonstration function."""
    print("=" * 60)
    print("Dips API - Traverse Services Demo")
    print("=" * 60)
    
    # Connect to Dips
    print("\nConnecting to Dips application...")
    try:
        app = DipsApp.LaunchApp(62535)
        print("✓ Connected to Dips")
    except Exception as e:
        print(f"✗ Failed to connect: {e}")
        print("Make sure Dips is running with scripting enabled on port 62535")
        return
    
    model = app.GetModel()
    
    # Show initial state
    print("\n" + "-" * 60)
    print("INITIAL STATE")
    print("-" * 60)
    demonstrate_get_traverse_list(model)
    
    # Add spot mapping traverse
    spot_traverse = demonstrate_add_spot_mapping_traverse(model)
    
    # Add linear scanline traverse
    linear_traverse = demonstrate_add_linear_scanline_traverse(model)
    
    # Add curved borehole traverse with survey data
    curved_traverse = demonstrate_add_curved_borehole_traverse(model)
    
    # List traverses again
    demonstrate_get_traverse_list(model)
    
    # Delete the traverses
    demonstrate_delete_traverse(model, spot_traverse)
    demonstrate_delete_traverse(model, linear_traverse)
    demonstrate_delete_traverse(model, curved_traverse)
    
    # Create multiple traverses
    created_traverses = demonstrate_multiple_traverses(model)
    
    # List all traverses
    demonstrate_get_traverse_list(model)
    
    # Cleanup (commented out to keep traverses visible)
    # demonstrate_cleanup(model, created_traverses)
    
    # Show final state
    print("\n" + "-" * 60)
    print("FINAL STATE")
    print("-" * 60)
    demonstrate_get_traverse_list(model)
    
    # Show the application
    app.Show()
    
    # Summary
    print("\n" + "=" * 60)
    print("DEMONSTRATION COMPLETE")
    print("=" * 60)
    print("""
Traverse services demonstrated (3 methods):

  - AddTraverse: Create a new traverse with discontinuity data
  - GetTraverseList: List all traverses in the project
  - DeleteTraverse: Remove a traverse from the project

Traverse Types (eOrientationDataType):
  - SpotMapping: Scattered point observations
  - LinearScanline: Linear measurement line (straight borehole, scanline)
  - PlanarMapping: Planar surface mapping
  - LinearBoreholeOrientedCore: Straight borehole with oriented core
  - LinearBoreholeTeleviewer: Straight borehole with televiewer
  - CurvedBoreholeOrientedCore: Curved borehole with oriented core (uses survey data)
  - CurvedBoreholeTeleviewer: Curved borehole with televiewer (uses survey data)
  - Clinorule: Clinorule measurements

Traverse Properties:
  - name: Traverse identifier
  - orientation_data_type: Type of traverse
  - orientation_convention: How orientations are specified
  - discontinuity_list: List of discontinuity measurements
  - Various unit settings for measurements
""")


if __name__ == "__main__":
    main()