🛣️ Civil 3D APIintermediateData Extraction

Extract Civil 3D Alignment Data Programmatically

Access and export alignment geometry, stations, and profile data from Civil 3D using the .NET API.

6 views12/14/2025

Extract Alignment Data Programmatically in Civil 3D

Application: Civil 3D .NET API
Category: Data Extraction
Difficulty: Intermediate
Language: C#

Problem

You need to extract geometric data from Civil 3D alignments - such as station values, coordinates, tangent directions, and curve parameters - for use in external applications or reports.

Solution

Use the Alignment class and its Entities collection to access detailed geometric information. Civil 3D provides methods to convert between stations and XYZ coordinates, making it easy to extract precise data along the alignment path.

Code Example

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Civil.ApplicationServices;
using Autodesk.Civil.DatabaseServices;
using System.Text;

[CommandMethod("EXTRACTALIGNMENT")]
public void ExtractAlignmentData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    CivilDocument civilDoc = CivilApplication.ActiveDocument;
    
    // Prompt user to select an alignment
    PromptEntityOptions peo = new PromptEntityOptions("\nSelect alignment: ");
    peo.SetRejectMessage("\nMust be an alignment.");
    peo.AddAllowedClass(typeof(Alignment), true);
    
    PromptEntityResult per = ed.GetEntity(peo);
    if (per.Status != PromptStatus.OK) return;
    
    using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
    {
        Alignment alignment = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Alignment;
        
        StringBuilder report = new StringBuilder();
        report.AppendLine($"Alignment: {alignment.Name}");
        report.AppendLine($"Length: {alignment.Length:F3}");
        report.AppendLine($"Start Station: {alignment.StartingStation:F3}");
        report.AppendLine($"End Station: {alignment.EndingStation:F3}");
        report.AppendLine();
        
        // Extract data at regular intervals
        double interval = 10.0; // Every 10 units
        double currentStation = alignment.StartingStation;
        
        report.AppendLine("Station,Easting,Northing,Elevation,Direction");
        
        while (currentStation <= alignment.EndingStation)
        {
            try
            {
                // Get coordinates at this station
                double x, y;
                alignment.PointLocation(currentStation, 0, out x, out y);
                
                // Get direction (bearing) at this station
                double direction = alignment.DirectionAtStation(currentStation);
                
                // Get elevation from profile if available
                double elevation = 0;
                if (alignment.ProfileCount > 0)
                {
                    ObjectId profileId = alignment.GetProfileIds()[0];
                    Profile profile = tr.GetObject(profileId, OpenMode.ForRead) as Profile;
                    elevation = profile.ElevationAt(currentStation);
                }
                
                report.AppendLine($"{currentStation:F3},{x:F3},{y:F3},{elevation:F3},{direction:F6}");
            }
            catch
            {
                // Skip stations that cause errors
            }
            
            currentStation += interval;
        }
        
        // Save report to file
        string reportPath = @"C:\Temp\AlignmentData.csv";
        System.IO.File.WriteAllText(reportPath, report.ToString());
        
        ed.WriteMessage($"\nAlignment data exported to: {reportPath}");
        
        tr.Commit();
    }
}

Advanced: Extract Curve and Spiral Parameters

[CommandMethod("EXTRACTCURVES")]
public void ExtractCurveData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    
    PromptEntityOptions peo = new PromptEntityOptions("\nSelect alignment: ");
    peo.SetRejectMessage("\nMust be an alignment.");
    peo.AddAllowedClass(typeof(Alignment), true);
    
    PromptEntityResult per = ed.GetEntity(peo);
    if (per.Status != PromptStatus.OK) return;
    
    using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
    {
        Alignment alignment = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Alignment;
        
        StringBuilder report = new StringBuilder();
        report.AppendLine($"Horizontal Geometry for: {alignment.Name}\n");
        
        // Iterate through alignment entities
        foreach (AlignmentEntity entity in alignment.Entities)
        {
            switch (entity.EntityType)
            {
                case AlignmentEntityType.Line:
                    AlignmentLine line = entity as AlignmentLine;
                    report.AppendLine($"LINE:");
                    report.AppendLine($"  Start Station: {line.StartStation:F3}");
                    report.AppendLine($"  End Station: {line.EndStation:F3}");
                    report.AppendLine($"  Length: {line.Length:F3}");
                    report.AppendLine($"  Direction: {line.Direction:F6}");
                    break;
                    
                case AlignmentEntityType.Arc:
                    AlignmentArc arc = entity as AlignmentArc;
                    report.AppendLine($"CURVE:");
                    report.AppendLine($"  Start Station: {arc.StartStation:F3}");
                    report.AppendLine($"  End Station: {arc.EndStation:F3}");
                    report.AppendLine($"  Radius: {arc.Radius:F3}");
                    report.AppendLine($"  Length: {arc.Length:F3}");
                    report.AppendLine($"  Delta Angle: {arc.Delta * 180 / System.Math.PI:F3}°");
                    report.AppendLine($"  Direction: {(arc.Clockwise ? "Clockwise" : "Counter-clockwise")}");
                    break;
                    
                case AlignmentEntityType.Spiral:
                    AlignmentSpiral spiral = entity as AlignmentSpiral;
                    report.AppendLine($"SPIRAL:");
                    report.AppendLine($"  Start Station: {spiral.StartStation:F3}");
                    report.AppendLine($"  End Station: {spiral.EndStation:F3}");
                    report.AppendLine($"  Length: {spiral.Length:F3}");
                    report.AppendLine($"  Radius In: {spiral.RadiusIn:F3}");
                    report.AppendLine($"  Radius Out: {spiral.RadiusOut:F3}");
                    report.AppendLine($"  Type: {spiral.SpiralDefinition}");
                    break;
            }
            
            report.AppendLine();
        }
        
        // Display report
        ed.WriteMessage("\n" + report.ToString());
        
        tr.Commit();
    }
}

Extract Profile Data

[CommandMethod("EXTRACTPROFILE")]
public void ExtractProfileData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    
    PromptEntityOptions peo = new PromptEntityOptions("\nSelect alignment: ");
    peo.SetRejectMessage("\nMust be an alignment.");
    peo.AddAllowedClass(typeof(Alignment), true);
    
    PromptEntityResult per = ed.GetEntity(peo);
    if (per.Status != PromptStatus.OK) return;
    
    using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
    {
        Alignment alignment = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Alignment;
        
        if (alignment.ProfileCount == 0)
        {
            ed.WriteMessage("\nNo profiles found on this alignment.");
            return;
        }
        
        // Get first profile
        ObjectId profileId = alignment.GetProfileIds()[0];
        Profile profile = tr.GetObject(profileId, OpenMode.ForRead) as Profile;
        
        StringBuilder report = new StringBuilder();
        report.AppendLine($"Profile: {profile.Name}");
        report.AppendLine("Station,Elevation,Grade");
        
        double interval = 10.0;
        double currentStation = alignment.StartingStation;
        
        while (currentStation <= alignment.EndingStation)
        {
            try
            {
                double elevation = profile.ElevationAt(currentStation);
                double grade = profile.GradeAt(currentStation) * 100; // Convert to percentage
                
                report.AppendLine($"{currentStation:F3},{elevation:F3},{grade:F3}%");
            }
            catch
            {
                // Skip invalid stations
            }
            
            currentStation += interval;
        }
        
        string reportPath = @"C:\Temp\ProfileData.csv";
        System.IO.File.WriteAllText(reportPath, report.ToString());
        
        ed.WriteMessage($"\nProfile data exported to: {reportPath}");
        
        tr.Commit();
    }
}

Key Points

  • Use PointLocation() to convert station/offset to XYZ coordinates
  • Use StationOffset() to convert XYZ coordinates to station/offset
  • DirectionAtStation() returns the tangent direction in radians
  • Profile elevations are accessed through the Profile object, not the Alignment
  • Always check if profiles exist before accessing them

Common Pitfalls

Mistake: Not handling stations outside the alignment range

// WRONG - may throw exception
double x, y;
alignment.PointLocation(station, 0, out x, out y);

// CORRECT - check station range first
if (station >= alignment.StartingStation && station <= alignment.EndingStation)
{
    double x, y;
    alignment.PointLocation(station, 0, out x, out y);
}

Mistake: Assuming profiles always exist

// WRONG - crashes if no profiles
ObjectId profileId = alignment.GetProfileIds()[0];

// CORRECT - check count first
if (alignment.ProfileCount > 0)
{
    ObjectId profileId = alignment.GetProfileIds()[0];
}

Related Topics

  • Alignment creation and modification
  • Profile view manipulation
  • Corridor modeling
  • Station equations
  • Superelevation data

Code Example

C#
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Civil.ApplicationServices;
using Autodesk.Civil.DatabaseServices;
using System.Text;

[CommandMethod("EXTRACTALIGNMENT")]
public void ExtractAlignmentData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    CivilDocument civilDoc = CivilApplication.ActiveDocument;
    
    // Prompt user to select an alignment
    PromptEntityOptions peo = new PromptEntityOptions("\nSelect alignment: ");
    peo.SetRejectMessage("\nMust be an alignment.");
    peo.AddAllowedClass(typeof(Alignment), true);
    
    PromptEntityResult per = ed.GetEntity(peo);
    if (per.Status != PromptStatus.OK) return;
    
    using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
    {
        Alignment alignment = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Alignment;
        
        StringBuilder report = new StringBuilder();
        report.AppendLine($"Alignment: {alignment.Name}");
        report.AppendLine($"Length: {alignment.Length:F3}");
        report.AppendLine($"Start Station: {alignment.StartingStation:F3}");
        report.AppendLine($"End Station: {alignment.EndingStation:F3}");
        report.AppendLine();
        
        // Extract data at regular intervals
        double interval = 10.0; // Every 10 units
        double currentStation = alignment.StartingStation;
        
        report.AppendLine("Station,Easting,Northing,Elevation,Direction");
        
        while (currentStation <= alignment.EndingStation)
        {
            try
            {
                // Get coordinates at this station
                double x, y;
                alignment.PointLocation(currentStation, 0, out x, out y);
                
                // Get direction (bearing) at this station
                double direction = alignment.DirectionAtStation(currentStation);
                
                // Get elevation from profile if available
                double elevation = 0;
                if (alignment.ProfileCount > 0)
                {
                    ObjectId profileId = alignment.GetProfileIds()[0];
                    Profile profile = tr.GetObject(profileId, OpenMode.ForRead) as Profile;
                    elevation = profile.ElevationAt(currentStation);
                }
                
                report.AppendLine($"{currentStation:F3},{x:F3},{y:F3},{elevation:F3},{direction:F6}");
            }
            catch
            {
                // Skip stations that cause errors
            }
            
            currentStation += interval;
        }
        
        // Save report to file
        string reportPath = @"C:\Temp\AlignmentData.csv";
        System.IO.File.WriteAllText(reportPath, report.ToString());
        
        ed.WriteMessage($"\nAlignment data exported to: {reportPath}");
        
        tr.Commit();
    }
}