| name | c3d-catchments |
| description | Catchment areas, flow paths, watershed, drainage analysis, labels |
Civil 3D Catchments
Use this skill when working with catchment areas, flow paths, watershed delineation, or drainage analysis in Civil 3D.
Catchment Overview
Catchments in Civil 3D represent drainage areas on a surface where water flows to a common discharge point. They are fundamental to stormwater and hydrology analysis, linking surface terrain data to pipe network design. Each catchment is defined by a boundary polygon, a reference surface, and a discharge point where collected runoff exits the catchment area.
Catchments are typically derived from surface analysis (watershed delineation) or created manually by drawing boundaries. They store hydrological properties such as area, runoff coefficient, rainfall intensity, and time of concentration for use in the rational method and other drainage calculations.
The .NET API for catchments is more limited than the interactive UI. Many creation and analysis operations must be performed through commands rather than direct API calls.
Accessing Catchments
CivilDocument and the Catchment Collection
Catchments are accessed through CivilDocument and its site collections. Catchments belong to a Site, which groups related drainage objects.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.Civil;
using Autodesk.Civil.DatabaseServices;
Document acadDoc = Application.DocumentManager.MdiActiveDocument;
Database db = acadDoc.Database;
CivilDocument doc = CivilDocument.GetCivilDocument(db);
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectIdCollection siteIds = doc.GetSiteIds();
foreach (ObjectId siteId in siteIds)
{
Site site = ts.GetObject(siteId, OpenMode.ForRead) as Site;
if (site == null) continue;
ObjectIdCollection catchmentIds = site.GetCatchmentIds();
foreach (ObjectId catchId in catchmentIds)
{
Catchment catchment = ts.GetObject(catchId, OpenMode.ForRead) as Catchment;
if (catchment == null) continue;
ed.WriteMessage($"\nCatchment: {catchment.Name}, Area: {catchment.Area:F2}");
}
}
ts.Commit();
}
Finding a Specific Catchment
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectIdCollection siteIds = doc.GetSiteIds();
foreach (ObjectId siteId in siteIds)
{
Site site = ts.GetObject(siteId, OpenMode.ForRead) as Site;
ObjectIdCollection catchmentIds = site.GetCatchmentIds();
foreach (ObjectId catchId in catchmentIds)
{
Catchment c = ts.GetObject(catchId, OpenMode.ForRead) as Catchment;
if (c != null && c.Name == "Catchment - (1)")
{
ProcessCatchment(c);
break;
}
}
}
ts.Commit();
}
Catchment Properties
Reading Properties
using (Transaction ts = db.TransactionManager.StartTransaction())
{
Catchment catchment = ts.GetObject(catchmentId, OpenMode.ForRead) as Catchment;
string name = catchment.Name;
double area = catchment.Area;
double perimeter = catchment.Perimeter;
ObjectId surfaceId = catchment.SurfaceId;
TinSurface surface = ts.GetObject(surfaceId, OpenMode.ForRead) as TinSurface;
double runoffCoeff = catchment.RunoffCoefficient;
double rainfall = catchment.RainfallIntensity;
double toc = catchment.TimeOfConcentration;
Point2d discharge = catchment.DischargePoint;
ed.WriteMessage($"\nCatchment: {name}");
ed.WriteMessage($"\n Area: {area:F2} sq ft");
ed.WriteMessage($"\n Perimeter: {perimeter:F2} ft");
ed.WriteMessage($"\n Runoff Coeff: {runoffCoeff:F3}");
ed.WriteMessage($"\n Rainfall: {rainfall:F2} in/hr");
ed.WriteMessage($"\n Tc: {toc:F2} min");
ed.WriteMessage($"\n Discharge: ({discharge.X:F2}, {discharge.Y:F2})");
ts.Commit();
}
Modifying Hydrology Properties
using (Transaction ts = db.TransactionManager.StartTransaction())
{
Catchment catchment = ts.GetObject(catchmentId, OpenMode.ForWrite) as Catchment;
catchment.RunoffCoefficient = 0.65;
catchment.RainfallIntensity = 4.5;
catchment.TimeOfConcentration = 15.0;
ts.Commit();
}
Computing Peak Runoff (Rational Method)
The rational method formula is Q = C x I x A, where Q is peak runoff (cfs), C is the runoff coefficient, I is rainfall intensity (in/hr), and A is area (acres). Convert Catchment.Area from sq ft to acres by dividing by 43,560.
Creating Catchments
Limitations of the .NET API
Direct catchment creation through the .NET API is limited. The primary creation workflows (watershed delineation from surface analysis, catchment from object) are driven by interactive commands. For programmatic creation, use SendStringToExecute to invoke the built-in commands.
Using Interactive Commands Programmatically
Document acadDoc = Application.DocumentManager.MdiActiveDocument;
acadDoc.SendStringToExecute("CREATECATCHMENTFROMOBJECT\n", true, false, false);
acadDoc.SendStringToExecute("CREATEWATERDROP\n", true, false, false);
Creating a Catchment from a Closed Polyline Boundary
If the API supports Catchment.Create in your version, it generally requires a site, a surface, and a boundary:
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectId siteId = doc.GetSiteIds()[0];
ObjectId surfaceId = ;
ts.Commit();
}
Interactive-Only Operations
Several catchment-related operations have no .NET API and must use commands:
Document acadDoc = Application.DocumentManager.MdiActiveDocument;
acadDoc.SendStringToExecute("CREATEWATERDROP\n", true, false, false);
acadDoc.SendStringToExecute("ANALYZEWATERSHED\n", true, false, false);
After watershed analysis, catchments created from the results are accessible through the site collection as shown in the Accessing Catchments section.
Catchment Groups
Catchments are organized within Sites. A site acts as a grouping mechanism, and multiple catchments within the same site share topology rules.
Iterating Catchment Groups by Site
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectIdCollection siteIds = doc.GetSiteIds();
foreach (ObjectId siteId in siteIds)
{
Site site = ts.GetObject(siteId, OpenMode.ForRead) as Site;
ObjectIdCollection catchmentIds = site.GetCatchmentIds();
if (catchmentIds.Count == 0) continue;
ed.WriteMessage($"\nSite: {site.Name} ({catchmentIds.Count} catchments)");
double totalArea = 0;
foreach (ObjectId cId in catchmentIds)
{
Catchment c = ts.GetObject(cId, OpenMode.ForRead) as Catchment;
if (c != null)
{
totalArea += c.Area;
ed.WriteMessage($"\n {c.Name}: {c.Area:F2} sq ft");
}
}
ed.WriteMessage($"\n Total area: {totalArea:F2} sq ft ({totalArea / 43560.0:F2} acres)");
}
ts.Commit();
}
Hydrology Calculations
Composite Runoff Coefficient
When a catchment has multiple land use types, compute a weighted runoff coefficient:
double[] areas = { 20000, 15000, 8000 };
double[] coefficients = { 0.35, 0.65, 0.90 };
double weightedC = 0;
double totalArea = 0;
for (int i = 0; i < areas.Length; i++)
{
weightedC += areas[i] * coefficients[i];
totalArea += areas[i];
}
weightedC /= totalArea;
using (Transaction ts = db.TransactionManager.StartTransaction())
{
Catchment catchment = ts.GetObject(catchmentId, OpenMode.ForWrite) as Catchment;
catchment.RunoffCoefficient = weightedC;
ts.Commit();
}
Summarizing Multiple Catchments for a Pipe Network
using (Transaction ts = db.TransactionManager.StartTransaction())
{
Site site = ts.GetObject(targetSiteId, OpenMode.ForRead) as Site;
ObjectIdCollection catchmentIds = site.GetCatchmentIds();
double totalQ = 0;
foreach (ObjectId cId in catchmentIds)
{
Catchment c = ts.GetObject(cId, OpenMode.ForRead) as Catchment;
if (c == null) continue;
double areaAcres = c.Area / 43560.0;
double q = c.RunoffCoefficient * c.RainfallIntensity * areaAcres;
totalQ += q;
ed.WriteMessage($"\n {c.Name}: Q = {q:F2} cfs");
}
ed.WriteMessage($"\nTotal design flow: {totalQ:F2} cfs");
ts.Commit();
}
Catchment Labels
Adding Catchment Labels
Catchment labels display area, runoff, and other properties directly on the drawing. Label placement is typically handled through the Annotate ribbon or via commands:
acadDoc.SendStringToExecute("ADDCATCHMENTLABEL\n", true, false, false);
Enumerating Catchment Label Styles
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectIdCollection styleIds = doc.Styles.LabelStyles
.CatchmentLabelStyles.AreaLabelStyles.GetDescendantIds();
foreach (ObjectId styleId in styleIds)
{
LabelStyle style = ts.GetObject(styleId, OpenMode.ForRead) as LabelStyle;
if (style != null)
{
ed.WriteMessage($"\nCatchment label style: {style.Name}");
}
}
ts.Commit();
}
Catchment Styles
Accessing and Assigning Catchment Styles
using (Transaction ts = db.TransactionManager.StartTransaction())
{
ObjectIdCollection styleIds = doc.Styles.CatchmentStyles;
foreach (ObjectId styleId in styleIds)
{
CatchmentStyle style = ts.GetObject(styleId, OpenMode.ForRead) as CatchmentStyle;
if (style != null)
ed.WriteMessage($"\nCatchment style: {style.Name}");
}
Catchment catchment = ts.GetObject(catchmentId, OpenMode.ForWrite) as Catchment;
ts.Commit();
}
Style Display Properties
Catchment styles control the visual representation of the catchment boundary, fill pattern, and flow path display. Style configuration is generally done through the UI (Edit Catchment Style dialog) since the style component display properties have limited .NET API write access.
Gotchas
- Catchment creation is primarily interactive. The .NET API does not provide robust
Catchment.Create methods in most versions. Use SendStringToExecute to invoke creation commands when automating.
- Catchments require a Site. You cannot create orphan catchments; they must belong to a Site. If no site exists, one must be created first.
- Surface dependency is critical. Catchments reference a surface for elevation and flow analysis. If the surface is deleted or rebuilt, catchment data may become invalid or require regeneration.
- Area units follow drawing settings.
Catchment.Area returns values in the drawing's current square units (sq ft or sq m). Always check DrawingUnits before performing calculations.
- Rational method assumes consistent units. The formula Q = CIA expects C (dimensionless), I (in/hr), and A (acres) to produce Q in cfs. Convert area from sq ft to acres (divide by 43560) before computing.
- Watershed delineation has no direct .NET API. Surface watershed analysis must be run through the UI or via command strings. Results are read-only after generation.
- Flow path geometry may not be directly accessible. The longest flow path used for Tc calculations is sometimes stored internally and not exposed as a standard polyline entity through the API.
GetCatchmentIds() returns only catchments in that specific site. If catchments are spread across multiple sites, you must iterate all sites to find them all.
- Time of Concentration is user-specified, not auto-calculated. Civil 3D stores the Tc value you assign but does not automatically compute it from flow path geometry via the .NET API.
- Label style hierarchy uses
GetDescendantIds(). Like other Civil 3D label styles, catchment label styles can be nested in subcategories. Always use GetDescendantIds() instead of direct iteration to get all styles.
- Catchment property changes require
OpenMode.ForWrite. Reading properties works with ForRead, but modifying RunoffCoefficient, RainfallIntensity, or TimeOfConcentration requires opening the catchment ForWrite within a transaction.
- COM/interop alternatives exist for some operations. If the managed .NET API is insufficient, some catchment operations can be accessed through the Civil 3D COM API, though this is less common in modern development.
Related Skills
c3d-surfaces -- surfaces that drive catchment delineation and flow analysis
c3d-pipe-networks -- storm pipe networks that receive catchment runoff at discharge points
c3d-label-styles -- general label style configuration and management
c3d-root-objects -- CivilDocument access and transaction patterns