| name | c3d-featurelines |
| description | FeatureLine and CorridorFeatureLine — creation, elevation editing, grading, export |
Civil 3D Feature Lines
Use this skill when working with feature lines - creating standalone/grading feature lines, managing elevations and geometry, accessing corridor feature lines, or exporting corridor feature lines to other object types.
Two Types of Feature Lines
Civil 3D has two distinct feature line types that share a name but differ fundamentally:
| Aspect | FeatureLine (Grading) | CorridorFeatureLine (Corridor) |
|---|
| Base class | Feature : Entity (DBObject) | Corridor sub-object (not a DBObject) |
| Persistence | Stored in drawing, has ObjectId | Computed on corridor rebuild |
| Creation | FeatureLine.Create() from polyline | Automatic from assembly point codes |
| Visibility | Prospector > Sites > Feature Lines | Corridor > Baselines > Feature Lines |
| Editing | Direct point/elevation editing | Read-only (change assembly/corridor) |
| Namespace | Autodesk.Civil.DatabaseServices | Autodesk.Civil.DatabaseServices |
Accessing Grading Feature Lines
CivilDocument doc = CivilApplication.ActiveDocument;
ObjectIdCollection sitelessFLs = doc.GetSitelessFeatureLineIds();
foreach (ObjectId siteId in doc.GetSiteIds())
{
Site site = ts.GetObject(siteId, OpenMode.ForRead) as Site;
ObjectIdCollection siteFLs = site.GetFeatureLineIds();
foreach (ObjectId flId in siteFLs)
{
FeatureLine fl = ts.GetObject(flId, OpenMode.ForRead) as FeatureLine;
ed.WriteMessage("Feature Line: {0}, Length2D: {1:F2}\n",
fl.Name, fl.Length2D);
}
}
Creating Feature Lines
Feature lines are created from existing geometry: Line, Arc, Polyline, Polyline2d, or Polyline3d.
ObjectId flId = FeatureLine.Create("My Feature Line", polylineId);
ObjectId flId = FeatureLine.Create("My Feature Line", polylineId, siteId);
Practical Workflow: Draw Then Create
using (Polyline pline = new Polyline())
{
pline.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
pline.AddVertexAt(1, new Point2d(100, 0), 0, 0, 0);
pline.AddVertexAt(2, new Point2d(200, 50), 0, 0, 0);
BlockTableRecord btr = ts.GetObject(db.CurrentSpaceId,
OpenMode.ForWrite) as BlockTableRecord;
ObjectId plineId = btr.AppendEntity(pline);
ts.AddNewlyCreatedDBObject(pline, true);
ObjectId flId = FeatureLine.Create("Centerline FL", plineId);
}
Properties
FeatureLine fl = ts.GetObject(flId, OpenMode.ForRead) as FeatureLine;
double len2d = fl.Length2D;
double len3d = fl.Length3D;
double maxGrade = fl.MaxGrade;
double minGrade = fl.MinGrade;
double maxElev = fl.MaxElevation;
double minElev = fl.MinElevation;
int totalPts = fl.PointsCount;
int piPts = fl.PIPointsCount;
int elevPts = fl.ElevationPointsCount;
int curves = fl.CurvesCount;
ObjectId siteId = fl.SiteId;
ObjectId surfId = fl.RelativeSurfaceId;
fl.RelativeSurfaceId = newSurfaceId;
string styleName = fl.StyleName;
fl.StyleId = newStyleId;
fl.StyleName = "New Style";
Getting Points
Point3dCollection piPoints = fl.GetPoints(FeatureLinePointType.PIPoint);
Point3dCollection elevPoints = fl.GetPoints(FeatureLinePointType.ElevationPoint);
Point3dCollection allPoints = fl.GetPoints(FeatureLinePointType.AllPoints);
foreach (Point3d pt in allPoints)
{
ed.WriteMessage(" ({0:F2}, {1:F2}, {2:F2})\n", pt.X, pt.Y, pt.Z);
}
Elevation Management
Set Point Elevation by Index
FeatureLine fl = ts.GetObject(flId, OpenMode.ForWrite) as FeatureLine;
fl.SetPointElevation(0, 100.0);
fl.SetPointElevation(fl.PointsCount - 1, 105.0);
Assign Elevations from Surface
fl.AssignElevationsFromSurface(surfaceId, false);
fl.AssignElevationsFromSurface(surfaceId, true);
Relative Elevations
bool isRelative = fl.IsElevationRelativeToSurface(point);
double relElev = fl.GetPointRelativeElevation(point);
fl.SetPointRelativeElevation(point, true, 2.5);
PI and Elevation Point Manipulation
Points are identified by Point3d coordinates, not by index.
Elevation Points
FeatureLine fl = ts.GetObject(flId, OpenMode.ForWrite) as FeatureLine;
fl.InsertElevationPoint(new Point3d(50, 0, 102.0));
Point3dCollection pts = new Point3dCollection();
pts.Add(new Point3d(25, 0, 101.0));
pts.Add(new Point3d(75, 0, 103.0));
fl.InsertElevationPoints(pts);
fl.DeleteElevationPoint(new Point3d(50, 0, 102.0));
fl.DeleteElevationPoints(pts);
PI Points
fl.InsertPIPoint(new Point3d(150, 25, 104.0));
fl.DeletePIPoint(new Point3d(150, 25, 104.0));
Grade and Geometry Analysis
All analysis methods take a Point3d that must lie on the feature line.
Point3dCollection points = fl.GetPoints(FeatureLinePointType.AllPoints);
Point3d pt = points[1];
double gradeIn = fl.GetGradeInAtPoint(pt);
double gradeOut = fl.GetGradeOutAtPoint(pt);
double deflection = fl.GetDeflectionAngleAtPoint(pt);
double dist3d = fl.Get3dDistanceAtPoint(pt);
Segment Bulge and Curves
Bulge and curve operations use segment index (0-based).
FeatureLine fl = ts.GetObject(flId, OpenMode.ForWrite) as FeatureLine;
double bulge = fl.GetBulge(0);
fl.SetBulge(0, 0.5);
double radius = fl.GetCurveRadius(1);
fl.SetCurveRadius(1, 50.0);
Extending Feature Lines
FeatureLine fl = ts.GetObject(flId, OpenMode.ForWrite) as FeatureLine;
fl.ExtendWithFixedLine(true, 25.0);
fl.ExtendWithFixedLine(false, 50.0);
fl.ExtendWithFixedLine(false, new Point3d(300, 0, 106.0));
fl.ExtendWithFixedCurve(false, new Point3d(250, 10, 105.0),
new Point3d(300, 0, 106.0));
fl.ExtendWithFixedCurve(false, new Point3d(300, 0, 106.0));
Site Management
Feature lines can live in a named site or be siteless. Sites enforce topology interactions between feature lines, alignments, and parcels within the same site.
ObjectId siteId = Site.Create(doc, "Grading Site");
Site site = ts.GetObject(siteId, OpenMode.ForRead) as Site;
ObjectIdCollection flIds = site.GetFeatureLineIds();
FeatureLine.MoveToSite(flId, siteId);
FeatureLine.MoveToNoneSite(flId);
Styles
FeatureLineStyleCollection styles = doc.Styles.FeatureLineStyles;
ObjectId styleId = styles.Add("My FL Style");
FeatureLineStyle style = ts.GetObject(styleId, OpenMode.ForWrite) as FeatureLineStyle;
DisplayStyle planStyle = style.GetFeatureLineDisplayStylePlan();
DisplayStyle modelStyle = style.GetFeatureLineDisplayStyleModel();
DisplayStyle flProfile = style.GetDisplayStyleProfile(
FeatureLineDisplayStyleProfileType.FeatureLine);
DisplayStyle beginVertex = style.GetDisplayStyleProfile(
FeatureLineDisplayStyleProfileType.BeginningVertex);
DisplayStyle internalVertex = style.GetDisplayStyleProfile(
FeatureLineDisplayStyleProfileType.InternalVertex);
DisplayStyle endVertex = style.GetDisplayStyleProfile(
FeatureLineDisplayStyleProfileType.EndingVertex);
DisplayStyle sectionMarker = style.GetSectionMarkerDisplayStyleSection();
style.ProfileBeginningVertexMarkerStyleId = markerStyleId;
style.ProfileInternalVertexMarkerStyleId = markerStyleId;
style.ProfileEndingVertexMarkerStyleId = markerStyleId;
style.SectionMarkerStyleId = markerStyleId;
FeatureLine fl = ts.GetObject(flId, OpenMode.ForWrite) as FeatureLine;
fl.StyleId = styleId;
Corridor Feature Lines
Corridor feature lines are computed results connecting assembly points that share the same code name along a baseline. They are not DBObjects and cannot be edited directly.
Accessing via Baseline
Corridor corridor = ts.GetObject(corridorId, OpenMode.ForRead) as Corridor;
foreach (Baseline baseline in corridor.Baselines)
{
BaselineFeatureLines mainBFL = baseline.MainBaselineFeatureLines;
string[] codes = mainBFL.CodeNames();
foreach (FeatureLineCollection flCol in mainBFL.FeatureLineCollectionMap)
{
foreach (CorridorFeatureLine cfl in flCol)
{
ed.WriteMessage("Code: {0}, Points: {1}\n",
cfl.CodeName, cfl.FeatureLinePoints.Count);
}
}
FeatureLineCollection topFLs = mainBFL.FeatureLineCollectionMap["Top"];
CorridorFeatureLine firstTop = topFLs[0];
foreach (BaselineFeatureLines obfl in baseline.OffsetBaselineFeatureLinesCol)
{
foreach (FeatureLineCollection flCol in obfl.FeatureLineCollectionMap)
{
foreach (CorridorFeatureLine cfl in flCol)
{
ed.WriteMessage("Offset FL: {0}\n", cfl.CodeName);
}
}
}
}
FeatureLineCollectionMap
FeatureLineCollectionMap map = mainBFL.FeatureLineCollectionMap;
string[] codeNames = map.CodeNames();
FeatureLineCollection flCol = map["ETW"];
FeatureLineCollection flCol2 = map[0];
int codeCount = map.Count;
FeatureLineCollection Properties
FeatureLineCollection flCol = map["Top"];
FeatureLineConnectDirectionType dir = flCol.ConnectDirection;
flCol.ConnectDirection = FeatureLineConnectDirectionType.Outward;
bool connectExtra = flCol.IsConnectExtraPoints;
flCol.IsConnectExtraPoints = true;
FeatureLineCodeInfo codeInfo = flCol.FeatureLineCodeInfo;
string codeName = codeInfo.CodeName;
bool isConnected = codeInfo.IsConnected;
FeatureLinePoint (Corridor)
CorridorFeatureLine cfl = flCol[0];
foreach (FeatureLinePoint flPt in cfl.FeatureLinePoints)
{
Point3d xyz = flPt.XYZ;
double station = flPt.Station;
double offset = flPt.Offset;
bool isBreak = flPt.IsBreak;
ed.WriteMessage(" Sta {0:F2}, Offset {1:F2}, Elev {2:F2}, Break: {3}\n",
station, offset, xyz.Z, isBreak);
}
Corridor Feature Line Style
CorridorFeatureLine cfl = flCol[0];
ObjectId styleId = cfl.StyleId;
string styleName = cfl.StyleName;
cfl.StyleId = newStyleId;
cfl.StyleName = "New Style";
ObjectId corrId = cfl.CorridorId;
Exporting Corridor Feature Lines
Corridor feature lines can be exported to persistent drawing objects.
Export as Polyline3d
CorridorFeatureLine cfl = flCol[0];
ObjectIdCollection pline3dIds = cfl.ExportAsPolyline3dCollection();
Export as Grading Feature Line
GradingSmoothOption smoothOpt = new GradingSmoothOption(
false,
0.0,
0.0,
0.0);
ObjectId gradingFLId = cfl.ExportAsGradingFeatureLine(
siteId,
true,
"Exported ETW",
layerId,
featureLineStyleId,
smoothOpt);
ObjectId gradingFLId2 = cfl.ExportAsGradingFeatureLine(siteId, false);
Export as Alignment
ObjectId alignId = cfl.ExportAsAlignment(
"ETW Alignment",
siteId,
layerId,
alignmentStyleId,
labelSetId,
AlignmentType.Centerline
);
Export as Profile
ObjectId profileId = cfl.ExportAsProfile(
"ETW Profile",
alignmentId,
layerId,
profileStyleId,
labelSetId
);
Enums
FeatureLinePointType
Used with FeatureLine.GetPoints():
| Value | Description |
|---|
PIPoint | PI (point of intersection) vertices that define the horizontal path |
ElevationPoint | Intermediate elevation/grade break points |
AllPoints | All points (PI + elevation combined) |
FeatureLineConnectDirectionType
Used with FeatureLineCollection.ConnectDirection:
| Value | Description |
|---|
Inward | Connect feature line points inward toward baseline |
Outward | Connect feature line points outward from baseline |
FeatureLineDisplayStyleProfileType
Used with FeatureLineStyle.GetDisplayStyleProfile():
| Value | Int | Description |
|---|
FeatureLine | 0 | The feature line itself in profile view |
BeginningVertex | 2 | Starting vertex marker |
InternalVertex | 3 | Internal vertex markers |
EndingVertex | 4 | Ending vertex marker |
Note: Values are non-sequential (0, 2, 3, 4 - no value 1).
GradingSmoothOption
Struct used when exporting corridor feature lines as grading feature lines:
| Property | Type | Description |
|---|
NeedSmooth | bool | Whether to apply smoothing |
ArcInclusionDistance | double | Arc inclusion distance for smoothing |
WeedingDistance | double | Point weeding distance |
HorizDeviation | double | Horizontal deviation tolerance |
Gotchas
Point manipulation uses Point3d, not index. InsertElevationPoint, DeleteElevationPoint, InsertPIPoint, DeletePIPoint all take Point3d coordinates. Only SetPointElevation uses an integer index.
StyleId has no getter on grading FeatureLine. Use StyleName to read the current style. CorridorFeatureLine.StyleId has both getter and setter.
SetBulge parameter is named "bugle" in the API. This is a typo in the Civil 3D API source. The method works correctly despite the misspelling.
GradingSmoothOption constructor parameter is named "isSoomth". Another API typo. Use NeedSmooth property for clarity.
No managed API to draw a feature line from scratch. FeatureLine.Create() requires an existing Line, Arc, Polyline, Polyline2d, or Polyline3d as input. To create a feature line from arbitrary points, first create a polyline, then call Create().
AssignElevationsFromSurface with grade breaks inserts many points. When bIncIntermediate=true, the method inserts elevation points at every TIN edge crossing. This can add hundreds of points on complex surfaces. Use false for simple draping.
Site topology interactions. Feature lines in a site interact topologically with alignments and parcels in the same site. Moving a feature line into a site may split parcels or create unexpected topology. Use siteless feature lines to avoid this.
FeatureLineDisplayStyleProfileType enum is non-sequential. Values are 0, 2, 3, 4 (no value 1). Do not iterate with a simple integer loop - use the named enum values.
Related Skills
c3d-corridors - Corridors that generate corridor feature lines
c3d-alignments - Alignments used as corridor baselines; feature lines can export as alignments
c3d-profiles - Feature lines can export as profiles
c3d-surfaces - Surfaces used for elevation assignment and relative elevations