Application: Civil 3D .NET API
Category: Data Extraction
Difficulty: Intermediate
Language: C#
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.
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.
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();
}
}
[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();
}
}
[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();
}
}
PointLocation() to convert station/offset to XYZ coordinatesStationOffset() to convert XYZ coordinates to station/offsetDirectionAtStation() returns the tangent direction in radiansMistake: 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];
}
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();
}
}