A API Deve elop per's Guide - Autodesk

42 downloads 4320 Views 2MB Size Report
NET Project for AutoCAD Civil 3D . ..... Support Files (CodesSpecific.vb, Utilities. vb) . .... In addition, this API can be used in the Visual Basic for Applications.
® AutoCAD A Civil 3D® 2013 2

API A Deveelop per’s Guiide

April 2012

Contents

Chapter 1

API Developer's Guide . . . . . . . . . . . . . . . . . . . . . . . 1 About the Developer's Guide . . . . . . . . . . . . . . . . . . . . . . . . 1 Intended Audience . . . . . . . . . . . . . . . . . . . . . . . . . . 1 AutoCAD Civil 3D APIs . . . . . . . . . . . . . . . . . . . . . . . . 1 Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 New Features in the .NET API . . . . . . . . . . . . . . . . . . . . 4 Legal Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Setting up a .NET Project for AutoCAD Civil 3D . . . . . . . . . . . 9 Running Commands from the Toolbox . . . . . . . . . . . . . . 14 Running Commands from the Toolbox . . . . . . . . . . . 15 Migrating COM code to .NET . . . . . . . . . . . . . . . . . . . . 16 Base Objects . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Transactions and ObjectIds . . . . . . . . . . . . . . . . . . 16 Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Limitations and Using Interop . . . . . . . . . . . . . . . . 19 Root Objects and Common Concepts . . . . . . . . . . . . . . . . . . 21 Root Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Accessing Application and Document Objects . . . . . . . . 22 Using Collections Within the Document Object . . . . . . . 22 Accessing and Using the Database Object . . . . . . . . . . 24 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

i

Accessing Drawing, Feature, and Command Settings . Label Styles . . . . . . . . . . . . . . . . . . . . . . . . . . Creating a Label Style Object . . . . . . . . . . . . . . Defining a Label Style . . . . . . . . . . . . . . . . . . Using Property Fields in Label Style Text . . . . . . . . Sharing Styles Between Drawings . . . . . . . . . . . . Sample Programs . . . . . . . . . . . . . . . . . . . . . . . Surfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Accessing Surfaces . . . . . . . . . . . . . . . . . . . . . . . Surface Properties . . . . . . . . . . . . . . . . . . . . . . . Creating Surfaces . . . . . . . . . . . . . . . . . . . . . . . Creating a TIN Surface from a TIN file . . . . . . . . . Creating a TIN Surface using TinSurface.Create() . . . Creating a Grid Surface from a DEM File . . . . . . . . Creating a GridSurface with GridSurface.Create() . . . Creating a Volume Surface . . . . . . . . . . . . . . . Working with Surfaces . . . . . . . . . . . . . . . . . . . . Adding a Boundary . . . . . . . . . . . . . . . . . . . Adding Data from DEM Files . . . . . . . . . . . . . . Improving Performance by Using Snapshots . . . . . . Working with TIN Surfaces . . . . . . . . . . . . . . . . . . Adding Point Data to a TIN Surface . . . . . . . . . . . Adding Points Using Point Groups . . . . . . . . . . . Smoothing a TIN Surface . . . . . . . . . . . . . . . . Adding A Breakline to a TIN Surface . . . . . . . . . . Adding a Wall Breakline . . . . . . . . . . . . . . . . Importing Breaklines from a File . . . . . . . . . . . . Adding Contours to a TIN Surface . . . . . . . . . . . Extracting Contours . . . . . . . . . . . . . . . . . . . Surface Styles . . . . . . . . . . . . . . . . . . . . . . . . . Creating and Changing a Style . . . . . . . . . . . . . Assigning a Style to a Surface . . . . . . . . . . . . . . Surface Analysis . . . . . . . . . . . . . . . . . . . . . . . . Creating an Elevation Analysis . . . . . . . . . . . . . Accessing a Watershed Analysis . . . . . . . . . . . . . Calculating Bounded Volumes . . . . . . . . . . . . . Alignments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basic Alignment Operations . . . . . . . . . . . . . . . . . Creating an Alignment . . . . . . . . . . . . . . . . . Defining an Alignment Path Using Entities . . . . . . Determining Entities Within an Alignment . . . . . . Stations . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modifying Stations with Station Equations . . . . . . Creating Station Sets . . . . . . . . . . . . . . . . . . Specifying Design Speeds . . . . . . . . . . . . . . . . Finding the Location of a Station . . . . . . . . . . . .

ii | Contents

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. 24 . 27 . 27 . 27 . 29 . 30 . 31 . 32 . 32 . 34 . 36 . 36 . 37 . 39 . 40 . 41 . 42 . 42 . 44 . 44 . 45 . 45 . 48 . 48 . 50 . 53 . 53 . 54 . 55 . 57 . 57 . 58 . 60 . 60 . 62 . 63 . 65 . 65 . 65 . 67 . 68 . 70 . 70 . 70 . 71 . 72

Superelevation . . . . . . . . . . . . . . . . . . . . . . . . . 72 Alignment Style . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Creating an Alignment Style . . . . . . . . . . . . . . . . . 74 Alignment Label Styles . . . . . . . . . . . . . . . . . . . . 76 Sample Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Creating a Profile From a Surface . . . . . . . . . . . . . . . 79 Creating a Profile Using Entities . . . . . . . . . . . . . . . 80 Editing Points of Vertical Intersection . . . . . . . . . . . . 82 Creating a Profile Style . . . . . . . . . . . . . . . . . . . . 83 Profile Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Creating a Profile View . . . . . . . . . . . . . . . . . . . . 85 Creating Profile View Styles . . . . . . . . . . . . . . . . . . 86 Setting Profile View Styles . . . . . . . . . . . . . . . . . . . 86 Sample Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Pipe Networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Base Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Accessing Pipe Network-Specific Base Objects . . . . . . . . 92 Pipe-Specific Ambient Settings . . . . . . . . . . . . . . . . 92 Listing and Adding Dynamic Part Properties . . . . . . . . . 93 Retrieving the Parts List . . . . . . . . . . . . . . . . . . . . 94 Creating a Pipe Network . . . . . . . . . . . . . . . . . . . 96 Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Creating Pipes . . . . . . . . . . . . . . . . . . . . . . . . . 98 Using Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Creating Pipe Styles . . . . . . . . . . . . . . . . . . . . . 100 Creating Pipe Label Styles . . . . . . . . . . . . . . . . . . 103 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Creating Structures . . . . . . . . . . . . . . . . . . . . . 103 Using Structures . . . . . . . . . . . . . . . . . . . . . . . 103 Creating Structure Styles . . . . . . . . . . . . . . . . . . . 104 Creating Structure Label Styles . . . . . . . . . . . . . . . 105 Interference Checks . . . . . . . . . . . . . . . . . . . . . . . . 106 Performing an Interference Check . . . . . . . . . . . . . 106 Listing the Interferences . . . . . . . . . . . . . . . . . . . 106 Interference Check Styles . . . . . . . . . . . . . . . . . . 106 Sample Program . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Corridors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Root Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Accessing Corridor-Specific Base Objects . . . . . . . . . . 108 Ambient Settings . . . . . . . . . . . . . . . . . . . . . . . 109 Corridors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Corridor Concepts . . . . . . . . . . . . . . . . . . . . . . 112 Listing Corridors . . . . . . . . . . . . . . . . . . . . . . . 112 Creating Corridors . . . . . . . . . . . . . . . . . . . . . . 113

Contents | iii

Baselines . . . . . . . . . . . . . . . . . . . . . . . . . Listing Baselines in a Corridor . . . . . . . . . . . Adding a Baseline to a Corridor . . . . . . . . . . Listing Baseline Regions . . . . . . . . . . . . . . Accessing and Modifying Baseline Stations . . . . Listing Offset Baselines . . . . . . . . . . . . . . Assemblies and Subassemblies . . . . . . . . . . . . . . Listing Applied Assemblies in a Baseline Region . Getting Applied Subassembly Information . . . . Feature Lines . . . . . . . . . . . . . . . . . . . . . . . Listing Feature Lines Along a Baseline . . . . . . Listing Feature Lines Along Offset Baselines . . . Corridor Surfaces . . . . . . . . . . . . . . . . . . . . . Listing Corridor Surfaces . . . . . . . . . . . . . . Listing Surface Boundaries . . . . . . . . . . . . . Computing Cut and Fill . . . . . . . . . . . . . . Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . Assembly Style . . . . . . . . . . . . . . . . . . . Link Style . . . . . . . . . . . . . . . . . . . . . Shape Style . . . . . . . . . . . . . . . . . . . . . Roadway Style Sets . . . . . . . . . . . . . . . . . Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Using the Points Collection . . . . . . . . . . . . . . . Using Points . . . . . . . . . . . . . . . . . . . . . . . Bulk Editing Points . . . . . . . . . . . . . . . . Point User-Defined Properties . . . . . . . . . . . Point Groups . . . . . . . . . . . . . . . . . . . . . . . Using Point Groups . . . . . . . . . . . . . . . . Adding Points to Point Groups with Queries . . . Point Style . . . . . . . . . . . . . . . . . . . . . . . . Creating Point Styles . . . . . . . . . . . . . . . . Creating Point Label Styles . . . . . . . . . . . . Using Point Description Keys . . . . . . . . . . . Creating Custom Subassemblies Using .NET . . . . . . . . . Overview . . . . . . . . . . . . . . . . . . . . . . . . . Subassembly Changes . . . . . . . . . . . . . . . . . . Designing Custom Subassemblies . . . . . . . . . . . . Naming Custom Subassemblies . . . . . . . . . . Attachment and Insertion Methodology . . . . . User-defined vs. Hard-coded Parameters . . . . . Input Parameter Types . . . . . . . . . . . . . . . Superelevation Behavior and Subassemblies . . . Creating Subassembly Help Files . . . . . . . . . Structure of Subassembly Programs . . . . . . . . . . . The Subassembly Template (SATemplate.vb) . . . The Corridor State Object . . . . . . . . . . . . .

iv | Contents

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. 113 . 114 . 114 . 114 . 115 . 116 . 117 . 118 . 119 . 119 . 120 . 121 . 122 . 122 . 123 . 125 . 125 . 125 . 126 . 126 . 127 . 128 . 128 . 129 . 130 . 131 . 134 . 134 . 135 . 139 . 139 . 140 . 141 . 143 . 143 . 144 . 146 . 146 . 146 . 147 . 148 . 149 . 152 . 156 . 156 . 157

Support Files (CodesSpecific.vb, Utilities.vb) . . . . Sample VB.NET Subassembly . . . . . . . . . . . . . . . The Subassembly Tool Catalog . . . . . . . . . . . . . . Overview . . . . . . . . . . . . . . . . . . . . . . . Creating a Tool Catalog ATC File . . . . . . . . . . Creating a Tool Catalog Cover Page . . . . . . . . . Creating a Tool Catalog Registry File . . . . . . . . Installing Custom Subassemblies . . . . . . . . . . . . . Exporting Custom Subassemblies Using a Package File . . Exporting Custom Subassemblies Using a Package File . . . . . . . . . . . . . . . . . . . . . . . . . Converting VBA Subassemblies to .NET . . . . . . . . . . . . . Procedure . . . . . . . . . . . . . . . . . . . . . . . . . . Create the Visual Basic.NET Subassembly Module . Copy Subassembly Code . . . . . . . . . . . . . . . Port the VBA Code to Visual Basic .NET Code . . . Final Adjustments . . . . . . . . . . . . . . . . . . Installing the New Subassembly . . . . . . . . . . . Replacing the VBA Subassembly . . . . . . . . . . . Legacy COM API . . . . . . . . . . . . . . . . . . . . . . . . . Using VBA in AutoCAD Civil 3D . . . . . . . . . . . . . Root Objects and Common Concepts in COM . . . . . . Root Objects . . . . . . . . . . . . . . . . . . . . . Ambient Settings . . . . . . . . . . . . . . . . . . . Label Styles . . . . . . . . . . . . . . . . . . . . . . Survey in COM . . . . . . . . . . . . . . . . . . . . . . . Object Hierarchy . . . . . . . . . . . . . . . . . . . Root Objects . . . . . . . . . . . . . . . . . . . . . Survey Network . . . . . . . . . . . . . . . . . . . Figures . . . . . . . . . . . . . . . . . . . . . . . . Sample Program . . . . . . . . . . . . . . . . . . . Points in COM . . . . . . . . . . . . . . . . . . . . . . . Object Hierarchy . . . . . . . . . . . . . . . . . . . Points . . . . . . . . . . . . . . . . . . . . . . . . Style . . . . . . . . . . . . . . . . . . . . . . . . . Point Groups . . . . . . . . . . . . . . . . . . . . . Sample Program . . . . . . . . . . . . . . . . . . . Surfaces in COM . . . . . . . . . . . . . . . . . . . . . . Object Hierarchy . . . . . . . . . . . . . . . . . . . Using the Surfaces Collection . . . . . . . . . . . . Creating a Surface . . . . . . . . . . . . . . . . . . Working with Surfaces . . . . . . . . . . . . . . . . Working with TIN Surfaces . . . . . . . . . . . . . Surface Style . . . . . . . . . . . . . . . . . . . . . Performing Surface Analysis . . . . . . . . . . . . . Sample Programs . . . . . . . . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. 157 . 160 . 174 . 174 . 175 . 185 . 186 . 188 . 189

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. 190 . 191 . 191 . 191 . 193 . 193 . 202 . 202 . 202 . 203 . 203 . 204 . 204 . 208 . 211 . 216 . 216 . 217 . 222 . 228 . 233 . 234 . 234 . 234 . 239 . 243 . 246 . 247 . 247 . 248 . 249 . 252 . 258 . 264 . 266 . 273

Contents | v

Sites and Parcels in COM . . . . . . . . . . . . . . . . . . . . . 275 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 275 Sites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Parcels . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 Sample Program . . . . . . . . . . . . . . . . . . . . . . . 284 Alignments in COM . . . . . . . . . . . . . . . . . . . . . . . . 284 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 285 Basic Alignment Operations . . . . . . . . . . . . . . . . . 286 Stations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 Alignment Style . . . . . . . . . . . . . . . . . . . . . . . 293 Sample Program . . . . . . . . . . . . . . . . . . . . . . . 296 Profiles in COM . . . . . . . . . . . . . . . . . . . . . . . . . . 297 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 298 Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Profile Views . . . . . . . . . . . . . . . . . . . . . . . . . 303 Sample Programs . . . . . . . . . . . . . . . . . . . . . . . 307 Sections in COM . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 308 Sample Lines . . . . . . . . . . . . . . . . . . . . . . . . . 309 Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Section Views . . . . . . . . . . . . . . . . . . . . . . . . 318 Sample Program . . . . . . . . . . . . . . . . . . . . . . . 324 Data Bands in COM . . . . . . . . . . . . . . . . . . . . . . . . 326 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 326 Defining a Data Band Style . . . . . . . . . . . . . . . . . 327 Creating a Data Band Set . . . . . . . . . . . . . . . . . . 345 Using Data Bands . . . . . . . . . . . . . . . . . . . . . . 347 Sample Programs . . . . . . . . . . . . . . . . . . . . . . . 349 Pipe Networks in COM . . . . . . . . . . . . . . . . . . . . . . 349 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . 350 Base Objects . . . . . . . . . . . . . . . . . . . . . . . . . 351 Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Interference Checks . . . . . . . . . . . . . . . . . . . . . 369 Sample Program . . . . . . . . . . . . . . . . . . . . . . . 374 Corridors in COM . . . . . . . . . . . . . . . . . . . . . . . . . 375 Root Objects . . . . . . . . . . . . . . . . . . . . . . . . . 375 Corridors . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 Baselines . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 Assemblies and Subassemblies . . . . . . . . . . . . . . . . 387 Feature Lines . . . . . . . . . . . . . . . . . . . . . . . . . 391 Corridor Surfaces . . . . . . . . . . . . . . . . . . . . . . . 395 Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Sample Program . . . . . . . . . . . . . . . . . . . . . . . 402 Object Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . 402 AutoCAD Civil 3D . . . . . . . . . . . . . . . . . . . . . 402

vi | Contents

Creating Client Applications . . . . . . . . . . . . . . . . . . . 404 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 404 Samples . . . . . . . . . . . . . . . . . . . . . . . . . . . 404

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407

Contents | vii

viii

API Developer's Guide

1

About the Developer's Guide Intended Audience The is designed for developers who want to customize AutoCAD® Civil 3D® or create applications using the underlying APIs. It can also be used for creating macros to automate repetitive tasks for AutoCAD Civil 3D users and for developers of custom subassemblies.

AutoCAD Civil 3D APIs There are three APIs available for customizing AutoCAD Civil 3D: ■ .NET API — allows you to write extensions to AutoCAD Civil 3D in any .NET language. In general, the AutoCAD Civil 3D.NET API performs significantly faster than the COM API. Development requires Microsoft Visual Studio 2008 SP1 or better. ■

COM API — you can create clients that access the COM API from managed (.NET) or unmanaged (C++) code. See Creating Client Applications (page 404). In addition, this API can be used in the Visual Basic for Applications (VBA) IDE, which is available as a separate download. VBA support is deprecated.



Custom Draw API (in C++) — an extension of the AutoCAD ObjectARX API that allows you to customize the way AutoCAD Civil 3D renders objects. Development requires Microsoft Visual Studio.

1

The COM and .NET APIs are described in this guide. For more information about the Custom Draw API, see the Custom Draw API Reference (civildraw-reference.chm). In addition, an API is provided for creating custom subassemblies in .NET. See Creating Custom Subassemblies Using .NET (page 143). Which API you choose to use depends on what you want to do: If you want to:

Use:

Customize the way objects are rendered in AutoCAD Civil 3D

the Custom Draw API. The Custom Draw API is an extension of the AutoCAD ObjectARX API. For example, if you wanted to number the triangles on a TIN surface, you could create a DLL using the Custom Draw API. See the sample applications shipped with AutoCAD Civil 3D for an example.

Create macros to automate repetitive actions

.NET or COM API.

Create applications to manipulate AutoCAD Civil 3D objects

.NET or COM API.

NOTE Where possible, you should use the Civil .NET API instead of the COM API, especially for longer operations, as the .NET API is a thin layer to Civil objects and has better performance. However, you may find you need to use the COM object to access some functionality or object members that are not yet exposed by the .NET API. In this case it's possible to use both. See Limitations and Using Interop (page 19) .

Organization This guide is organized by the major features of AutoCAD Civil 3D. It consists of the following chapters, each of which includes samples from applicable APIs taken from one or more demonstration programs:

2 | Chapter 1 API Developer's Guide

Chapter 1: Getting Started (page 9) Explains how to set up a COM and .NET project. Also discusses porting COM to .NET code, limitations of the .NET API, and how to use interop to access COM objects.

Chapter 2: Root Objects and Common Concepts (page 21) Explains how to obtain the base .NET objects representing the documents and databases, which are required for all subsequent chapters. Also discussed are objects common to many features, such as ambient settings and label styles.

Chapter 3: Surfaces (page 32) Explains how to import surfaces from files, manipulate point data directly, insert breaklines, manage borders, modify contours, and analyze elevation and watershed information.

Chapter 4: Alignments (page 65) Explains how to create alignments through layout functions, from polyline entities, or based on an offset from existing alignments, using the .NET API. Includes discussion of stations, design speeds, and superelevation.

Chapter 5: Profiles (page 78) Explains the creation of profiles and profile styles, using the .NET API.

Chapter 6: Pipe Networks (page 92) Explains the creation of pipes, structures, and pipe networks, including interference detection, using the .NET API.

Chapter 7: Corridors (page 108) Explains how to gather and modify information about existing corridors, baselines, feature lines, assemblies and subassemblies in a document, using the .NET API.

Chapter 8: Points (page 128) Explains how to access and create points in a document's point collection, how to create point groups and populate them using queries, and how to style points using point description keys.

About the Developer's Guide | 3

Chapter 9: Creating Custom Subassemblies (page 143) Explains how to create and install custom subassemblies using Visual Basic .NET and the creation of catalog files which enables users to access custom subassemblies. You can also convert subassemblies written in VBA to .NET (see the Appendix Converting VBA Subassemblies to .NET (page 191) for more information).

Appendix A: Converting VBA Subassemblies to .NET (page 191) Explains how to convert legacy custom subassemblies written in Visual Basic for Applications (VBA) to .NET.

Appendix B: COM API (page 203) Covers the Legacy COM API.

New Features in the .NET API This section covers changes to the .NET API for AutoCAD Civil 3D for the 2013 release.

Points COGO points and related features are now fully supported in the .NET API with these new classes: ■ CogoPoint class: exposes Coordinate Geometry (COGO) points , including styles and labels. ■

PointGroup class: exposes point groups. ■

CivilDocument.PointGroups is the collection of all point groups in the

drawing. ■



CogoPoint.PrimaryPointGroupId indicates the primary point group a CogoPoint belongs to.

Point Group Queries: Standard and custom point group queries are exposed: StandardPointGroupQuery and CustomPointGroupQuery classes.



User-defined properties are exposed. You can create/modify UDPs, and they can be set on CogoPoint objects. The collection of all point user-defined properties defined in a drawing is accessed via the CivilDocument.PointUDPs property.

4 | Chapter 1 API Developer's Guide



Description keys are exposed with the PointDescriptionKey class. The collection of all point description key sets in a drawing is accessed with the static PointDescriptionKeySetCollection.GetPointDescriptionKeySets()

method.

Surfaces Additional changes have been made to the Surface API: ■ You can now calculate the bounded volume of a surface with Surface.GetBoundedVolumes(). ■ ■

The SurfaceOperationAdd3DFaces Surface operation is now exposed. You can now sample Surface Points along linear entities with the SampleElevation() method.



You can now extract contour information from a surface with several new ExtractContours*() and related methods.



You can now extract elements from surface objects (ExtractBorder(), ExtractWatershed(), and ExtractGridded()).

Profile Views Stacked and multiple profile views can now be created using ProfileView.Create().

Labels The following changes have been made to the Labels API in this release: ■ Parcel Area labels are exposed (ParcelAreaLabel class). These labels are obtained from the Parcel.GetAvailableParcelAreaLabelIds() method. ■

Pipe labels are exposed: PipeLabel, SpanningPipeLabel, PipeProfileLabel, PipeSectionLabel, SpanningPipeProfileLabel, StructureLabel, StructureProfileLabel and StructureSectionLabel classes.



Catchment labels are exposed: CatchmentAreaLabel and FlowSegmentLabel.



Sampleline, Section, and SectionView labels: SectionViewDepthLabel, SectionViewOffsetElevationLabel, SectionProjectionLabel, SectionLabelSetItem, SectionSegmentLabelGroup, SectionGradeBreakLabelGroup, SectionDataBandLabelGroup, SectionSegmentBandLabelGroup, SampleLineLabelGroup.



Plan production labels: ViewFrameLabelGroup and MatchLineLabelGroup

About the Developer's Guide | 5



General labels: NoteLabel, GeneralSegmentLabel



Intersection Label: IntersectionLocationLabel



The GetAvailableLabelGroups() method for existing *LabelGroup classes is deprecated in favor of the more accurately named GetAvailableLabelGroupIds().

Transportation The following changes have been made to the Transportation-related API in this release: ■ You can access FeatureLines by code and modify their properties. You can also export Corridor FeatureLines as Polylines, Grading FeatureLines, Alignments and Profiles. ■

You can export a Corridor as COGO points.



You can get the offset for a FeatureLinePoint.



You can get/set corridor region lock options while creating a Corridor though the API.



Cant View is exposed, including command settings and style.



Cant Label is exposed. You can now get/create the cant label object and access cant label's properties.



You can get/set Cant Option and Rail Alignment Options for an Alignment.

Survey The following changes have been made to the Survey API: ■ The Survey project collection is now exposed (SurveyProjectCollection class) . It can be obtained from the CivilApplication::SurveyProjects() method. Users can now set/get the current working folder for survey projects, get the currently opened survey project object, get a survey project object from the project guid or path. ■

The Survey project object is exposed (SurveyProject class). Users can now open and close the project, get the project's guid and name, and get the survey project's queries.



SurveyQueries are exposed (SurveyQueryCollection class and SurveyQuery class). Users can now get the survey query object with the query guid and access the survey query's properties.



Survey query definitions for TIN Surfaces are exposed, including AddPoint and AddFigure survey query definitions

6 | Chapter 1 API Developer's Guide

(SurveyQueriesAddPointDefinition, SurveyQueriesAddFigureDefinition, SurfaceOperationAddPointSurveyQuery, and SurfaceOperationAddFigureSurveyQuery class). Users can now add data for a TinSurface with a survey query, and access the properties of a surface operation with a survey query.

.NET Namespace Changes This release introduces a namespace restructuring that simplifies referencing objects. The "domain" part of of the namespace has been removed, so for existing projects, you will have to update your "using" statements to include the new namespace. As an example, previous releases exposes "Land" related classes and types in the Autodesk.Civil.Land.DatabaseServices namespace. In AutoCAD Civil 3D 2013, these classes and types are in the Autodesk.Civil.DatabaseServices namespace.

COM Changes If you are using the COM API, you need to update the object version to 10.0 (from 9.0 used in AutoCAD Civil 3D 2012). The objects and interfaces exposed have remained the same, but you should reference the new libraries, which are installed by default to: "C:\Program Files\Common Files\Autodesk Shared\Civil Engineering 100". In addition, interop DLLs are no longer registered as Primary Interop Assemblies (PIAs), and are deployed in the AutoCAD Civil 3D installation directory rather than the Global Assembly Cache (GAC). This means that these assemblies must now be added to Visual Studio projects using the Browse tab of the Add Reference dialog, rather than from the COM tab as was done previously. To compile previously written projects against AutoCAD Civil 3D 2013, you will need to remove references to all interop assemblies from your project, and then re-add them using the Browse tab. The assemblies required for COM interop are: ■

Autodesk.AEC.Interop.Base



Autodesk.AEC.Interop.UiBase



Autodesk.AutoCAD.Interop



Autodesk.AutoCAD.Interop.Common



Autodesk.AECC.Interop.



Autodesk.AEC.Interop.Ui

About the Developer's Guide | 7

Where is one of the four Civil 3D COM domains: Land, Roadway, Pipe, or Survey. We recommend that you set the "Embed Interop Types" property for each interop assembly to True, as this will embed all referenced types into your target assembly, and the referenced interop DLLs are therefore not required at runtime. Also see the for detailed information about these new APIs, and items that have been deprecated in this release.

Legal Notices AutoCAD Civil 3D 2013 © 2012 Autodesk, Inc. All Rights Reserved. Except as otherwise permitted

by Autodesk, Inc., this publication, or parts thereof, may not be reproduced in any form, by any method, for any purpose. Certain materials included in this publication are reprinted with the permission of the copyright holder. Trademarks The following are registered trademarks or trademarks of Autodesk, Inc., and/or its subsidiaries and/or affiliates in the USA and other countries: 123D, 3ds Max, Algor, Alias, Alias (swirl design/logo), AliasStudio, ATC, AUGI, AutoCAD, AutoCAD Learning Assistance, AutoCAD LT, AutoCAD Simulator, AutoCAD SQL Extension, AutoCAD SQL Interface, Autodesk, Autodesk Homestyler, Autodesk Intent, Autodesk Inventor, Autodesk MapGuide, Autodesk Streamline, AutoLISP, AutoSketch, AutoSnap, AutoTrack, Backburner, Backdraft, Beast, Beast (design/logo) Built with ObjectARX (design/logo), Burn, Buzzsaw, CAiCE, CFdesign, Civil 3D, Cleaner, Cleaner Central, ClearScale, Colour Warper, Combustion, Communication Specification, Constructware, Content Explorer, Creative Bridge, Dancing Baby (image), DesignCenter, Design Doctor, Designer's Toolkit, DesignKids, DesignProf, DesignServer, DesignStudio, Design Web Format, Discreet, DWF, DWG, DWG (design/logo), DWG Extreme, DWG TrueConvert, DWG TrueView, DWFX, DXF, Ecotect, Evolver, Exposure, Extending the Design Team, Face Robot, FBX, Fempro, Fire, Flame, Flare, Flint, FMDesktop, Freewheel, GDX Driver, Green Building Studio, Heads-up Design, Heidi, Homestyler, HumanIK, IDEA Server, i-drop, Illuminate Labs AB (design/logo), ImageModeler, iMOUT, Incinerator, Inferno, Instructables, Instructables (stylized robot design/logo),Inventor, Inventor LT, Kynapse, Kynogon, LandXplorer, LiquidLight, LiquidLight (design/logo), Lustre,

8 | Chapter 1 API Developer's Guide

MatchMover, Maya, Mechanical Desktop, Moldflow, Moldflow Plastics Advisers, Moldflow Plastics Insight, Moldflow Plastics Xpert, Moondust, MotionBuilder, Movimento, MPA, MPA (design/logo), MPI, MPI (design/logo), MPX, MPX (design/logo), Mudbox, Multi-Master Editing, Navisworks, ObjectARX, ObjectDBX, Opticore, Pipeplus, Pixlr, Pixlr-o-matic, PolarSnap, PortfolioWall, Powered with Autodesk Technology, Productstream, ProMaterials, RasterDWG, RealDWG, Real-time Roto, Recognize, Render Queue, Retimer, Reveal, Revit, RiverCAD, Robot, Scaleform, Scaleform GFx, Showcase, Show Me, ShowMotion, SketchBook, Smoke, Softimage, Softimage|XSI (design/logo), Sparks, SteeringWheels, Stitcher, Stone, StormNET, Tinkerbox, ToolClip, Topobase, Toxik, TrustedDWG, U-Vis, ViewCube, Visual, Visual LISP, Voice Reality, Volo, Vtour, WaterNetworks, Wire, Wiretap, WiretapCentral, XSI. All other brand names, product names or trademarks belong to their respective holders. Disclaimer THIS PUBLICATION AND THE INFORMATION CONTAINED HEREIN IS MADE AVAILABLE BY AUTODESK, INC. "AS IS." AUTODESK, INC. DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE REGARDING THESE MATERIALS.

Getting Started This chapter describes how to use VBA, and how to set up a new project for using the COM or .NET APIs for AutoCAD Civil 3D.

Setting up a .NET Project for AutoCAD Civil 3D This section describes the basic steps to set up a .NET solution using Visual Studio and the AutoCAD Civil 3D managed classes. The steps are the similar whether you use Microsoft Visual C# .NET or Visual Basic .NET. The example below uses C# in Visual Studio 2010. Express (free) versions of Visual Studio may look slightly different, but can also be used. To create a new project that uses the AutoCAD Civil 3D managed classes in Microsoft Visual Studio 1 In Visual Studio 2010, create a new class library solution and project.

Getting Started | 9

New Project Dialog in Visual Studio

2 Select Project menu ➤ Add References, or right-click References in the Solution Explorer and select Add References.

10 | Chapter 1 API Developer's Guide

3 Browse to the install directory for AutoCAD Civil 3D, and select the base libraries acdbmgd.dll, acmgd.dll, accoremgd.dll, AecBaseMgd.dll, and AeccDbMgd.dll. NOTE These are the base AutoCAD and AutoCAD Civil 3D managed libraries. Your .NET assembly can use classes defined in additional libraries. To allow debugging and reduce the disk space requirements for your projects, select these libraries in the Visual Studio Solution Explorer, and set the Copy Local property to False. 4 Optionally, you can configure your project to start AutoCAD Civil 3D when you run the application from Visual Studio, which is useful for debugging. NOTE This option is not avaiable in Express (free) versions of Visual Studio. 1 On the project Properties page, select the Debug panel. 2 Under Start Action, choose Start external program, and enter the path to the acad.exe executable in the AutoCAD Civil 3D directory. 3 Under Start Options, fill in the Command line arguments: /ld "C:\Program Files\AutoCAD Civil 3D 2013\AecBase.dbx" /p ""

4 Under Start Options, fill in the Working directory, for example: C:\Program Files\AutoCAD Civil 3D 2013\UserDataCache\

5 Implement the IExtensionApplication interface in your main class. Add the Autodesk.AutoCAD.Runtime namespace (which is where this interface is defined), and IExtensionApplication after your class definition: Visual Studio will provide a code complete option to implement stubs for the interface. Your code should now look like this: using System; using Autodesk.AutoCAD.Runtime; namespace GettingStarted { public class Class1 : IExtensionApplication {

Getting Started | 11

#region IExtensionApplication Members public void Initialize() { throw new System.Exception("The method or operation is not implemented."); } public void Terminate() { throw new System.Exception("The method or operation is not implemented."); } #endregion } } You can remove or comment out the default content of these methods. Initialize() is called when your assembly is first loaded by a NETLOAD

command in AutoCAD Civil 3D, and can be used for setting up resources, reading configuration files, and other initialization tasks. Terminate() is called when AutoCAD Civil 3D shuts down (there is no NETUNLOAD command to unload .NET assemblies), and can be used for cleanup to free resources. 6 You are now ready to create a public method that is the target of a CommandMethod attribute. This attribute defines the AutoCAD Civil 3D command that invokes the method. For example: [CommandMethod("HelloWorld")] public void HelloWorld() { } 7 Let’s make the method print out a “Hello World” message on the command line. Add the Autodesk.AutoCAD.ApplicationServices namespace, and add this line to the HelloWorld() method: Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nHello World!\n");

12 | Chapter 1 API Developer's Guide

You can now build the assembly and run it. Start AutoCAD Civil 3D, and type NETLOAD at the command line. In the Choose .NET Assembly dialog, browse to your assembly DLL (if you are using the project settings from step 1, this will be GettingStarted.dll). Type HELLOWORLD at the command line, and you will see the command output:

8 The previous step used functionality from the AutoCAD Application class. Let’s include some functionality specific to the AutoCAD Civil 3D managed classes. First, add two more namespaces: Autodesk.AutoCAD.DatabaseServices and Autodesk.Civil.ApplicationServices. Then add these lines to obtain the current Civil document, get some basic information about it, and print the information out: public void HelloWorld() { CivilDocument doc = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument; ObjectIdCollection alignments = doc.GetAlignmentIds(); ObjectIdCollection sites = doc.GetSiteIds(); String docInfo = String.Format("\nHello World!\nThis document has {0} alignments and {1} sites.\n", alignments.Count, sites.Count); Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(docInfo); } Open or create a document in AutoCAD Civil 3D that contains alignments and sites. When you run the HELLOWORLD command now, you should see output similar to this:

Getting Started | 13

For more samples, look in the AutoCAD Civil 3D\samples\dotNet directory.

Running Commands from the Toolbox The recommended method to expose a AutoCAD Civil 3D extension to users is to add it to the Toolbox tab in Toolspace, by creating a toolbox macro. The Toolbox handles loading the .NET assembly or ARX DLL containing the commands. There are two execution types that a toolbox macro can have: 1 CMD - the command name is sent to the command line to execute. This is the recommended execution type for both .NET and ARX commands. 2 .NET - a method name is located, via Reflection, in the assembly, and is executed directly. No attribute flags are read and the code is always run in application context. (A command executed from the command line runs in the drawing context by default). Therefore, code run as as a .NET execute type must always be a static method, and must handle its own document locking. NOTE It is safe to explicitly lock a document, even if the code might be run in document context. Here is an example of how to handle document locking: static void setPrecision() { using (Autodesk.AutoCAD.ApplicationServices.DocumentLock locker = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument())

14 | Chapter 1 API Developer's Guide

{ // perform any document / database modifications here CivilApplication.ActiveDocument.Settings.DrawingSettings.AmbientSettings.Station.Precision.Value = 2; } }

Running Commands from the Toolbox To create a toolbox macro for a compiled command using the Toolbox Editor 1 Click the Toolbox tab in Toolspace. 2 Click

to open the Toolbox Editor.

3 Right-click Miscellaneous Utilities and click New Category. 4 Right-click the new category, and click New Tool. 5 Select the new tool, and enter its name. 6 For Execute Type, click the drop-down and select CMD or .NET. NOTE CMD is the recommended execution type in most cases, because you do not need to explicitly handle document locking. See the discussion above. 7 For Execute File, browse to the .NET assembly or ARX DLL that contains the command. 8 For Macro Name, enter: ■ Name of the command to run, if the execute type is CMD ■

Name of the method to run, if the execute type is .NET.

9 Optionally, enter a help file and help topic for the command. 10 Click

to apply the changes and close the editor.

After a command has been set up, it can be run by right-clicking it and clicking Execute.

Getting Started | 15

Migrating COM code to .NET In the majority of cases, the .NET API mirrors the structure of the COM API, so porting code to .NET involves setting up a .NET project, copying the code lines, and renaming classes and methods to match the .NET names. If you are using C# instead of VB.NET, some additional changes to code structure are required. The following sections describe some of the differences in the two APIs.

Base Objects The COM API requires that you access the AcadApplication object (via the ThisDrawing.Application object), get the interface object for the AeccApplication object, and from that get the active document. In the .NET API you import Autodesk.Civil.ApplicationServices namespace, and get the active document from the CivilApplication class: g_oDocument = CivilApplication.ActiveDocument() There is a single root document object, CivilDocument, instead of four domain-specific root objects for Land, Pipe, Roadway and Survey.

Transactions and ObjectIds In the .NET API, code that reads and writes to root Civil documents need to use an Autodesk.AutoCAD.DatabaseServices.TransactionManager object to start and commit transactions. It’s a best practice to manage a Transaction with a Using statement, which automatically disposes the Transaction at the end of the block; otherwise, the Transaction should be explicitly disposed of in the Finally section of a Try-Finally block. Here’s an example of a Transaction in a Using block: using (Transaction trans=TransactionManager.StartTransaction()) { //operation here trans.Commit(); }

16 | Chapter 1 API Developer's Guide

NOTE See the section Use Transactions with the Transaction Manager in the AutoCAD .NET Developer’s Guide for more information about using the Transaction object to open and modify drawing database objects. In the .NET API, objects that you get from collections are, in most cases, type ObjectId, which have to be cast to their intended type using a Transaction object (returned by TransactionManager.StartTransaction()). Here’s an example: m_AligmentStyleId = m_doc.Styles.AlignmentStyles.Item(sStyleName) oAlignmentStyle = m_trans.GetObject(m_AligmentStyleId, OpenMode.ForWrite) As AlignmentStyle

Styles In the COM API, styles are held by the root document object. In the .NET API, they are located under CivilDocument.Styles, which is an object of type StylesRoot and contains style objects inherited from StyleBase. Getting and setting style attributes for StyleBase objects requires using a GetDisplayStyle*() method rather than a property. Here’s an example from COM VBA: oAlignmentStyle.ArrowDisplayStyle2d.Visible oAlignmentStyle.ArrowDisplayStyle3d.Visible ' Display curves using violet. oAlignmentStyle.CurveDisplayStyle2d.color = oAlignmentStyle.CurveDisplayStyle3d.color = oAlignmentStyle.CurveDisplayStyle2d.Visible oAlignmentStyle.CurveDisplayStyle3d.Visible

= False = False 200 ' violet 200 ' violet = True = True

This is the equivalent code in VB.NET: oAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Arrow).Visible = False oAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Arrow).Visible = False ' Display curves using violet. oAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Curve).Color

Getting Started | 17

= Autodesk.AutoCAD.Colors.Color.FromRgb(191, 0, 255) ' violet oAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Curve).Color = Autodesk.AutoCAD.Colors.Color.FromRgb(191, 0, 255) ' violet oAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Curve).Visible = True oAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Curve).Visible = True

Settings In the COM API, settings are accessed through the AeccDatabase::Settings object, which contains properties representing the settings object hierarchy. In the .NET API, you use a method to retrieve specific settings objects, for example: SettingsPipeNetwork oSettingsPipeNetwork = doc.Settings.GetFeatureSettings() as SettingsPipeNetwork;

Properties In the COM API, properties are usually simple built-in types, such as double, or types such as BSTR that map to built-in VBA types such as String. In the .NET API, most properties are one of the Property* classes that implement the IProperty interface. For these properties, you get or set the Value of the property. For example, this code in COM: oLabelStyleLineComponent.Visibility = True Becomes this in .NET: oLabelStyleLineComponent.General.Visible.Value = true;

18 | Chapter 1 API Developer's Guide

NOTE There are a few other changes here: the Visibility property is renamed Visible, which has moved to a sub-property of LabelStyleLineComponent called General.

Limitations and Using Interop The .NET API does not expose all the functionality of AutoCAD Civil 3D, and it exposes less than the COM API. The following areas are not yet exposed in .NET: ■ Sites and Parcels ■

Sections



Data Bands



Some labels

In addition, there are some areas in implemented functionality that are not yet complete: ■ Pipes: interference checks (except interference check styles) ■

Corridors: ■ creating new corridors ■

adding baselines to corridors



creating or modifying corridor boundaries or masks



computing cut and fill



setting the CodeSetStyle

If you require this functionality in your .NET project, you can use the corresponding COM objects. To use AutoCAD Civil 3D COM APIs from .NET 1 Create a .NET solution and project. 2 Select Add Reference from the Project menu or Solution Explorer. 3 On the Browse tab, browse to the Civil 3D install directory, and select the following COM interop DLLs, where is the Civil domain you want to use (Land, Roadway, Pipe, or Survey): ■ Autodesk.AEC.Interop.Base

Getting Started | 19



Autodesk.AEC.Interop.UiBase



Autodesk.AutoCAD.Interop



Autodesk.AutoCAD.Interop.Common



Autodesk.AECC.Interop.



Autodesk.AECC.Interop.Ui

4 Select the references above, and set the "Copy Local" property to true, as this will embed all referenced types into your target assembly, and the referenced interop DLLs are therefore not required at runtime. 5 Add the Autodesk.AutoCAD.Interop and Autodesk.AECC.Interop.Ui namespaces to your using or Imports statement. NOTE You may see warnings about types not being found in various Autodesk.AutoCAD.Interop namespaces (warning type 1684). To disable these warnings, enter 1684 under Supress Warnings on the Build tab of the project’s properties. Here is a C# example of getting a count of point groups and surfaces from a document using COM interop: string m_sAcadProdID = "AutoCAD.Application"; string m_sAeccAppProgId = "AeccXUiLand.AeccApplication.10.0"; ... private void useCom() { //Construct AeccApplication object, Document and Database objects m_oAcadApp = (IAcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject(m_sAcadProdID); if (m_oAcadApp != null) { m_oAeccApp = (IAeccApplication)m_oAcadApp.GetInterfaceObject(m_sAeccAppProgId); m_oAeccDoc = (IAeccDocument)m_oAeccApp.ActiveDocument;

20 | Chapter 1 API Developer's Guide

// get the Database object via a late bind m_oAeccDb = (Autodesk.AECC.Interop.Land.IAeccDatabase)m_oAeccDoc.GetType().GetProperty("Database").GetValue(m_oAeccDoc, null); long lCount = m_oAeccDb.PointGroups.Count; m_sMessage += "Number of PointGroups = " + lCount.ToString() + "\n"; lCount = m_oAeccDb.Surfaces.Count; m_sMessage += "Number of Surfaces = " + lCount.ToString() + "\n\n"; MessageBox.Show(m_sMessage); m_sMessage = ""; } } For more interoprability examples, see the CSharpClient and VbDotNetClient sample projects located in \Sample\AutoCAD Civil 3D\COM\.

Root Objects and Common Concepts This chapter explains how to work with the root objects required to access all other objects exposed by the AutoCAD Civil 3D .NET API: CivilApplication and CivilDocument, as well as how to work with collections. It also describes how to work with settings and label styles.

Root Objects This section explains how to acquire references to the base objects, which are required for all applications using the .NET API. It also explains the uses of the application, document, and database objects and how to use collections, which are commonly used throughout the .NET API. To help developers who are already familiar with COM to migrate existing code to .NET, the differences between the two APIs are highlighted with notes.

Root Objects and Common Concepts | 21

Accessing Application and Document Objects The root object in the AutoCAD Civil 3D .NET hierarchy is the CivilApplication object. It contains a reference to the currently active document, and information about the running product. NOTE Unlike the COM API, CivilApplication does not inherit from the AutoCAD object Autodesk.AutoCAD.ApplicationServices.Application. Therefore, if you need access to application-level methods and properties (such as the collection of all open documents, information about the main window, etc.), you need to access through the AutoCAD Application object. See the ObjectARX Managed Class Reference in the ObjectARX SDK for information about this class. The active CivilDocument object is accessed by importing the AutodeskCivil.ApplicationServices namespace, and getting the CivilApplication.ActiveDocument property. This example demonstrates the process of accessing the CivilApplication and CivilDocument objects: using Autodesk.Civil.ApplicationServices; namespace CivilSample { class CivilExample { CivilDocument doc = CivilApplication.ActiveDocument; } }

Using Collections Within the Document Object The document object not only contains collections of AutoCAD Civil 3D drawing elements (such as points and alignments) but also objects that modify those elements (such as styles and label styles). Collections in CivilDocument are ObjectID collections (Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection) for most objects. Objects in these collections must be retrieved with a Transaction.GetObject(), and cast to their type before they can be used.

22 | Chapter 1 API Developer's Guide

NOTE In the COM API, document objects are contained in collections of objects that do not need to be cast. ObjectIdCollection objects implement the IList interface, and can be

enumerated or accessed by index. Here’s an example of iterating through the Corridor collection with foreach, and retrieving and casting the resulting ObjectId to a Corridor to access its methods and properties: public static void iterateCorridors () { CivilDocument doc = CivilApplication.ActiveDocument; using ( Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction() ) { foreach ( ObjectId objId in doc.CorridorCollection ) { Corridor oCorridor = ts.GetObject(objId, OpenMode.ForRead) as Corridor; Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("Corridor: {0}\nLargest possible triangle side: {1}\n", oCorridor.Name, oCorridor.MaximumTriangleSideLength); } } } For more information about ObjectIdCollections, see the . This example creates a new point style: ObjectId pointStyleID = doc.Styles.PointStyles.Add("Name"); // Now a new point style is added to the collection of styles, // and we can modify it by setting the properties // of the oPointStyle object, which we get from the Transaction ts: PointStyle oPointStyle = ts.GetObject(pointStyleID, OpenMode.ForWrite) as PointStyle; oPointStyle.Elevation = 114.6; // You must commit the transaction for the add / modify operation

Root Objects and Common Concepts | 23

// to take effect ts.Commit(); If you attempt to add a new element with properties that match an already existing element, try to access an item that does not exist, or remove an item that does not exist or is in use, an error will result. You should trap the error and respond accordingly. The following sample demonstrates one method of dealing with such errors: // Try to access the style named "Name" try { // This raises an ArgumentException if the item doesn't // exist: ObjectId pointStyleId = doc.Styles.PointStyles["Name"]; // do something with the point style... } catch ( ArgumentException e ) { ed.WriteMessage(e.Message); }

Accessing and Using the Database Object CivilDocument class does not expose the underlying database associated with the document. However, you can access the database from the AutoCAD Application.DocumentManager.MdiActiveDocument.Database object. The Database object contains references to AutoCAD Civil 3D entities, as well as

base AutoCAD entities. See the in the ObjectARX SDK for information.

Settings This section explains the purpose and use of the document settings objects, and covers changing general and specific settings.

Accessing Drawing, Feature, and Command Settings Settings apply at three levels in AutoCAD Civil 3D: 1 Drawing level: there are drawing-wide settings, such as units and zone, abbreviations, etc. There are also ambient settings, which affect a variety

24 | Chapter 1 API Developer's Guide

of AutoCAD Civil 3D behaviors. While these settings also apply drawing-wide, they can be overridden at the feature or command level. 2 Feature (object) level: ambient settings override drawing level ambient settings for that feature only. There are also feature-specific settings, such as default styles. 3 Command level: ambient settings can be set on a command-by-command basis. These settings override both drawing level and feature level settings. For more information on settings in general, see Understanding Settings in the . A document’s settings are accessed through the properties of the SettingsRoot object, which is obtained from the Document.Settings property. This object contains the DrawingSettings property (type SettingsDrawing), which contains all the top-level ambient settings for the document. It also has the GetSettings() method, which gets feature and command settings. Drawing settings and general ambient settings are in the Autodesk.Civil.Settings namespace, while feature and command settings are in the namespace for the related feature. For example, alignment-related ambient and command settings are in the Autodesk.Civil.Land.Settings namespace. The following sample shows how to access the angle settings for alignments: SettingsAlignment alignmentSettings = doc.Settings.GetSettings(); Autodesk.Civil.Settings.SettingsAmbient.SettingsAngle angleSettings = alignmentSettings.Angle; ed.WriteMessage(@"Alignment settings:\n Precision: {0}\n Rounding: {1} Unit: {2}\n Drop Decimal: {3}\n DropZeros: {4}\n ", angleSettings.Precision.Value, angleSettings.Rounding.Value, angleSettings.Unit.Value, angleSettings.DropDecimalForWholeNumbers.Value, angleSettings.DropLeadingZerosForDegrees.Value); The command settings apply to commands, and correspond to the settings in the Commands folder for each item in the AutoCAD Civil 3DToolspace Settings Tab. Each command setting has a corresponding class named SettingsCmdCommandName. For example, the settings class corresponding to the CreateAlignmentLayout command is SettingsCmdCreateAlignmentLayout. As with other types of settings, you use

Root Objects and Common Concepts | 25

the CivilDocument.Settings.GetSettings() method to access command settings objects in the document. The following snippet determines what the “Alignment Type Option” is for the CreateAlignmentLayout command: SettingsCmdCreateAlignmentLayout alignLayoutCmdSettings = doc.Settings.GetSettings(); ed.WriteMessage(@"Alignment Layout Command settings: AlignmentType: {0} ", alignLayoutCmdSettings.AlignmentTypeOption.AlignmentType.Value ); The result of this code returns the current command setting:

26 | Chapter 1 API Developer's Guide

Getting command settings

Label Styles This section explains common features of label styles. It covers creating a new label style object, defining a label style, and using property fields in label style text strings. Details specific to each construct are covered in the appropriate chapters.

Creating a Label Style Object All types of annotation for AutoCAD Civil 3D elements are governed by label styles, which are objects of type LabelStyle. A label style can include any number of text labels, tick marks, lines, markers, and direction arrows. The following example creates a new label style object that can be used with points: CivilDocument doc = CivilApplication.ActiveDocument; ObjectId labelStyleId; labelStyleId = doc.Styles.LabelStyles.PointLabelStyles.LabelStyles.Add ("New Point Label Style");

Defining a Label Style A label style consists of collections of different features of a label, called “components”. The collection of these components is accessed with the LabelStyle::GetComponents() method, which takes the type (LabelStyleComponentType) of component to get. The component types are: ■

Text



Line



Block - symbols



Tick - for both major and minor tick marks



ReferenceText



DirectionArrow

Root Objects and Common Concepts | 27



TextForEach

Not all of these may be valid, depending on the label style type. For example, adding a tick mark component to a label style meant for a point has no visible effect. Label styles also depend on graphical objects that may or may not be part of the current document. For example, if the style references a block that is not part of the current document, then the specified block or tick components are not shown. To add a feature to a label style, add a new component to the corresponding collection using the LabelStyle::AddComponent() method. Then set the properties of that component to the appropriate values. Always make sure to set the Visible property to true. try { // Add a line to the collection of lines in our label style ObjectId lineComponentId = oLabelStyle.AddComponent("New Line Component", LabelStyleComponentType.Line); // Get the new component: ObjectIdCollection lineCompCol = oLabelStyle.GetComponents(LabelStyleComponentType.Line); var newLineComponent = ts.GetObject(lineComponentId, OpenMode.ForWrite) as LabelStyleLineComponent; // Now we can modify the component newLineComponent.General.Visible.Value = true; newLineComponent.Line.Color.Value = Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByAci, 40); // orange-yellow newLineComponent.Line.Angle.Value = 2.094; // radians, = 120 deg // negative lengths are allowed - they mean the line is drawn // in the opposite direction to the angle specified: newLineComponent.Line.Length.Value = -0.015; newLineComponent.Line.StartPointXOffset.Value = 0.005; newLineComponent.Line.StartPointYOffset.Value = -0.005; } // Thrown if component isn't valid, or name is duplicated catch (System.ArgumentException e) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("Error:

28 | Chapter 1 API Developer's Guide

{0}\n", e.Message); } When first created, the label style object is set according to the ambient settings. Because of this, a new label style object may already contain features. If you are creating a new label style object, be sure to check for such existing features or your style might contain unintended elements. // Check to see whether any text components already exist. // If not, add one. if (oLabelStyle.GetComponentsCount(LabelStyleComponentType.Text) == 0) { // Add a text component oLabelStyle.AddComponent("New Text ", LabelStyleComponentType.Text); } // Now modify the first one: ObjectIdCollection textCompCol = oLabelStyle.GetComponents(LabelStyleComponentType.Text); var newTextComponent = ts.GetObject(textCompCol[0], OpenMode.ForWrite) as LabelStyleTextComponent; The ambient settings also define which units are used. If you are creating an application designed to work with different drawings, you should take ambient settings into account or labels may demonstrate unexpected behavior in each document.

Using Property Fields in Label Style Text Text within a label is designated by the LabelStyleTextComponent.Contents property, a PropertyString value. Of course, text labels are most useful if they can provide some sort of information that is unique to each particular item being labeled. This is accomplished by specifying property fields within the string. These property fields are of the form “”. Modifier values are optional and can be in any order. Any number of property fields can be combined with normal text in the Contents property. In this example, a string component of a label is modified to show design speeds and station values for a point along an alignment:

Root Objects and Common Concepts | 29

var newTextComponent = ts.GetObject(textCompCol[0], OpenMode.ForWrite) as LabelStyleTextComponent; newTextComponent.Text.Contents.Value = "SPD="; newTextComponent.Text.Contents.Value += "STA="; Valid property fields for each element are listed in the appropriate chapter.

Sharing Styles Between Drawings Label styles, like all style objects, can be shared between drawings. To do this, call the style’s ExportTo() method, targeting the drawing you want to add the style to. NOTE You can also export collections of styles to another drawing by using the static StyleBase::ExportTo() method. When exporting styles, you specify how conflicts are resolved using the StyleConflictResolverType enum. In this example, the first style in the MajorStationLabelStyles collection is exported from the active drawing to another open drawing named Drawing1.dwg: [CommandMethod("ExportStyle")] public void ExportStyle() { CivilDocument doc = CivilApplication.ActiveDocument; Document AcadDoc = Application.DocumentManager.MdiActiveDocument; Database destDb = null; // Find the database for "Drawing 1" foreach (Document d in Application.DocumentManager) { if (d.Name.Equals("Drawing1.dwg")) destDb = d.Database; } // cancel if no matching drawing: if (destDb == null) return; using (Transaction ts =

30 | Chapter 1 API Developer's Guide

AcadDoc.Database.TransactionManager.StartTransaction()) { // Export style: ObjectId styleId = doc.Styles.LabelStyles.AlignmentLabelStyles.MajorStationLabelStyles[0]; LabelStyle oLabelStyle = ts.GetObject(styleId, OpenMode.ForRead) as LabelStyle; oLabelStyle.ExportTo(destDb, Autodesk.Civil.StyleConflictResolverType.Rename); } } NOTE In certain situations attempts to abort the transaction will fail when calling ExportTo(). This is the case when all the following conditions are all true: multiple styles are being exported, there is a naming conflict between styles, and the StyleConflictResolverType is StyleConflictResolverType.Override.

Sample Programs BatchEditLabelTextSample \Sample\Civil 3D API\DotNet\CSharp\BatchEditLabelTextSample\ This sample prompts the user to select multiple alignment labels, then it prompts for some input text, and replaces the original text for all selected labels.

CommandSettingsSample \Sample\Civil 3D API\DotNet\CSharp\CommandSettingsSample\ This sample demonstrates how to iterate through all the command settings defined in a drawing using Reflection. All commands are exported to an xml file.

CompareStyles \Sample\Civil 3D API\DotNet\CSharp\CompareStyles\

Root Objects and Common Concepts | 31

This sample illustrates how to compare styles defined in two drawings.

DraggedLabelSample \Sample\Civil 3D API\DotNet\CSharp\DraggedLabelSample\ This sample illustrates how to add a new label in a dragged state.

EditLabelStyleSample \Sample\Civil 3D API\DotNet\CSharp\EditLabelStyleSample\ This sample illustrates how to batch copy label styles from one label to multiple selected labels.

Surfaces This chapter covers Civil 3D Surface objects, and how to work with them using the AutoCAD Civil 3D .NET API. There are four classes of surface in Civil 3D: ■

TinSurface



GridSurface



TinVolumeSurface



GridVolumeSurface

The first two represent a single layer of terrain, while the second two represent a volume between two layers. All four derive from a generic Surface object, which exposes the common methods and properties shared by all surfaces.

Accessing Surfaces There are many ways to access the surfaces objects in a drawing. All the surfaces contained by a Document can be obtained using the CivilDocument.GetSurfaceIds() method, which returns an ObjectIdCollection. ObjectIdCollection SurfaceIds = doc.GetSurfaceIds(); foreach (ObjectId surfaceId in SurfaceIds)

32 | Chapter 1 API Developer's Guide

{ CivSurface oSurface = surfaceId.GetObject(OpenMode.ForRead) as CivSurface; editor.WriteMessage("Surface: {0} \n Type: {1}", oSurface.Name, oSurface.GetType().ToString()); } Note that there is also a Surface class in the Autodesk.AutoCAD.DatabaseServices namespace, which will conflict with Autodesk.Civil.DatabaseServices.Surface if you use both namespaces. In this case you can fully qualify the Surface object, or use a "using" alias directive to disambiguate the reference. For example: using CivSuface = Autodesk.Civil.DatabaseServices.Surface; And then use the alias like this: CivSuface oSurface = surfaceId.GetObject(OpenMode.ForRead) as CivSuface; You can also prompt a user to select a specific surface type, such as a TIN Surface, and then get the surface ID from the selection: private ObjectId promptForTinSurface(String prompt) { PromptEntityOptions options = new PromptEntityOptions( String.Format("\n{0}: ", prompt)); options.SetRejectMessage( "\nThe selected object is not a TIN Surface."); options.AddAllowedClass(typeof(TinSurface), true); PromptEntityResult result = editor.GetEntity(options); if (result.Status == PromptStatus.OK) { // We have the correct object type return result.ObjectId; } return ObjectId.Null; // Indicating error. }

Surfaces | 33

Surface Properties The Surface object exposes general surface properties, which you can access using the various GetGeneralProperties() methods. Calculating and returning properties is a resource-intensive process, so you are encouraged to call this method once and re-use the returned object, instead of calling the method for each property. TIN and Grid surfaces have type-specific properties (returned by GetTinProperties() and GetGridProperties() respectively). Both Tin and Grid surfaces also implement a GetTerrainProperties() method. This example gets general properties for the first surface in the database, and then depending on the surface type, it gets the Tin or Grid surface properties: [CommandMethod("SurfaceProperties")] public void SurfaceProperties() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { try { // Get the first surface in a document // "doc" is the CivilApplication.ActiveDocument ObjectId surfaceId = doc.GetSurfaceIds()[0]; CivSurface oSurface = surfaceId.GetObject(OpenMode.ForRead) as CivSurface; // print out general properties: GeneralSurfaceProperties genProps = oSurface.GetGeneralProperties(); String propsMsg = "\nGeneral Properties for " + oSurface.Name; propsMsg += "\n-------------------"; propsMsg += "\nMin X: " + genProps.MinimumCoordinateX; propsMsg += "\nMin Y: " + genProps.MinimumCoordinateY; propsMsg += "\nMin Z: " + genProps.MinimumElevation; propsMsg += "\nMax X: " + genProps.MaximumCoordinateX;

34 | Chapter 1 API Developer's Guide

propsMsg += "\nMax Y: " + genProps.MaximumCoordinateY; propsMsg += "\nMax Z: " + genProps.MaximumElevation; propsMsg += "\nMean Elevation: " + genProps.MeanElevation; propsMsg += "\nNumber of Points: " + genProps.NumberOfPoints; propsMsg += "\n--"; editor.WriteMessage(propsMsg); // Depending on the surface type, let's look at grid or TIN properties: if (oSurface is TinSurface) { TinSurfaceProperties tinProps = ((TinSurface)oSurface).GetTinProperties(); propsMsg = "\nTIN Surface Properties for " + oSurface.Name; propsMsg += "\n-------------------"; propsMsg += "\nMin Triangle Area: " + tinProps.MinimumTriangleArea; propsMsg += "\nMin Triangle Length: " + tinProps.MinimumTriangleLength; propsMsg += "\nMax Triangle Area: " + tinProps.MaximumTriangleArea; propsMsg += "\nMax Triangle Length: " + tinProps.MaximumTriangleLength; propsMsg += "\nNumber of Triangles: " + tinProps.NumberOfTriangles; propsMsg += "\n--"; editor.WriteMessage(propsMsg); } else if (oSurface is GridSurface) { #region GetGridProperties GridSurfaceProperties gridProps = ((GridSurface)oSurface).GetGridProperties(); propsMsg = "\\Grid Surface Properties for " + oSurface.Name;

Surfaces | 35

propsMsg propsMsg gridProps.SpacingX; propsMsg gridProps.SpacingY; propsMsg gridProps.Orientation; propsMsg

+= "\n-------------------"; += "\n X Spacing: " + += "\n Y Spacing: " + += "\n Orientation: " + += "\n--";

editor.WriteMessage(propsMsg); #endregion } } catch (System.Exception e) { editor.WriteMessage(e.Message); } } }

Creating Surfaces GridSurface and TinSurface objects can be created from an imported file, or

created as a new, empty surface to which surface data can be added later. A new TinSurface can also be created by cropping existing TinSurface objects. NOTE Import from LandXML data is not supported in the .NET API at this time. You can use the COM API to import or export surface data to or from LandXML. Most methods for creating empty or importing surfaces are similar in that they all have two overloads: one that specifies the database where the surface will be created (with the default SurfaceStyle applied), the other specifies a SurfaceStyle to apply, and adds the surface to the database that contains the SurfaceStyle. Volume surfaces are created from two existing surfaces, the base (bottom) surface and a comparison surface.

Creating a TIN Surface from a TIN file You can create a new TIN surface from a binary .tin file using the TinSurface.CreateFromTin() method. This method takes two arguments, the

36 | Chapter 1 API Developer's Guide

database for the drawing to which the TIN surface will be added, and the path to a .tin file, as a string. [CommandMethod("CreateFromTIN")] public void CreateFromTin() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Example TIN surface from Civil Tutorials: string tinFile = @"C:\Program Files\Autodesk\AutoCAD Civil 3D 2013\Help\Civil Tutorials\Corridor surface.tin"; try { Database db = Application.DocumentManager.MdiActiveDocument.Database; ObjectId tinSurfaceId = TinSurface.CreateFromTin(db, tinFile); editor.WriteMessage("Import succeeded: {0} \n {1}", tinSurfaceId.ToString(), db.Filename); } catch (System.Exception e) { // handle bad file path editor.WriteMessage("Import failed: {0}", e.Message); } // commit the transaction ts.Commit(); } }

Creating a TIN Surface using TinSurface.Create() You can create an empty TIN surface and add it to the document’s surface collection with the TinSurface.Create() method. This method has two overloads, one that specifies the SurfaceStyle to apply, while the other uses the default style.

Surfaces | 37

In this example, we create a new TIN surface with a specified style, and then add some random point data. [CommandMethod("CreateTINSurface")] public void CreateTINSurface() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { string surfaceName = "ExampleTINSurface"; // Select a style to use ObjectId surfaceStyleId = doc.Styles.SurfaceStyles[3]; // Create the surface ObjectId surfaceId = TinSurface.Create(surfaceName, surfaceStyleId); TinSurface surface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; // Add some random points Point3dCollection points = new Point3dCollection(); Random generator = new Random(); for (int i = 0; i < 10; i++) { double x = generator.NextDouble() * 250; double y = generator.NextDouble() * 250; double z = generator.NextDouble() * 100; points.Add(new Point3d(x, y, z)); } surface.AddVertices(points); // commit the create action ts.Commit(); } }

38 | Chapter 1 API Developer's Guide

Creating a Grid Surface from a DEM File You can create a GridSurface from a Digital Elevation Model (DEM) file using the GridSurface.CreateFromDEM() method. There are two overloads of this method: one that applies the default style, while the other allows you to specify the SurfaceStyle to use. Both take the filename and path of a DEM file, as a string. [CommandMethod("CreateFromDEM")] public void CreateFromDEM() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Prompt user for a DEM file: // string demFile = @"C:\Program Files\Autodesk\AutoCAD Civil 3D 2013\Help\Civil Tutorials\Corridor surface.tin"; PromptFileNameResult demResult = editor.GetFileNameForOpen("Enter the path and name of the DEM file to import:"); editor.WriteMessage("Importing: {0}", demResult.StringResult); try { // surface style #3 is "slope banding" in the default template ObjectId surfaceStyleId = doc.Styles.SurfaceStyles[3]; ObjectId gridSurfaceId = GridSurface.CreateFromDEM(demResult.StringResult, surfaceStyleId); editor.WriteMessage("Import succeeded: {0} \n", gridSurfaceId.ToString()); } catch (System.Exception e) { // handle bad file data or other errors editor.WriteMessage("Import failed: {0}", e.Message); }

Surfaces | 39

// commit the transaction ts.Commit(); } }

Creating a GridSurface with GridSurface.Create() You can create an empty GridSurface using the GridSurface.Create() method. There are two overloads of this method: one applies the default SurfaceStyle, while the other allows you to specify which SurfaceStyle to use. Both take the name of the new GridSurface, x and y spacing, and orientation. The units for x and y spacing and orientation are specified in the surface creation ambient settings (SettingsCmdCreateSurface Distance and Area properties). GridSurface objects are defined on a regularly-spaced grid, and each location

on the grid (represented by the GridLocation structure) has a row index and column index. The grid address (0,0) is at the bottom left corner of the grid. The following example creates a new, empty GridSurface with 25’ x 25’ spacing, and then iterates through a 10 x 10 grid and adds a random elevation at each sample location: [CommandMethod("CreateGridSurface")] public void CreateGridSurface() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { string surfaceName = "ExGridSurface"; // Select a surface style to use ObjectId surfaceStyleId = doc.Styles.SurfaceStyles["Slope Banding (2D)"]; // Create the surface with grid spacing of 25' x 25', orientation 0 degrees: ObjectId surfaceId = GridSurface.Create(surfaceName, 25, 25, 0.0, surfaceStyleId); GridSurface surface = surfaceId.GetObject(OpenMode.ForWrite) as GridSurface;

40 | Chapter 1 API Developer's Guide

// Add some random elevations Random m_Generator = new Random(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { double z = m_Generator.NextDouble() * 10; GridLocation loc = new GridLocation(i, j); surface.AddPoint(loc, z); } } // commit the create action ts.Commit(); } }

Creating a Volume Surface A volume surface represents the difference or composite between two TIN or grid surface areas in a document. You can create a volume surface using the Create() method for either the TinVolumeSuface or GridVolumeSurface class. In this example, the user is prompted to select the base and comparison surfaces, and then a new TinVolumeSurface is created from them. The implementation of promptForTinSurface() is left out for clarity. CommandMethod("CreateTinVolumeSurface")] public void CreateTinVolumeSurface() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { string surfaceName = "ExampleVolumeSurface"; // Prompt user to select surfaces to use // promptForTinSurface uses Editor.GetEntity() to select a TIN surface ObjectId baseId = promptForTinSurface("Select the base surface"); ObjectId comparisonId = promptForTinSurface("Select the comparison surface");

Surfaces | 41

try { // Create the surface ObjectId surfaceId = TinVolumeSurface.Create(surfaceName, baseId, comparisonId); TinVolumeSurface surface = surfaceId.GetObject(OpenMode.ForWrite) as TinVolumeSurface; } catch (System.Exception e) { editor.WriteMessage("Surface create failed: {0}", e.Message); } // commit the create action ts.Commit(); } }

Working with Surfaces This section describes the various methods for adding and editing surface data. This includes adding a boundary, adding information to an existing surface from a DEM file, and using snapshots to improve surface performance.

Adding a Boundary A boundary is a closed polygon that affects the visibility of the triangles inside it. All boundaries applied to a surface are stored in the Surface.BoundariesDefinition collection. The boundary itself is defined by an AutoCAD entity such as a closed polyline or polygon. The height of the entity plays no part in how surface triangles are clipped, so you can use 2D or 3D entities. This entity can also contain curves, but the boundary always consists of lines. How these lines are tessellated is defined by the mid-ordinate

42 | Chapter 1 API Developer's Guide

distance, which is the maximum distance between a curve and the lines that are generated to approximate it. You can add boundaries to a surface with its BoundariesDefinition.AddBoundaries() method. There are three overloads of this method that take one of these to define the new boundaries: 1 an ObjectIdCollection containing an existing polyline, polygon, or parcel 2 a Point2dCollection 3 a Point3dCollection This method also specifies the boundary type (data clip, outer, hide, or show), whether non-destructive breaklines should be used, and the mid-ordinate distance value, which determines how lines are tessellated from curves. In this example, the user is prompted to select a TIN surface and a polyline, and the polyline is added to the surface’s boundaries collection. Note that the surface must be re-built after the boundary is added. The re-build icon in the Civil 3D GUI is not displayed when a surface’s boundaries are modified using the .NET API. [CommandMethod("AddSurfaceBoundary")] public void AddSurfaceBoundary() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Prompt the user to select a surface and a polyline ObjectId surfaceId = promptForEntity("Select the surface to add a boundary to", typeof(TinSurface)); ObjectId polyId = promptForEntity("Select the object to use as a boundary", typeof(Polyline)); // The boundary or boundaries must be added to an ObjectIdCollection for the AddBoundaries method: ObjectId[] boundaries = { polyId }; TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; try {

Surfaces | 43

oSurface.BoundariesDefinition.AddBoundaries(new ObjectIdCollection(boundaries), 100, Autodesk.Civil.Land.SurfaceBoundaryType.Outer, true); oSurface.Rebuild(); } catch (System.Exception e) { editor.WriteMessage("Failed to add the boundary: {0}", e.Message); } // commit the transaction ts.Commit(); } }

Adding Data from DEM Files Any number of DEM files can be added to existing grid and TIN surfaces. When a DEM file is added to the GridSurface.DEMFilesDefinition or TinSurface.DEMFilesDefinition collection, its information is converted to an evenly spaced lattice of triangles that is added to the surface.

Improving Performance by Using Snapshots A surface is made up of all the operations that modifiy the surface’s triangles. If you rebuild the surface, re-performing all these operations can be slow. Snapshots can improve performance by recording the current state of all the triangles in a surface. Subsequent rebuilds start from the data of the snapshot, thus saving time by not performing complicated calculations that have already been done once. Surface objects have CreateSnapshot(), RebuildSnapshot(), and RemoveSnapshot() methods. Both CreateSnapshot() and RebuildSnapshot() will overwrite an existing snapshot.

44 | Chapter 1 API Developer's Guide

NOTE RebuildSnapshot() will cause an error if the snapshot does not exist. CreateSnapshot() and RebuildSnapshot() can also cause errors if the surface

is out-of-date. You can check whether the Surface has a snapshot already with the Surface.HasSnapshot property. For example: if (oSurface.HasSnapshot) { oSurface.RemoveSnapshot(); } oSurface.CreateSnapshot(); oSurface.RebuildSnapshot();

Working with TIN Surfaces This section covers the various methods and properties for examining or modifying existing TIN surfaces, including adding new point data, adding breaklines, and adding contours.

Extracting Contours The ExtractContour() and ExtracBorder() methods exposed in the COM API are not yet available in the .NET API.

Adding Point Data to a TIN Surface There are two techniques for adding points that are unique to TIN surfaces: 1 Using point files 2 Using point groups The TinSurface.PointFilesDefinition property contains the names of text files that contain point information. These text files must consist only of lines containing the point number, easting, northing, and elevation delineated by spaces. Except for comment lines beginning with “#”, any other information will result in an error. Unlike TIN or LandXML files, text files do not contain a list of faces - the points are automatically joined into a series of triangles based on the document settings.

Surfaces | 45

The method PointFilesDefinition.AddPointFile() takes the path to a point file, and the ObjectId of a PointFileFormat object. This object is obtained from the Database’s PointFileFormatCollection using the same string used to describe the format in the Civil 3D GUI:

46 | Chapter 1 API Developer's Guide

This is an example of adding a PENZD format point file to an existing surface. The file in this example is from the Civil 3D tutorials directory. [CommandMethod("SurfacePointFile")] public void SurfacePointFile() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Select the first Surface in the document ObjectId surfaceId = doc.GetSurfaceIds()[0]; TinSurface oSurface = surfaceId.GetObject(OpenMode.ForRead) as TinSurface; try { // add points from a point file to the surface // this is the location of an example PENZD file from the C3D tutorials, the actual path may // differ based on the OS string penzdFile = @"C:\Program Files\Autodesk\AutoCAD Civil 3D 2013\Help\Civil Tutorials\EG-Surface-PENZD (space delimited).txt"; // get the point file format object, required for import: PointFileFormatCollection ptFileFormats = PointFileFormatCollection.GetPointFileFormats(HostApplicationServices.WorkingDatabase); ObjectId ptFormatId = ptFileFormats["PENZD (space delimited)"];

oSurface.PointFilesDefinition.AddPointFile(penzdFile, ptFormatId); } catch (System.Exception e) { editor.WriteMessage("Failed: {0}", e.Message); }

Surfaces | 47

// commit the transaction ts.Commit(); } }

Adding Points Using Point Groups Although the TinSurface class exposes a point group collection (as the SurfaceDefinitionPointGroups property), the API doesn’t support adding point groups to the collection at this time.

Smoothing a TIN Surface Surface smoothing adds points at system-determined elevations using Natural Neighbor Interpolation (NNI) or Kriging methods, which results in smoothed contours with no overlapping. See the for more information about the two supported smoothing methods. TinSurface objects expose these two smoothing operations with the SmoothSurfaceByNNI() and SmoothSurfaceByKriging() methods.

Setting up a smoothing operation takes a couple of steps: 1 Create a SurfacePointOutputOptions object. 2 Set the OutputLocations property (enumerated by SurfacePointOutputLocationsType) to specify the output locations. The other options you need to set on SurfacePointOutputOptions depend on what is specified for this setting: 1 EdgeMidPoints – specifies the Edges property, an array of TinSurfaceEdge objects representing edges on the surface. 2 RandomPoints – specifies the number of points (RandomPointsNumber) and output regions (OutputRegions, a Point3dCollection ) 3 Centroids – specifies the OutputRegions property. 4 GridBased – sets the OutputRegions property, grid spacing (GridSpacingX and GridSpacingY), and grid orientation (GridOrientation).

48 | Chapter 1 API Developer's Guide

3 If you are using the Kriging method, you need to also create a KrigingMethodOptions object to set the options for this method: 1 SemivariogramModel property – set to one of the models enumerated by KrigingSemivariogramType. 2 SampleVertices property – set to the collection of vertices to which to smooth (for example, you can use the TinSurface.GetVerticesInsidePolylines() to get this collection). 3 Optionally set NuggetEffect, VariogramParamA and VariogramParamC depending on the model selected. 4 Pass the options to SmoothSurfaceByNNI() or SmoothSurfaceByKriging(). These methods return a SurfaceOperationSmooth object that includes the number of output points in the operation. This example illustrates setting up and using the SmoothSurfaceByNNI() method, using the Centroids output location: [CommandMethod("SmoothTinSurface")] public void SmoothTinSurface() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { try { // Select a TIN Surface: ObjectId surfaceId = promptForEntity("Select a TIN surface to smooth\n", typeof(TinSurface)); TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; // Select a polyline to define the output region: ObjectId polylineId = promptForEntity("Select a polyline to define the output region\n", typeof(Polyline)); Point3dCollection points = new Point3dCollection(); Polyline polyline = polylineId.GetObject(OpenMode.ForRead) as Polyline; // Convert the polyline into a collection of

Surfaces | 49

points: int count = polyline.NumberOfVertices; for (int i = 0; i < count; i++) { points.Add(polyline.GetPoint3dAt(i)); } // Set the options: SurfacePointOutputOptions output = new SurfacePointOutputOptions(); output.OutputLocations = SurfacePointOutputLocationsType.Centroids; output.OutputRegions = new Point3dCollection[] { points }; SurfaceOperationSmooth op = oSurface.SmoothSurfaceByNNI(output); editor.WriteMessage("Output Points: {0}\n", op.OutPutPoints.Count); // Commit the transaction ts.Commit(); } catch (System.Exception e) { editor.WriteMessage(e.Message); } } }

Adding A Breakline to a TIN Surface Breaklines are used to shape the triangulation of a TIN surface. Each TIN surface has a collection of breaklines contained in the TinSurface.BreaklinesDefinition property, which is a SurfaceDefinitionBreaklines object. There are different kinds of breaklines, and each is created in a slightly different way. NOTE For more information about breakline types, see the The SurfaceDefinitionBreaklines class allows you to add standard, non-destructive, and proximity breaklines in similar ways. Each breakline type has its own Add*() method (for example, AddStandardBreaklines for standard

50 | Chapter 1 API Developer's Guide

breaklines), and each method has three versions, depending on the type of object you are creating a breakline from. You can add breaklines from a Point2dCollection, a Point3dCollection, or an ObjectIdCollection that contains one or more 3D lines, grading feature lines, splines, or 3D polylines. Each type of breakline requires a specified mid-ordinate distance parameter, which determines how curves are tessellated. A standard breakline consists of an array of 3D lines or polylines. Each line endpoint becomes a point in the surface and surface triangles around the breakline are redone. The AddStandardBreaklines() method requires that you specify the maximum distance, weeding distance and weeding angle, in addition to the mid-ordinate distance. The maximumDistance parameter corresponds to the Supplementing Distance in the AutoCAD Civil 3D GUI, while weedingDistance and weedingAngle correspond to the weeding distance and angle, respectively. A proximity breakline does not add new points to a surface. Instead, the nearest surface point to each breakline endpoint is used. The triangles that make up a surface are then recomputed making sure those points are connected. A non-destructive breakline does not remove any triangle edges. It places new points along the breakline at each intersection with a triangle edge and new triangles are computed. This example illustrates how to add standard, non-destructive and proximity breaklines: [CommandMethod("SurfaceBreaklines")] public void SurfaceBreaklines() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Prompt the user to select a TIN surface and a polyline, and create a breakline from the polyline ObjectId surfaceId = promptForEntity("Select a TIN surface to add a breakline to", typeof(TinSurface)); ObjectId lineId = promptForEntity("Select a 3D polyline to use as the breakline", typeof(Polyline3d)); TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; ObjectId[] lines = { lineId };

Surfaces | 51

PromptKeywordOptions pKeyOpts = new PromptKeywordOptions(""); pKeyOpts.Message = "\nEnter the type of breakline to create: "; pKeyOpts.Keywords.Add("Standard"); pKeyOpts.Keywords.Add("Non-Destructive"); pKeyOpts.Keywords.Add("Proximity"); pKeyOpts.Keywords.Default = "Standard"; pKeyOpts.AllowNone = true; PromptResult pKeyRes = editor.GetKeywords(pKeyOpts);

try { switch (pKeyRes.StringResult) { case "Non-Destructive": oSurface.BreaklinesDefinition.AddNonDestructiveBreaklines(new ObjectIdCollection(lines), 1); break; case "Proximity": oSurface.BreaklinesDefinition.AddProximityBreaklines(new ObjectIdCollection(lines), 1); break; case "Standard": default: oSurface.BreaklinesDefinition.AddStandardBreaklines(new ObjectIdCollection(lines), 10, 5, 5, 0); break; } } catch (System.Exception e) { editor.WriteMessage("Operation failed: {0}", e.Message); } // commit the transaction ts.Commit();

52 | Chapter 1 API Developer's Guide

} }

Adding a Wall Breakline A wall breakline is used when the height of the surface on one side of the breakline is different than the other side. The AddWallBreaklines() method creates two breaklines, one for the top of the wall and one for the bottom. However, you cannot have a perfectly vertical wall in a TIN surface. The first breakline is placed along the path specified, and the second breakline is very slightly offset to one side and raised or lowered by a relative elevation. There are two versions of AddWallBreaklines(): one takes a WallBreaklineCreation structure that sets one elevation for all vertices in the breakline, while WallBreaklineCreationEx specifies an elevation for each individual vertex. The IsRightOffset property indicates in which direction the wall at each entity endpoint is offset. If set to true, the offset is to the right as you walk along the breakline from the start point to the end.

Importing Breaklines from a File You can import breaklines from a file in .FLT format, using SurfaceDefinitionBreaklines.ImportBreaklineFromFile(). When you

import the file, all breaklines in the FLT file are copied into the surface as Add Breakline operations, and the link to the file is not maintained. The option available on the GUI to maintain a link to the file is not available via the API. This sample shows how to import breaklines from a file named eg1.flt, and to get the first newly created breakline: [CommandMethod("ImportBreaklines")] public void ImportBreaklines() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Prompt the user to select a TIN surface and a polyline, and create a breakline from the polyline ObjectId surfaceId = promptForEntity("Select a TIN surface to add a breakline to", typeof(TinSurface));

Surfaces | 53

TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; string breaklines = "eg1.flt"; oSurface.BreaklinesDefinition.ImportBreaklinesFromFile(breaklines);

// commit the transaction ts.Commit(); } }

Adding Contours to a TIN Surface A contour is an open or closed entity that describes the altitude of the surface along the entity. Contours must have a constant altitude. The z value of the first point of the entity is used as the altitude of entire entity, no matter what is specified in the following points. Contours also have settings that can adjust the number of points added to the surface - when you create a contour, you specify a weeding distance, a weeding angle, and a distance parameter. Points in the contour are removed if the distance between the points before and after is less than the weeding distance and if the angle between the lines before and after is less than the weeding angle. Each line segment is split into equal sections with a length no greater than the maximumDistance parameter. Any curves in the entity are also tessellated according to the mid-ordinate distance, just as with breaklines. The maximumDistance value has precedence over the weeding values, so it is possible that the final contour will have line segments smaller than the weeding parameters specify. Contours can be added from a Point2dCollection, Point3dCollection, or an ObjectIdCollection containing polylines. You can optionally specify options for minimizing flat areas in a surface by passing a SurfaceMinimizeFlatAreaOptions object as a parameter to SurfaceDefinitionContours.AddContours(). For more information about the ways you can minimize flat areas, see “Minimizing Flat Areas in a Surface” in the Civil 3D user’s Guide. The following sample demonstrates adding a contour to a surface from a polyline: [CommandMethod("CreateContour")] public void CreateContour() {

54 | Chapter 1 API Developer's Guide

using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Prompt the user to select a TIN surface and a polyline, and create a contour from the polyline ObjectId surfaceId = promptForEntity("Select a TIN surface to add a contour to", typeof(TinSurface)); ObjectId polyId = promptForEntity("Select a polyline to create a contour from", typeof(Polyline)); TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; ObjectId[] contours = {polyId}; oSurface.ContoursDefinition.AddContours(new ObjectIdCollection(contours), 1, 85.5, 55.5, 0); // commit the transaction ts.Commit(); } }

Extracting Contours Contours can be extracted as AutoCAD drawing objects from surfaces (both TIN and Grid) using versions of ExtractContours(), ExtractContoursAt(), ExtractMajorContours(), and ExtractMinorContours(). These methods are defined by the ITerrainSurface interface, and implemented by all Surface classes. The four methods are very similar, but accomplish different things: ■ ExtracContours() - this method extracts contours at a specified elevation interval, starting at the surface's lowest elevation. ■

ExtractContoursAt() - this method extracts all contours at a single

specified elevation. ■

ExtractMajorContours() - this method extracts only major contours from

a Surface. ■

ExtractMinorContours() - this method extracts only minor contours from

a Surface.

Surfaces | 55

The ExtractContours() method has four versions, the simplest taking an interval parameter, and another taking an elevation range and interval. There are also versions of these two methods that take additional smoothing parameters to smooth the extracted polylines. The extracted contours are lightweight AutoCAD Polyline objects, and the method returns an ObjectIdCollection containing the IDs of all extracted objects. The objects are independent of the surface and can be manipulated without affecting the underlying surface. This example creates a random surface, and then extracts contours in a couple of ways. // Setup: creates a new, random surface // TinSurface surface = CreateRandomSurface("Example Surface"); // Extract contours and print information about them: ObjectIdCollection contours; double contourInterval = 50.0; contours = surface.ExtractContours(contourInterval); write("# of extracted contours: " + contours.Count + "\n"); int totalVertices = 0; for (int i = 0; i < contours.Count; i++) { ObjectId contourId = contours[i]; // Contours are lightweight Polyline objects: Polyline contour = contourId.GetObject(OpenMode.ForRead) as Polyline; write(String.Format("Contour #{0} length:{1}, # of vertices:{2}\n", i, contour.Length, contour.NumberOfVertices)); totalVertices += contour.NumberOfVertices; } // Extract contours with smoothing: contours = surface.ExtractContours(contourInterval, ContourSmoothingType.AddVertices, 10); int totalVerticesSmoothed = 0; foreach (ObjectId contourId in contours) { Polyline contour = contourId.GetObject(OpenMode.ForRead) as Polyline; totalVerticesSmoothed += contour.NumberOfVertices;

56 | Chapter 1 API Developer's Guide

} // Compare smoothing by adding vertices: write(String.Format("Effects of smoothing:\n total vertices no smoothing: {0}\n total vertices with smoothing: {1}\n", totalVertices, totalVerticesSmoothed)); // Extract contours in a range: double startRange = 130.0; double endRange = 190.0; contours = surface.ExtractContours(contourInterval, startRange, endRange); write("# of extracted contours in range: " + contours.Count + "\n"); // You can also extract contours in a range with smoothing: // contours = surface.ExtractContours(contourInterval, startRange, endRange, // ContourSmoothingType.SplineCurve, 10);

Surface Styles This section describes how to create and apply styles to surface objects.

Creating and Changing a Style Surface styles are stored in the CivilDocument.Styles.SurfaceStyles collection. To create a new style, call the SurfaceStyleCollection.Add() method with the name of your new style. The new style is created according to the document’s ambient settings. In addition to the properties inherited from StyleBase, a surface style consists of different objects governing the appearance of boundaries, contours, direction analysis, elevation analysis, grids, points, slope arrows, triangles, and watershed analysis. Usually a single style only displays some of these objects. When initially created, a style is set according to the document’s ambient settings and may show some unwanted style elements. Always set the visibility properties of all style elements to ensure the style behaves as you expect.

Surfaces | 57

Assigning a Style to a Surface To assign a style to a surface, set the Surface object’s StyleId property to the ObjectId of a valid SurfaceStyle object. This example illustrates creating a new style, changing its settings, and then assigning it to the first surface in the document. Only the plan display settings are changed for brevity, though you would normally also change the model display settings as well. [CommandMethod("SurfaceStyle")] public void SurfaceStyle() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // create a new style called 'example style': ObjectId styleId = doc.Styles.SurfaceStyles.Add("example style"); // modify the style: SurfaceStyle surfaceStyle = styleId.GetObject(OpenMode.ForWrite) as SurfaceStyle; // display surface triangles surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Triangles).Visible = true; surfaceStyle.GetDisplayStyleModel(SurfaceDisplayStyleType.Triangles).Visible = true; // display boundaries, exterior only: surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Boundary).Visible = true; surfaceStyle.BoundaryStyle.DisplayExteriorBoundaries = true; surfaceStyle.BoundaryStyle.DisplayInteriorBoundaries = false; // display major contours:

58 | Chapter 1 API Developer's Guide

surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.MajorContour).Visible = true; // turn off display of other items: surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.MinorContour).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.UserContours).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Directions).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Elevations).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Slopes).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.SlopeArrows).Visible = false; surfaceStyle.GetDisplayStylePlan(SurfaceDisplayStyleType.Watersheds).Visible = false; // do the same for all model display settings as well

// assign the style to the first surface in the document: CivSurface surf = doc.GetSurfaceIds()[0].GetObject(OpenMode.ForWrite) as CivSurface; surf.StyleId = styleId; // commit the transaction ts.Commit(); } }

Surfaces | 59

Surface Analysis This section shows you how to access surface analysis data using the .NET API. All surface analysis data is accessed with the Surface.Analysis property, which exposes data for contour, user-defined contour, direction, elevation, slope arrow, slope, and watershed analyses.

Creating an Elevation Analysis An elevation analysis creates a 2-dimensional projection of a surface and then adds bands of color indicating ranges of altitude. Calling Surface.Analysis.GetElevationData() returns an array of SurfaceAnalysisElevationData objects, one for each elevation region created by the analysis, or an empty array if no analysis exists. Each elevation region represents a portion of the surface’s total elevation. The collection lets you modify the color, minimum elevation, and maximum elevation of each region. Note that each time a surface’s elevation analysis is generated in the GUI, AutoCAD Civil 3D discards all existing elevation regions for the surface and creates a new collection of regions. Changes to the previous collection of SurfaceAnalysisElevationData objects are discarded. The .NET API does not have an equivalent to the COM API SurfaceAnalysisElevation.CalculateElevationRegions() method, but you

can implement one that does the same thing. This example shows one implementation, and the implemented method being used by a command: /// /// Calculates elevation regions for a given surface, and returns an array that can be passed /// to Surface.Analysis.SetElevationData() /// /// A Civil 3D Surface object /// The number of elevation steps to calculate /// The index of the start color. Each subsequent color index is incremeted by 2. /// An array of SurfaceAnalysisElevationData objects. private SurfaceAnalysisElevationData[] CalculateElevationRegions(Autodesk.Civil.Land.DatabaseServices.Surface

60 | Chapter 1 API Developer's Guide

surface, int steps, short startColor) { // calculate increments based on # of steps: double minEle = surface.GetGeneralProperties().MinimumElevation; double maxEle = surface.GetGeneralProperties().MaximumElevation; double incr = (maxEle - minEle) / steps; SurfaceAnalysisElevationData[] newData = new SurfaceAnalysisElevationData[steps]; for (int i = 0; i < steps; i++) { Color newColor = Color.FromColorIndex(ColorMethod.ByLayer, (short)(100 + (i * 2))); newData[i] = new SurfaceAnalysisElevationData(minEle + (incr * i), minEle + (incr * (i + 1)), newColor); } return newData; } /// /// Illustrates performing an elevation analysis /// [CommandMethod("SurfaceAnalysis")] public void SurfaceAnalysis() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Select first TIN Surface ObjectId surfaceId = doc.GetSurfaceIds()[0]; TinSurface oSurface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; // get existing analysis, if any: SurfaceAnalysisElevationData[] analysisData = oSurface.Analysis.GetElevationData(); editor.WriteMessage("Existing Analysis length: {0}\n", analysisData.Length);

Surfaces | 61

SurfaceAnalysisElevationData[] newData = CalculateElevationRegions(oSurface, 10, 100); oSurface.Analysis.SetElevationData(newData); // commit the transaction ts.Commit(); } } Many elevation analysis features can be modified through the surface style. For example, you can use a number of pre-set color schemes (as defined in the ColorSchemeType enumeration).

Accessing a Watershed Analysis A watershed analysis predicts how water will flow over and off a surface. The analysis data is managed by an object of type SurfaceAnalysisWatershedDataCollection returned by the Surface.Analysis.GetWatershedData() method. The .NET API does not implement an equivalent to the COM API AeccSurfaceAnalysisWatershed.CalculateWatersheds() method, but you can use the SurfaceAnalysis.GetWatershedData() method to access watershed data from an existing analysis, and change properties (such as AreaColor) of watershed regions. Each item in the SurfaceAnalysisWatershedDataCollection represents a watershed region. Depending on the nature of the drain target, each watershed region is a different type specified by the WatershedType enumeration. (For more information about watershed region types, see “Types of Watersheds” in the ). Other properties, such as the region color, hatch pattern, description, and visibility, are all accessible. This example illustrates reading the properties of an existing watershed analysis: [CommandMethod("SurfaceWatershedAnalysis")] public void SurfaceWatershedAnalysis() { using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction())

62 | Chapter 1 API Developer's Guide

{ // Select first TIN Surface ObjectId surfaceId = doc.GetSurfaceIds()[0]; CivSurface oSurface = surfaceId.GetObject(OpenMode.ForRead) as CivSurface; SurfaceAnalysisWatershedDataCollection analysisData = oSurface.Analysis.GetWatershedData(); editor.WriteMessage("Number of watershed regions: {0}\n", analysisData.Count); foreach (SurfaceAnalysisWatershedData watershedData in analysisData) { editor.WriteMessage("Data item AreaId: {0} \n" + "Description: {1}\n" + "Type: {2}\n" + "Drains into areas: {3}\n" + "Visible? {4}\n", watershedData.AreaID, watershedData.Description, watershedData.Type, String.Join(", ", watershedData.DrainsInto), watershedData.Visible); } // commit the transaction ts.Commit(); } }

Calculating Bounded Volumes The .NET API exposes the Civil 3D Bounded Volumes Utility as the GetBoundedVolumes() method for the Surface class, which means that bounded volumes can be calculated for both TIN and Grid surfaces. This method takes a Point3dCollection containing points that define the vertices of a polygon area, and an optional elevation datum. If you do not supply an elevation datum, the method uses 0.0. The first and last point in the vertices collection must be the same; that is, the polygon must be closed. The method returns a SurfaceVolumeInfo object that includes values for net volume, cut volume, and fill volume.

Surfaces | 63

In this example, a sample TIN surface is created, a polygon inside the surface is defined, and both versions of the GetBoundedVolumes() method is called on the surface. // Create a sample surface ObjectId surfaceId = TinSurface.Create(_acaddoc.Database, "Example Surface"); TinSurface surface = surfaceId.GetObject(OpenMode.ForWrite) as TinSurface; // Generates 100 random points between 0,100: Point3dGenerator p3dgen = new Point3dGenerator(); Point3dCollection locations = p3dgen.AsPoint3dCollection(); surface.AddVertices(locations); // Create a region that is a polygon inside the surface. // The first and last point must be the same to create a closed polygon. // Point3dCollection polygon = new Point3dCollection(); polygon.Add(new Point3d(20, 20, 20)); polygon.Add(new Point3d(20, 80, 15)); polygon.Add(new Point3d(80, 40, 25)); polygon.Add(new Point3d(20, 20, 20)); double elevation = 30.5; SurfaceVolumeInfo surfaceVolumeInfo = surface.GetBoundedVolumes(polygon, elevation); write(String.Format("Surface volume info:\n Cut volume: {0}\n Fill volume: {1}\n Net volume: {2}\n", surfaceVolumeInfo.Cut, surfaceVolumeInfo.Fill, surfaceVolumeInfo.Net)); // If you do not specify an elevation, 0.0 is used: // surfaceVolumeInfo = surface.GetBoundedVolumes(polygon); write(String.Format("Surface volume info:\n Cut volume: {0}\n Fill volume: {1}\n Net volume: {2}\n", surfaceVolumeInfo.Cut, surfaceVolumeInfo.Fill, surfaceVolumeInfo.Net));

64 | Chapter 1 API Developer's Guide

Alignments This chapter covers creating and using Alignments, Stations, and Alignment styles using the AutoCAD Civil 3D .NET API.

Basic Alignment Operations Creating an Alignment Alignments are usually created without being associated with an existing site. Each CivilDocument object has its own collection of alignments not associated with a site accessed with the GetSitelessAlignmentIds() method. There is also a collection of all alignments (siteless and associated with a site) accessed with GetAlignmentIds() method. Alignments can be moved into a site with the Alignment.CopyToSite() method. A siteless alignment can be copied from a sited alignment using Alignment.CopyToSite(), and passing ObjectId.Null or ““ as the site.

Creating a New Alignment The Alignment class provides versions of the Create() method to create a new Alignment object from a polyline, or without geometry data. There are two overloads for creating an alignment without geometry data. Both take a reference to the document object, and the name of the new alignment. One takes ObjectIds for the site to associate the alignment with (pass ObjectId.Null to create a siteless alignment), the layer to create the alignment on, the style to apply to the alignment, and the label set style to use. The other overload takes strings naming these items. Here’s a simple example that creates a siteless alignment without geometry data: // Uses an existing Alignment Style named "Basic" and Label Set Style named "All Labels" (for example, from // the _AutoCAD Civil 3D (Imperial) NCS.dwt template. This call will fail if the named styles // don't exist. // Uses layer 0, and no site (ObjectId.Null) ObjectId testAlignmentID = Alignment.Create(doc, "New Alignment", ObjectId.Null, "0", "Basic", "All Labels");

Alignments | 65

There are two overloads of the create() method for creating alignments from polylines. The first takes a reference to the CivilDocument object, a PolylineOptions object (which contains the ID of the polyline to create an alignment from), a name for the new alignment, the ObjectID of a layer to draw to, the ObjectID of an alignment style, and the ObjectID of a label set object, and returns the OjectID of the new Alignment. The second overload takes the same parameters, except the layer, alignment style, and label set are specified by name instead of ObjectID. This code creates an alignment from a 2D polyline, using existing styles: [CommandMethod("CreateAlignment")] public void CreateAlignment() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; // Ask the user to select a polyline to convert to an alignment PromptEntityOptions opt = new PromptEntityOptions("\nSelect a polyline to convert to an Alignment"); opt.SetRejectMessage("\nObject must be a polyline."); opt.AddAllowedClass(typeof(Polyline), false); PromptEntityResult res = ed.GetEntity(opt); // create some polyline options for creating the new alignment PolylineOptions plops = new PolylineOptions(); plops.AddCurvesBetweenTangents = true; plops.EraseExistingEntities = true; plops.PlineId = res.ObjectId; // uses an existing Alignment Style and Label Set Style named "Basic" (for example, from // the Civil 3D (Imperial) NCS Base.dwt template. This call will fail if the named styles // don't exist. ObjectId testAlignmentID = Alignment.Create(doc, plops, "New Alignment", "0", "Standard", "Standard"); }

66 | Chapter 1 API Developer's Guide

Creating an Alignment Offset From Another Alignment Alignments can also be created based on the layout of existing alignments. The Alignment::CreateOffsetAlignment() method creates a new alignment with a constant offset and adds it to the same parent site as the original alignment. The new alignment has the same name (followed by a number in parenthesis) and the same style as the original, but it does not inherit any station labels, station equations, or design speeds from the original alignment. [CommandMethod("CreateOffsetAlignment")] public void CreateOffsetAlignment() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment to create a new offset alignment from PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment."); opt.AddAllowedClass(typeof(Alignment), false); ObjectId alignID = ed.GetEntity(opt).ObjectId; Alignment align = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; // Creates a new alignment with an offset of 10: ObjectId offsetAlignmentID = align.CreateOffsetAlignment(10.0); } }

Defining an Alignment Path Using Entities An alignment is made up of a series of entities, which are individual lines, curves, and spirals that make up the path of an alignment. A collection of entities is held in the Alignment::Entities property, which is an

Alignments | 67

AlignmentEntityCollection object. This collection has a wide array of methods

for creating new entities. Here’s a short code snippet that illustrates one of the methods for adding a FixedCurve entitiy to an alignment’s Entities collection: Int32 previousEntityId = 0; Point3d startPoint = new Point3d(8800.7906, 13098.1946, 0.0000); Point3d middlePoint = new Point3d(8841.9624, 13108.6382, 0.0000); Point3d endPoint = new Point3d(8874.2664, 13089.3333, 0.0000); AlignmentArc retVal = myAlignment.Entities.AddFixedCurve(previousEntityId, startPoint, middlePoint, endPoint);

Determining Entities Within an Alignment Each of the entities in the Alignment::Entities collection is a type derived from the Alignment::AlignmentEntity class. By checking the AlignmentEntity.EntityType property, you can determine the specific type of each entity and cast the reference to the correct type. The following sample loops through all entities in an alignment, determines the type of entity, and prints one of its properties. [CommandMethod("EntityProperties")] public void EntityProperties() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment to get info about PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment.");

68 | Chapter 1 API Developer's Guide

opt.AddAllowedClass(typeof(Alignment), false); ObjectId alignID = ed.GetEntity(opt).ObjectId; Alignment align = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; int i = 0; // iterate through each Entity and check its type foreach (AlignmentEntity myAe in align.Entities){ i++; String msg = ""; switch (myAe.EntityType) { case AlignmentEntityType.Arc: AlignmentArc myArc = myAe as AlignmentArc; msg = String.Format("Entity{0} is an Arc, length: {1}\n", i, myArc.Length); break; case AlignmentEntityType.Spiral: AlignmentSpiral mySpiral = myAe as AlignmentSpiral; msg = String.Format("Entity{0} is a Spiral, length: {1}\n", i, mySpiral.Length); break; // we could detect other entity types as well, such as // Tangent, SpiralCurve, SpiralSpiral, etc. default: msg = String.Format("Entity{0} is not a spiral or arc.\n", i); break; } // write out the Entity information ed.WriteMessage(msg); } } } Each entity has an identification number contained in its AlignmentEntity.EntityId property. Each entity knows the numbers of the entities before and after it in the alignment, and you can access specific entities

Alignments | 69

by identification number through the AlignmentEntityCollection.EntityAtId() method.

Stations Modifying Stations with Station Equations A station is a point along an alignment a certain distance from the start of the alignment. By default the station at the start point of an alignment is 0 and increases contiguously through its length. This can be changed by using station equations, which can renumber stations along an alignment. A station equation is an object of type StationEquation which contains a location along the alignment, a new station number basis, and a flag describing whether station values should increase or decrease from that location on. A collection of these station equations is contained in the Alignment::StationEquations property. The following code adds a station equation to an alignment, starting at a point 80 units from the beginning of the alignment, and increasing in value: StationEquation myStationEquation = myAlignment.StationEquations.Add(80, 0, StationEquationType.Increasing); NOTE Some functions, such as Alignment::DesignSpeedCollection::GetDesignSpeed(), require a “raw”

station value without regard to modifications made by station equations.

Creating Station Sets Alignment stations are usually labeled at regular intervals. You can compute the number, location, and geometry of stations taken at regular spacings by using the Alignment::GetStationSet() method. Overloads of this method return a collection of stations based on the type of station requested, and optionally major and minor intervals.

70 | Chapter 1 API Developer's Guide

// Get all the potential stations with major interval = 100, and minor interval = 20 // Print out the raw station number, type, and location Station[] myStations = myAlignment.GetStationSet( StationType.All,100,20); ed.WriteMessage("Number of possible stations: {0}\n", myStations.Length); foreach (Station myStation in myStations){ ed.WriteMessage("Station {0} is type {1} and at {2}\n", myStation.RawStation, myStation.StnType.ToString(), myStation.Location.ToString()); }

Specifying Design Speeds You can assign design speeds along the length of an alignment to aid in the future design of a roadway based on the alignment. The collection of speeds along an alignment are contained in the Alignment::DesignSpeeds property. Each item in the collection is an object of type DesignSpeed, which contains a raw station value, a speed to be used from that station on until the next specified design speed or the end of the alignment, the design speed number, and an optional string comment. // Starting at station 0 + 00.00 DesignSpeed myDesignSpeed = myAlignment.DesignSpeeds.Add(0, 45); myDesignSpeed.Comment = "Straigtaway"; // Starting at station 4 + 30.00 myDesignSpeed = myAlignment.DesignSpeeds.Add(430, 30); myDesignSpeed.Comment = "Start of curve"; // Starting at station 14 + 27.131 to the end myDesignSpeed = myAlignment.DesignSpeeds.Add(1427.131, 35); myDesignSpeed.Comment = "End of curve"; // make alignment design speed-based: myAlignment.UseDesignSpeed = true;

Alignments | 71

Finding the Location of a Station You can find the point coordinates (northing and easting) of a station and offset on an alignment using the Alignment::PointLocation() method. The simplest version of the method takes a station and offset, and returns a northing and easting (as ref parameters). Another version of this method takes a station, offset, and tolerance, and returns a northing, easting, and bearing (as ref parameters). The tolerance determines on which alignment entity the point is returned. If the tolerance is greater than the desired station minus the station at the alignment entity transition, the point will be reported on that entity. For example, consider an alignment made up of a tangent (length 240) and curve (length 260). Looking for the location of station 400, with tolerance = 0, will find a point on the curve. However, a tolerance of 200 will cause the method to report a point on the tangent, because 400 - 240 < 200.

Superelevation Another setting that can be applied to certain stations of an alignment is the superelevation, used to adjust the angle of roadway section components for corridors based on the alignment. The inside and outside shoulders and road surfaces can be adjusted for both the left and right sides of the road. NOTE The Superelevation feature was substantially changed in AutoCAD Civil 3D 2013, and the API has also changed. The Alignment::SuperelevationData property and Alignment::SuperElevationAtStation() method have been removed. Existing code should be updated to use the new API, and access superelevation via Alignment::SuperelevationCurves, Alignment::SuperelevationCriticalStations, and SuperelevationCriticalStationCollection::GetCriticalStationAt(). The superelevation data for an alignment is divided into discrete curves, called superelevation curves, and each superelevation curve contains transition regions where superelevation transitions from normal roadway to full superelevation and back (with separate regions for transition in, and transition out of superelvation). These regions are defined by “critical stations”, or stations where there is a transition in the roadway cross-section. The collection of superelevation curves for an alignment is accessed with the Alignment::SuperelevationCurves property, while all critical stations for all

72 | Chapter 1 API Developer's Guide

curves is accessed with the Alignment::SuperelevationCriticalStations property. The SuperelevationCurves collection is empty if superelevation curves have not been added to the alignment (either manually, or calculated by the Superelevation Wizard in the user interface). The SuperelevationCriticalStations collection contains default entites for the start and end stations of the Alignment if no superelevation data has been calculated for the curves. An individual SuperelevationCriticalStation can be accessed through the Alignment::SuperelevationCriticalStations::GetCriticalStationAt()

method. In this code snippet, the collection of superelevation curves for an alignment is iterated, and information about each critical station in each curve is printed out. Note that you must specify the cross segment type to get either the slope or smoothing length, but you may not know which segment types are valid for the critical station. In this snippet, the code attempts to get all segment types, and silently catches the InvalidOperationException exception for invalid types. [CommandMethod("GetSECurves")] public void GetSECurves() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // get first alignment: ObjectId alignID = doc.GetAlignmentIds()[0]; Alignment myAlignment = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; if (myAlignment.SuperelevationCurves.Count < 1) { ed.WriteMessage("You must calculate superelevation data.\n"); return; } foreach (SuperelevationCurve sec in myAlignment.SuperelevationCurves) { ed.WriteMessage("Name: {0}\n", sec.Name); ed.WriteMessage(" Start: {0} End: {1}\n",

Alignments | 73

sec.StartStation, sec.EndStation); foreach (SuperelevationCriticalStation sest in sec.CriticalStations) { ed.WriteMessage(" Critical station: {0} {1} {2}\n", sest.TransitionRegionType, sest.Station, sest.StationType); // try to get the slope: foreach (int i in Enum.GetValues(typeof(SuperelevationCrossSegmentType))) { try { // if this succeeds, we know the segment type: double slope = sest.GetSlope((SuperelevationCrossSegmentType)i, false); ed.WriteMessage(" Slope: {0} Segment type: {1}\n",slope,Enum.GetName(typeof(SuperelevationCrossSegmentType),i)); } // silently fail: catch (InvalidOperationException e) { } } } } } }

Alignment Style Creating an Alignment Style Styles govern many aspects of how alignments are drawn, including direction arrows and curves, spirals, and lines within an alignment. All alignment styles are contained in the CivilDocument.Styles.AlignmentStyles collection. Alignment styles must be added to this collection before being used by an alignment object. A style is normally assigned to an alignment when it is first

74 | Chapter 1 API Developer's Guide

created, but it can also be assigned to an existing alignment through the Alignment.StyleId property. [CommandMethod("SetAlignStyle")] public void SetAlignStyle() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment.\n"); opt.AddAllowedClass(typeof(Alignment), false); ObjectId alignID = ed.GetEntity(opt).ObjectId; Alignment myAlignment = ts.GetObject(alignID, OpenMode.ForWrite) as Alignment; ObjectId styleID = doc.Styles.AlignmentStyles.Add("Sample alignment style"); AlignmentStyle myAlignmentStyle = ts.GetObject(styleID, OpenMode.ForWrite) as AlignmentStyle; // don't show direction arrows myAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Arrow).Visible = false; myAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Arrow).Visible = false; // show curves in violet myAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Curve).Color = Color.FromColorIndex(ColorMethod.ByAci, 200); myAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Curve).Color = Color.FromColorIndex(ColorMethod.ByAci, 200); // show straight sections in blue myAlignmentStyle.GetDisplayStyleModel(AlignmentDisplayStyleType.Line).Color = Color.FromColorIndex(ColorMethod.ByAci, 160); myAlignmentStyle.GetDisplayStylePlan(AlignmentDisplayStyleType.Line).Color = Color.FromColorIndex(ColorMethod.ByAci, 160); // assign style to an existing alignment myAlignment.StyleId = myAlignmentStyle.Id; ts.Commit();

Alignments | 75

} }

Alignment Label Styles The style of text labels and graphical markers displayed along an alignment are set by specifying a LabelSet (by name or ObjectID) when the alignment is first created with one of the Alignment::Create() methods, or by assigning the label set object to the CivilDocument.Styles.LabelSetStyles.AlignmentLabelSetStyles property. The AlignmentLabelSetStyles collection consists of separate sets of styles to be placed at major stations, minor stations, and where the alignment geometry, design speed, or station equations change. Alignment labels are described in the AlignmentLabelSetStyle collection, which is a collection of AlignmentLabelSetItem objects. Labels at major stations are described by AlignmentLabelSetItem objects with a LabelStyleType property of LabelStyleType.AlignmentMajorStation. Minor station labels are described by AlignmentLabelSetItem objects with a LabelStyleType property of LabelStyleType.AlignmentMinorStation. Each AlignmentLabelSetItem object has a related LabelStyle object (which you can get or set with the LabelStyleId and LabelStyleName properties) and a number of properties describing the limits of the labels and the interval between labels along the alignment. When a new AlignmentLabelSetItem is created for a minor station label (using BaseLabelSetStyle.Add()), it must reference a parent major station label AlignmentLabelSetItem object. Labels may be placed at the endpoints of each alignment entity. Such labels are controlled through the AlignmentLabelSetItem.GetLabeledAlignmentGeometryPoints() and AlignmentLabelSetItem.GetLabeledAlignmentGeometryPoints() methods. These methods also access labels at each change in alignment design speeds and station equations. The get method returns a Dictionary hash object: Dictionary, specifiying the location of the geometry point, and the bool indicates whether the point is labeled. Label text for all label styles at alignment stations is controlled by a LabelStyle object’s Text component, which is set by the LabelStyle.SetComponent()

76 | Chapter 1 API Developer's Guide

method. The following list of property fields indicates valid values for the Text component: Valid property fields for LabelStyleComponentType.Text Contents

Label styles for minor stations, geometry points, design speeds, and station equations can also use the following property fields:

Minor stations



Geometry points



Geometry points

Alignments | 77



Geometry points



Design speeds



Station equations



Station equations



Station equations

Label styles are described in detail in the chapter 2 section Label Styles (page 27).

Sample Programs AlignmentSample \Sample\Civil 3D API\DotNet\VB.NET\AlignmentSample Some of the sample code from this chapter can be found in context in the AlignmentSample project. This sample shows how to create an alignment, add entities to an alignment, create an alignment label style set, get a site, and create an alignment style.

Sprial2 Demo \Sample\Civil 3D API\DotNet\CSharp\Spiral2 Demo Demonstrates how to get simple and complex alignment information.

Profiles This chapter describes the process for creating and using profiles with the AutoCAD Civil 3D .NET API. For information about using Profiles with the COM API, see Profiles (page 297)

78 | Chapter 1 API Developer's Guide

Profiles Profiles are the vertical analogue to alignments. Together, an alignment and a profile represent a 3D path.

Creating a Profile From a Surface A profile is an object consisting of elevations along an alignment. Each alignment contains a collection of profiles which you can access by the Alignment.GetProfileIds() method. The Profile.CreateFromSurface() method creates a new profile and derives its elevation information from the specified surface along the path of the alignment. // Illustrates creating a new profile from a surface [CommandMethod("ProfileFromSurface")] public void ProfileFromSurface() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment.\n"); opt.AddAllowedClass(typeof(Alignment), true); ObjectId alignID = ed.GetEntity(opt).ObjectId; // get layer id from the alignment Alignment oAlignment = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; ObjectId layerId = oAlignment.LayerId; // get first surface in the document ObjectId surfaceId = doc.GetSurfaceIds()[0]; // get first style in the document ObjectId styleId = doc.Styles.ProfileStyles[0]; // get the first label set style in the document ObjectId labelSetId =

Profiles | 79

doc.Styles.LabelSetStyles.ProfileLabelSetStyles[0]; try { ObjectId profileId = Profile.CreateFromSurface("My Profile", alignID, surfaceId, layerId, styleId, labelSetId); ts.Commit(); } catch (Autodesk.AutoCAD.Runtime.Exception e) { ed.WriteMessage(e.Message); } } }

Creating a Profile Using Entities The various Profile.CreateByLayout() overloaded methods create a new profile with no elevation information. The vertical shape of a profile can then be specified using entities. Entities are geometric elements - tangents or symmetric parabolas. The collection of all entities that make up a profile are contained in the Profile.Entities collection. The ProfileEntityCollection class also contains all the methods for creating new entities. This sample creates a new profile along the alignment “oAlignment” and then adds three entities to define the profile shape. Two straight entities are added at each end and a symmetric parabola is added in the center to join them and represent the sag of a valley. // Illustrates creating a new profile without elevation data, then adding the elevation // via the entities collection [CommandMethod("CreateProfileNoSurface")] public void CreateProfileNoSurface() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts =

80 | Chapter 1 API Developer's Guide

Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment.\n"); opt.AddAllowedClass(typeof(Alignment), false); ObjectId alignID = ed.GetEntity(opt).ObjectId; Alignment oAlignment = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; // use the same layer as the alignment ObjectId layerId = oAlignment.LayerId; // get the standard style and label set // these calls will fail on templates without a style named "Standard" ObjectId styleId = doc.Styles.ProfileStyles["Standard"]; ObjectId labelSetId = doc.Styles.LabelSetStyles.ProfileLabelSetStyles["Standard"]; ObjectId oProfileId = Profile.CreateByLayout("My Profile", alignID, layerId, styleId, labelSetId); // Now add the entities that define the profile. Profile oProfile = ts.GetObject(oProfileId, OpenMode.ForRead) as Profile; Point3d startPoint = new Point3d(oAlignment.StartingStation, -40, 0); Point3d endPoint = new Point3d(758.2, -70, 0); ProfileTangent oTangent1 = oProfile.Entities.AddFixedTangent(startPoint, endPoint); startPoint = new Point3d(1508.2, -60.0, 0); endPoint = new Point3d(oAlignment.EndingStation, -4.0, 0); ProfileTangent oTangent2 =oProfile.Entities.AddFixedTangent(startPoint, endPoint);

Profiles | 81

oProfile.Entities.AddFreeSymmetricParabolaByLength(oTangent1.EntityId, oTangent2.EntityId, VerticalCurveType.Sag, 900.1, true); ts.Commit(); } }

Editing Points of Vertical Intersection The point where two adjacent tangents would cross (whether they actually cross or not) is called the “point of vertical intersection”, or “PVI.” This location can be useful for editing the geometry of a profile because this one point controls the slopes of both tangents and any curve connecting them. The collection of all PVIs in a profile is contained in the Profile.PVIs property. This collection lets you access, add, and remove PVIs from a profile, which can change the position and number of entities that make up the profile. Individual PVIs (type ProfilePVI) do not have a name or id, but are instead identified by a particular station and elevation. The collection methods ProfilePVICollection.GetPVIAt and ProfilePVICollection.RemoveAt either access or delete the PVI closest to the station and elevation parameters so you do not need the exact location of the PVI you want to modify. This sample identifies the PVI closest to a specified point. It then adds a new PVI to the profile created in the Creating a Profile Using Entities (page 299) topic and adjusts its elevation. [CommandMethod("EditPVI")] public void EditPVI() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // get first profile of first alignment in document ObjectId alignID = doc.GetAlignmentIds()[0]; Alignment oAlignment = ts.GetObject(alignID, OpenMode.ForRead) as Alignment; Profile oProfile =

82 | Chapter 1 API Developer's Guide

ts.GetObject(oAlignment.GetProfileIds()[0], OpenMode.ForRead) as Profile; // check to make sure we have a profile: if (oProfile == null) { ed.WriteMessage("Must have at least one alignment with one profile"); return; } // Find the PVI close to station 1000 elevation -70. ProfilePVI oProfilePVI = oProfile.PVIs.GetPVIAt(1000, -70); ed.WriteMessage("PVI closest to station 1000 is at station: {0}", oProfilePVI.Station); // Add another PVI and slightly adjust its elevation. oProfilePVI = oProfile.PVIs.AddPVI(607.4, -64.3); oProfilePVI.Elevation -= 2.0; ts.Commit(); } }

Creating a Profile Style The profile style, an object of type ProfileStyle, defines the visual display of profiles. The collection of all such styles in a document are stored in the CivilDocument.Styles.ProfileStyles collection. The style contains properties of type DisplayStyle which govern the display of arrows showing alignment direction and of the lines, line extensions, curves, parabolic curve extensions, symmetrical parabolas and asymmetrical parabolas that make up a profile. The properties of a new profile style are defined by the document’s ambient settings. // Illustrates creating a new profile style [CommandMethod("CreateProfileStyle")] public void CreateProfileStyle() { doc = CivilApplication.ActiveDocument;

Profiles | 83

Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { ObjectId profileStyleId = doc.Styles.ProfileStyles.Add("New Profile Style"); ProfileStyle oProfileStyle = ts.GetObject(profileStyleId, OpenMode.ForRead) as ProfileStyle; oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.Arrow).Visible = true; // set to yellow: oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.Line).Color = Color.FromColorIndex(ColorMethod.ByAci, 50); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.Line).Visible = true; // grey oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.LineExtension).Color = Color.FromColorIndex(ColorMethod.ByAci, 251); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.LineExtension).Visible = true; // green oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.Curve).Color = Color.FromColorIndex(ColorMethod.ByAci, 80); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.Curve).Visible = true; // grey oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.ParabolicCurveExtension).Color = Color.FromColorIndex(ColorMethod.ByAci, 251); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.ParabolicCurveExtension).Visible = true; // green oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.SymmetricalParabola).Color = Color.FromColorIndex(ColorMethod.ByAci, 81); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.SymmetricalParabola).Visible = true; // green oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.AsymmetricalParabola).Color = Color.FromColorIndex(ColorMethod.ByAci, 83); oProfileStyle.GetDisplayStyleProfile(ProfileDisplayStyleProfileType.AsymmetricalParabola).Visible = true;

84 | Chapter 1 API Developer's Guide

// properties for 3D should also be set } }

Profile Views This section describes the creation and display of profile views. A profile view is a graph displaying the elevation of a profile along the length of the related alignment.

Creating a Profile View The ProfileView class has two versions of the ProfileView::Create() method for adding new ProfileView objects to a drawing. Each method overload takes a reference to the CivilDrawing object, the name of the new profile view, and a Point3d location in the drawing where the profile view is inserted. They also both take a band set style and alignment; one version takes these arguments as ObjectIds, while the other takes them as strings. This example demonstrates creating a new ProfileView: [CommandMethod("CreateProfileView")] public void CreateProfileView() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Ask the user to select an alignment PromptEntityOptions opt = new PromptEntityOptions("\nSelect an Alignment"); opt.SetRejectMessage("\nObject must be an alignment.\n"); opt.AddAllowedClass(typeof(Alignment), false); ObjectId alignID = ed.GetEntity(opt).ObjectId; // Create insertion point: Point3d ptInsert = new Point3d(100, 100, 0);

Profiles | 85

// Get profile view band set style ID: ObjectId pfrVBSStyleId = doc.Styles.ProfileViewBandSetStyles["Standard"]; // If this doesn't exist, get the first style in the collection if (pfrVBSStyleId == null) pfrVBSStyleId = doc.Styles.ProfileViewBandSetStyles[0]; ObjectId ProfileViewId = ProfileView.Create(doc, "New Profile View", pfrVBSStyleId, alignID, ptInsert); ts.Commit(); } }

Creating Profile View Styles The profile view style, an object of type ProfileViewStyle, governs all aspects of how the graph axes, text, and titles are drawn. Within ProfileViewStyle are objects dealing with the top, bottom, left, and right axes; lines at geometric locations within profiles; and with the graph as a whole. All profile view styles in the document are stored in the CivilDocument.ProfileViewStyles collection. New styles are created using the collection’s Add method with the name of the new style. ObjectId profileViewStyleId = doc.Styles.ProfileViewStyles.Add("New Profile View Style"); ProfileViewStyle oProfileViewStyle = ts.GetObject(profileViewStyleId, OpenMode.ForRead) as ProfileViewStyle;

Setting Profile View Styles The profile view style object consists of separate objects for each of the four axes, one object for the graph overall, and an DisplayStyle object for grid lines displayed at horizontal geometry points (accessed with the GetDisplayStylePlan() method). The axis styles and graph style also contain subobjects for specifying the style of tick marks and titles.

86 | Chapter 1 API Developer's Guide

Setting the Axis Style All axis styles are based on the AxisStyle class. The axis style object controls the display style of the axis itself, tick marks and text placed along the axis, and a text annotation describing the axis’s purpose. The annotation text, location, and size is set through the AxisStyle.TitleStyle property, an object of type AxisTitleStyle. The annotation text can use any of the following property fields: Valid property fields for AxisTitleStyle.Text

Axis Tick Marks Within each axis style are properties for specifying the tick marks placed along the axis. Both major tick marks and minor tick marks are represented by objects of type AxisTickStyle. The AxisTickStyle class manages the location, size, and visual style of tick marks through its Interval, Size and other properties. Note that while most style properties use drawing units, the Interval property uses the actual ground units of the surface. The AxisTickStyle object also determines the text that is displayed at each tick, including the following property fields: Valid property fields for TickStyle.Text

Axis



horizontal



horizontal



horizontal



vertical

Profiles | 87

Valid property fields for TickStyle.Text

Axis



vertical

Setting the Graph Style The graph is managed by objects of type GraphStyle and GridStyle. These objects can be used to change the scale, title, and grid of the graph. The grid is controlled by the ProfileViewStyle.GridStyle property, an object of type GridStyle. The grid style sets the amount of empty space above and below the extents of the section through the GridStyle.GridPaddingAbove and GridStyle.GridPaddingBottom properties. The grid style also manages the line styles of major and minor vertical and horizontal gridlines with the DisplayStyle.PlotStyle property accessed by the GetDisplayStylePlan() method.

Graph Title The title of the graph is controlled by the GraphStyle.TitleStyle property, an object of type GraphTitleStyle. The title style object can adjust the position, style, and border of the title. The text of the title can include any of the following property fields: Valid property fields for GraphTitleStyle.Text

88 | Chapter 1 API Developer's Guide

Valid property fields for GraphTitleStyle.Text

Working With Hatch Areas Hatch areas are a feature of profile views that apply a style to areas of cut and fill to highlight them. In addition to cut and fill, a hatch area can highlight areas of intersection between any two defined profiles. The hatching feature for ProfileView objects is exposed by the HatchAreas property. This is a collection of all ProfileHatchArea objects defined for the ProfileView, which can be used to access or add additional hatch areas. Each ProfileHatchArea has a set of criteria (ProfileCriteria objects) that specify the profile that defines the upper or lower boundary for the hatch area. The criteria also references a ShapeStyle object that defines how the hatch area is styled in the profile view. This code sample illustrates how to access the hatch areas for a profile view, and prints out some information about each ProfileHatchArea object’s criteria: [CommandMethod("ProfileHatching")] public void ProfileHatching () { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using ( Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction() ) { // Ask the user to select a profile view PromptEntityOptions opt = new PromptEntityOptions("\nSelect a profile view"); opt.SetRejectMessage("\nObject must be a profile view.\n"); opt.AddAllowedClass(typeof(ProfileView), false);

Profiles | 89

ObjectId profileViewID = ed.GetEntity(opt).ObjectId; ProfileView oProfileView = ts.GetObject(profileViewID, OpenMode.ForRead) as ProfileView; ed.WriteMessage("\nHatch areas defined in this profile view: \n"); foreach ( ProfileHatchArea oProfileHatchArea in oProfileView.HatchAreas ) { ed.WriteMessage(" Hatch area: " + oProfileHatchArea.Name + " shape style: " + oProfileHatchArea.ShapeStyleName + "\n"); foreach ( ProfileCriteria oProfileCriteria in oProfileHatchArea.Criteria ) { ed.WriteMessage(string.Format(" Criteria: type: {0} profile: {1}\n", oProfileCriteria.BoundaryType.ToString(), oProfileCriteria.ProfileName) ); } } } }

Profile View Style Example This example takes an existing profile view style and modifies its top axis and title: [CommandMethod("ModProfileViewStyle")] public void ModProfileViewStyle() { doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()) { // Get the first style in the document's collection of styles ObjectId profileViewStyleId =

90 | Chapter 1 API Developer's Guide

doc.Styles.ProfileViewStyles[0]; ProfileViewStyle oProfileViewStyle = ts.GetObject(profileViewStyleId, OpenMode.ForRead) as ProfileViewStyle; // Adjust the top axis. Put station information here, with the title // at the far left. oProfileViewStyle.GetDisplayStylePlan(ProfileViewDisplayStyleType.TopAxis).Visible = true; oProfileViewStyle.TopAxis.MajorTickStyle.LabelText = " m"; oProfileViewStyle.TopAxis.MajorTickStyle.Interval = 164.041995; oProfileViewStyle.TopAxis.TitleStyle.OffsetX = 0.13; oProfileViewStyle.TopAxis.TitleStyle.OffsetY = 0.0; oProfileViewStyle.TopAxis.TitleStyle.Text = "Meters"; oProfileViewStyle.TopAxis.TitleStyle.Location = Autodesk.Civil.DatabaseServices.Styles.AxisTitleLocationType.TopOrLeft; // Adjust the title to show the alignment name oProfileViewStyle.GraphStyle.TitleStyle.Text = "Profile View of:"; ts.Commit(); } }

Sample Programs Profile Sample This sample is located in \Sample\Civil 3D API\DotNet\VB.NET\ProfileSample\. It illustrates: ■ How to create a profile ■

Creating profile styles and profile views

Profiles | 91

Pipe Networks This chapter describes working with pipe networks with the AutoCAD Civil 3D .NET API.

Base Objects This section explains how to get the base objects required for using the pipe network API classes.

Accessing Pipe Network-Specific Base Objects Applications that access pipe networks do so through the CivilDocument object. This is different from the COM API, in which pipe network functionality is accessed through the separate AeccPipeDocument instead of AeccDocument. In the .NET API, the CivilDocument object contains collections of pipe network-related items, such as pipe networks, pipe styles, and interference checks.

Pipe-Specific Ambient Settings Ambient settings allow you to get and set the units and default property settings of pipe network objects as well as access the catalog of all pipe and structure parts held in the document. Ambient settings for a pipe document are obtained from the CivilDocument.Settings.GetSettings() method, which returns an object inherited from SettingsAmbient. Among the classes that inherit from SettingsAmbient are SettingsPipe, SettingsPipeNetwork, and SettingsStructure. Each of these has properties that describe the default units of measurement for interference, pipe, and structure objects. The PipeSettingsRoot.PipeNetworkSettings property contains the name of the default styles for pipe and structure objects as well as the default label placement, units, and naming conventions for pipe networks as a whole. public void ShowPipeRules() { CivilDocument doc = CivilApplication.ActiveDocument;

92 | Chapter 1 API Developer's Guide

Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; SettingsPipeNetwork oSettingsPipeNetwork = doc.Settings.GetSettings() as SettingsPipeNetwork; ed.WriteMessage("Using pipe rules: {0}\n", oSettingsPipeNetwork.Rules.Pipe.Value); // Set the default units used for pipes in this document. oSettingsPipeNetwork.Angle.Unit.Value = Autodesk.Civil.AngleUnitType.Radian; oSettingsPipeNetwork.Coordinate.Unit.Value = Autodesk.Civil.LinearUnitType.Foot; oSettingsPipeNetwork.Distance.Unit.Value = Autodesk.Civil.LinearUnitType.Foot; }

Listing and Adding Dynamic Part Properties Each type of pipe and structure has many unique attributes (such as size, geometry, design, and composition) that cannot be stored in the standard pipe and structure properties. To give each part appropriate attributes, pipe and structure objects have sets of dynamic properties. A single property is represented by an PartDataField object. Data fields are held in collections of type PartDataRecord. You can reach these collections through the PartData property of the Part class, from which Pipe and Structure objects inherit. Each data field contains an internal variable name, a text description of the value, a global context used to identify the field, data type, and the data value itself, as well as other properties. This sample enumerates all the data fields contained in a pipe object “oPipe” and displays information from each field. // Get PartDataRecord for first pipe in the network ObjectId pipeId = oNetwork.GetPipeIds()[0]; Pipe oPipe = ts.GetObject(pipeId, OpenMode.ForRead) as Pipe; PartDataField[] oDataFields = oPipe.PartData.GetAllDataFields(); ed.WriteMessage("Additional info for pipe: {0}\n", oPipe.Name); foreach (PartDataField oPartDataField in oDataFields)

Pipe Networks | 93

{ ed.WriteMessage("Name: {0}, Description: {1}, {2}, Value: {3}\n", oPartDataField.Name, oPartDataField.Description, oPartDataField.DataType, oPartDataField.Value); }

DataType:

Dynamic properties created with the NetworkCatalogDef class are not yet supported by the .NET API.

Retrieving the Parts List CivilDocument.Styles.PartsListSet contains a read-only collection of all

the lists of part types available in the document. Each list is an object of type PartList, a read-only collection of PartsList ObjectIds. A part family represents a broad category of parts, and is identified by a GUID (Globally Unique Identification) value. A part family can only contain parts from one domain - either pipes or structures but not both. Part families contain a read-only collection of part filters (PartSizeFilter), which are the particular sizes of parts. A part filter is defined by its PartSizeFilter.PartDataRecord property, a collection of fields describing various aspects of the part. This sample prints the complete listing of all parts in a document. [CommandMethod("PrintParts")] public void PrintParts() { CivilDocument doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { // SettingsPipeNetwork oSettingsPipeNetwork = doc.Settings.GetSettings() as SettingsPipeNetwork; PartsListCollection oPartListCollection = doc.Styles.PartsListSet; ed.WriteMessage("Number of parts lists in document:

94 | Chapter 1 API Developer's Guide

{0}\n", oPartListCollection.Count); foreach (ObjectId objId in oPartListCollection) { PartsList oPartsList = ts.GetObject(objId, OpenMode.ForWrite) as PartsList; ed.WriteMessage("PARTS LIST: {0}\n----------------\n", oPartsList.Name); // From the part list, looking at only those part families // that are pipes, print all the individual parts, plus // some information about each part. ObjectIdCollection pipeFamilyCollection = oPartsList.GetPartFamilyIdsByDomain(DomainType.Pipe); ed.WriteMessage(" Pipes\n =====\n"); foreach (ObjectId objIdPfa in pipeFamilyCollection) { PartFamily oPartFamily = ts.GetObject(objIdPfa, OpenMode.ForWrite) as PartFamily; if (oPartFamily.Domain == DomainType.Pipe) { ed.WriteMessage(" Family: {0}\n", oPartFamily.Name); SizeFilterRecord oSizeFilterRecord = oPartFamily.PartSizeFilter; SizeFilterField SweptShape = oSizeFilterRecord.GetParamByContextAndIndex(PartContextType.SweptShape, 0); SizeFilterField MinCurveRadius = oSizeFilterRecord.GetParamByContextAndIndex(PartContextType.MinCurveRadius, 0); //SizeFilterField StructPipeWallThickness; SizeFilterField FlowAnalysisManning = oSizeFilterRecord.GetParamByContextAndIndex(PartContextType.FlowAnalysisManning, 0); SizeFilterField m_Material = oSizeFilterRecord.GetParamByContextAndIndex(PartContextType.Material, 0); // SizeFilterField PipeInnerDiameter =

Pipe Networks | 95

oSizeFilterRecord.GetParamByContextAndIndex(PartContextType.PipeInnerDiameter, 0); ed.WriteMessage(" {0}: {1}, {2}: {3}, {4}: {5} {6}: {7}\n", SweptShape.Description, SweptShape.Value, MinCurveRadius.Description, MinCurveRadius.Value, FlowAnalysisManning.Description, FlowAnalysisManning.Value, m_Material.Description, m_Material.Value ); } } // From the part list, looking at only those part families // that are structures, print all the individual parts. ed.WriteMessage(" Structures\n =====\n"); foreach (ObjectId objIdPfa in pipeFamilyCollection) { PartFamily oPartFamily = ts.GetObject(objIdPfa, OpenMode.ForWrite) as PartFamily; if (oPartFamily.Domain == DomainType.Structure) { ed.WriteMessage(" Family: {0}\n", oPartFamily.Name); } } } } }

Creating a Pipe Network A pipe network is a set of interconnected or related parts. The collection of all pipe networks is returned by the CivilDocument.GetPipeNetworkIds() method. A pipe network, an object of type Network, contains the collection of pipes and the collection of structures which make up the network. Network

96 | Chapter 1 API Developer's Guide

also contains the method FindShortestNetworkPath() for determining the path between two network parts. The Network.ReferenceSurfaceId is used primarily for Pipe Rules. For example, you can have a rule that places the structure rim at a specified elevation from the surface. Public Function CreatePipeNetwork() As Boolean Dim trans As Transaction = tm.StartTransaction() Dim oPipeNetworkIds As ObjectIdCollection Dim oNetworkId As ObjectId Dim oNetwork As Network oNetworkId = Network.Create(g_oDocument, NETWORK_NAME) ' get the network Try oNetwork = trans.GetObject(oNetworkId, OpenMode.ForWrite) Catch CreatePipeNetwork = False Exit Function End Try ' 'Add pipe and Structure ' Get the Networks collections oPipeNetworkIds = g_oDocument.GetPipeNetworkIds() If (oPipeNetworkIds Is Nothing) Then MsgBox("There is no PipeNetwork Collection." + Convert.ToChar(10)) ed.WriteMessage("There is no PipeNetwork Collection." + Convert.ToChar(10)) CreatePipeNetwork = False Exit Function End If Dim oPartsListId As ObjectId = g_oDocument.Styles.PartsListSet(PARTS_LIST_NAME) 'Standard PartsList Dim oPartsList As PartsList = trans.GetObject(oPartsListId, OpenMode.ForWrite) Dim oidPipe As ObjectId = oPartsList("Concrete Pipe SI") Dim opfPipe As PartFamily = trans.GetObject(oidPipe, OpenMode.ForWrite) Dim psizePipe As ObjectId = opfPipe(0)

Pipe Networks | 97

Dim line As LineSegment3d = New LineSegment3d(New Point3d(30, 9, 0), New Point3d(33, 7, 0)) Dim oidNewPipe As ObjectId = ObjectId.Null oNetwork.AddLinePipe(oidPipe, psizePipe, line, oidNewPipe, True) Dim oidStructure As ObjectId = oPartsList("CMP Rectangular End Section SI") Dim opfStructure As PartFamily = trans.GetObject(oidStructure, OpenMode.ForWrite) Dim psizeStructure As ObjectId = opfStructure(0) Dim startPoint As Point3d = New Point3d(30, 9, 0) Dim endPoint As Point3d = New Point3d(33, 7, 0) Dim oidNewStructure As ObjectId = ObjectId.Null oNetwork.AddStructure(oidStructure, psizeStructure, startPoint, 0, oidNewStructure, True) oNetwork.AddStructure(oidStructure, psizeStructure, endPoint, 0, oidNewStructure, True) ed.WriteMessage("PipeNetwork created" + Convert.ToChar(10)) trans.Commit() CreatePipeNetwork = True End Function ' CreatePipeNetwork

Pipes This section explains the creation and use of pipes. Pipes represent the conduits within a pipe network.

Creating Pipes Pipe objects represent the conduits of the pipe network. Pipes are created using the pipe network’s methods for creating either straight or curved pipes, AddLinePipe() and AddCurvePipe(). Both methods require you to specify a particular part family (using the ObjectId of a family) and a particular part size filter object as well as the geometry of the pipe. This sample creates a straight pipe between two hard-coded points using the first pipe family and pipe size filter it can find in the part list: [CommandMethod("AddPipe")]

98 | Chapter 1 API Developer's Guide

public void AddPipe() { CivilDocument doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { ObjectIdCollection oIdCollection = doc.GetPipeNetworkIds(); // Get the first network in the document ObjectId objId = oIdCollection[0]; Network oNetwork = ts.GetObject(objId, OpenMode.ForWrite) as Network; ed.WriteMessage("Pipe Network: {0}\n", oNetwork.Name); // Go through the list of part types and select the first pipe found ObjectId pid = oNetwork.PartsListId; PartsList pl = ts.GetObject(pid, OpenMode.ForWrite) as PartsList; ObjectId oid = pl["Concrete Pipe"]; PartFamily pfa = ts.GetObject(oid, OpenMode.ForWrite) as PartFamily; ObjectId psize = pfa[0]; LineSegment3d line = new LineSegment3d(new Point3d(30, 9, 0), new Point3d(33, 7, 0)); ObjectIdCollection col = oNetwork.GetPipeIds(); ObjectId oidNewPipe = ObjectId.Null; oNetwork.AddLinePipe(oid, psize, line, ref oidNewPipe, false); Pipe oNewPipe = ts.GetObject(oidNewPipe, OpenMode.ForRead) as Pipe; ed.WriteMessage("Pipe created: {0}\n", oNewPipe.DisplayName); ts.Commit(); } }

Pipe Networks | 99

Using Pipes To make a new pipe a meaningful part of a pipe network, it must be connected to structures or other pipes using the Pipe.ConnectToStructure() or Pipe.ConnectToPipe() methods, or structures must be connected to it using the Structure.ConnectToPipe() method. Connecting pipes together directly creates a new virtual Structure object to serve as the joint. If a pipe end is connected to a structure, it must be disconnected before attempting to connect it to a different structure. After a pipe has been connected to a network, you can determine the structures at either end by using the StartStructureId and EndStructureId properties. There are methods and properties for setting and determining the flow direction, getting all types of physical measurements, and for accessing collections of user-defined properties for custom descriptions of the pipe.

Creating Pipe Styles A pipe style controls the visual appearance of pipes in a document. All pipe style objects in a document are stored in the CivilDocument.PipeStyles collection. Pipe styles have four display methods and three hatch methods for controlling general appearance attributes and three properties for controlling display attributes that are specific to pipes. The methods GetDisplayStyleProfile|Section|Plan(), and GetHatchStyleProfile() all take a parameter describing the feature being modified, and return a reference to the DisplayStyle or HatchDisplayStyle object controlling common display attributes, such as line styles and color. The methods GetDisplayStyleModel(), GetHatchStylePlan(), and GetHatchStyleSection() do not take a component parameter. The properties PlanOption and ProfileOption set the size of the inner wall, outer wall, and end lines according to either the physical properties of the pipe, custom sizes using drawing units, or a certain percentage of its previous drawing size. The HatchOption property sets the area of the pipe covered by any hatching used. A pipe object is given a style by assigning the Pipe.Style property to a PipeStyle object. This sample attempts to create a new pipe style object and set some of its properties. If a style already exists with the same name, it sets the properties on the existing style: Public Function CreatePipeStyle(ByVal sStyleName As String)

100 | Chapter 1 API Developer's Guide

As PipeStyle Dim oPipeStyleId As ObjectId Dim oPipeStyle As PipeStyle Dim trans As Transaction = tm.StartTransaction() Try oPipeStyleId = g_oDocument.Styles.PipeStyles.Add(sStyleName) Catch End Try If (oPipeStyleId = ObjectId.Null) Then Try oPipeStyleId = g_oDocument.Styles.PipeStyles.Item(sStyleName) Catch End Try If (oPipeStyleId = ObjectId.Null) Then MsgBox("Could not create or use a pipe style with the name:" & sStyleName) CreatePipeStyle = Nothing Exit Function End If End If oPipeStyle = trans.GetObject(oPipeStyleId, OpenMode.ForWrite) ' Set the display size of the pipes in plan view. We will ' use absolute drawing units for the inside, outside, and ' ends of each pipe. ' enter a value greater than or equal to 0.000mm and less than or equal to 1000.000mm oPipeStyle.PlanOption.InnerDiameter = 0.0021 oPipeStyle.PlanOption.OuterDiameter = 0.0024 ' Indicate that we will use our own measurements for the inside ' and outside of the pipe, and not base drawing on the actual ' type of pipe. oPipeStyle.PlanOption.WallSizeType = PipeWallSizeType.UserDefinedWallSize ' Inidcate what kind of custom sizing to use. oPipeStyle.PlanOption.WallSizeOptions = PipeUserDefinedType.UseDrawingScale

Pipe Networks | 101

oPipeStyle.PlanOption.EndLineSize = 0.0021 ' Indicate that we will use our own measurements for the end 'line of the pipe, and not base drawing on the actual type ' of pipe. oPipeStyle.PlanOption.EndSizeType = PipeEndSizeType.UserDefinedEndSize ' Inidcate what kind of custom sizing to use. oPipeStyle.PlanOption.EndSizeOptions = PipeUserDefinedType.UseDrawingScale ' ' Modify the colors of pipes using this style, as shown 'in plan view. oPipeStyle.GetDisplayStylePlan(PipeDisplayStylePlanType.OutsideWalls).Color = Color.FromRgb(255, 191, 0) ' orange, ColorIndex = 40 oPipeStyle.GetDisplayStylePlan(PipeDisplayStylePlanType.InsideWalls).Color = Color.FromRgb(191, 0, 255) ' violet, ColorIndex = 200 oPipeStyle.GetDisplayStylePlan(PipeDisplayStylePlanType.EndLine).Color = Color.FromRgb(191, 0, 255) ' violet, ColorIndex = 200 ' ' Set the hatch style for pipes using this style, as shown 'in plan view. oPipeStyle.GetHatchStylePlan().Pattern = "DOTS" oPipeStyle.GetHatchStylePlan().HatchType = Autodesk.Civil.DatabaseServices.Styles.HatchType.PreDefined oPipeStyle.GetHatchStylePlan().UseAngleOfObject = False oPipeStyle.GetHatchStylePlan().ScaleFactor = 9.0# oPipeStyle.GetDisplayStylePlan(PipeDisplayStylePlanType.Hatch).Color = Color.FromRgb(0, 255, 191) ' turquose, ColorIndex = 120 oPipeStyle.GetDisplayStylePlan(PipeDisplayStylePlanType.Hatch).Visible = True oPipeStyle.PlanOption.HatchOptions = PipeHatchType.HatchToInnerWalls trans.Commit() ed.WriteMessage("Create PipeStyle succeeded." + Convert.ToChar(10)) CreatePipeStyle = oPipeStyle End Function ' CreatePipeStyle

102 | Chapter 1 API Developer's Guide

Creating Pipe Label Styles The collection of all pipe label styles in a document is found in the CivilDocument.Styles.PipeLabelStyles property, which is a LabelStylesPipeRoot object. This object lets you get and set cross section label styles, plan / profile view label styles, and default label styles for pipes. NOTE The label style of a particular pipe cannot be set using the .NET API.

Structures This section describes the creation and use of structures. Structures are the connectors within a pipe network.

Creating Structures Structures represent physical objects such as manholes, catch basins, and headwalls. Logically, structures are used as connections between pipes at pipe endpoints. In cases where two pipes connect directly, an Structure object not representing any physical object is still created to serve as the joint. Any number of pipes can connect with a structure. Structures are represented by objects of type Structure, which are created by using the AddStructure() method of Network. See the code sample in Creating a Pipe Network (page 96) for an example of how to call this method.

Using Structures To make the new structure a meaningful part of a pipe network, it must be connected to pipes in the network using the Structure.ConnectToPipe() method or pipes must be connected to it using the Pipe.ConnectToStructure() method. After a structure has been connected to a network, you can determine the pipes connected to it by using the ConnectedPipe property, which is a read-only collection of network parts. There are also methods and properties for setting and determining all types of physical measurements for the structure

Pipe Networks | 103

and for accessing collections of user-defined properties for custom descriptions of the structure.

Creating Structure Styles A structure style controls the visual appearance of structures in a document. All structure style objects are stored in the CivilDocument.Styles.StructureStyles property. Structure styles have four methods for controlling general appearance attributes and three properties for controlling display attributes that are specific to structures. The methods GetDisplayStylePlan|Profile|Section() and GetHatchStyleProfile() all take a parameter describing the feature being modified and return a reference to the DisplayStyle or HatchDisplayStyle object controlling common display attributes such as line styles and color. The properties PlanOption, ProfileOption, SectionOption, and ModelOption set the display size of the structure and whether the structure is shown as a model of the physical object or only symbolically. A structure object is given a style by assigning the Structure.StyleId or Structure.StyleName property to a StructureStyle object. This sample attempts to create a new structure style object and set some of its properties. If the style already exists, it changes the existing style: Public Function CreateStructureStyle(ByVal sStyleName As String) As StructureStyle Dim oStructureStyle As StructureStyle Dim oStructureStyleId As ObjectId Dim trans As Transaction = tm.StartTransaction() Try oStructureStyleId = g_oDocument.Styles.StructureStyles.Add(sStyleName) Catch End Try If (oStructureStyleId = ObjectId.Null) Then Try oStructureStyleId = g_oDocument.Styles.StructureStyles.Item(sStyleName) Catch End Try If (oStructureStyleId = ObjectId.Null) Then MsgBox("Could not create or use a structure style with the name:" & sStyleName)

104 | Chapter 1 API Developer's Guide

CreateStructureStyle = Nothing Exit Function End If End If oStructureStyle = trans.GetObject(oStructureStyleId, OpenMode.ForWrite) oStructureStyle.GetDisplayStylePlan(StructureDisplayStylePlanType.Structure).Color = Color.FromRgb(255, 191, 0) ' orange oStructureStyle.GetDisplayStylePlan(StructureDisplayStylePlanType.Structure).Visible = True oStructureStyle.PlanOption.MaskConnectedObjects = False oStructureStyle.PlanOption.SizeType = StructureSizeOptionsType.UseDrawingScale oStructureStyle.PlanOption.Size = 0.0035 oStructureStyle.GetDisplayStyleSection(StructureDisplayStylePlanType.Structure).Visible = False oStructureStyle.GetDisplayStyleSection(StructureDisplayStylePlanType.StructureHatch).Visible = False oStructureStyle.GetDisplayStylePlan(StructureDisplayStylePlanType.StructureHatch).Visible = False oStructureStyle.GetDisplayStyleProfile(StructureDisplayStylePlanType.Structure).Visible = False oStructureStyle.GetDisplayStyleProfile(StructureDisplayStylePlanType.StructureHatch).Visible = False trans.Commit() ed.WriteMessage("Create StructureStyle Successful." + Convert.ToChar(10)) CreateStructureStyle = oStructureStyle End Function ' CreateStructureStyle

Creating Structure Label Styles The collection of all structure label styles in a document is found in the CivilDocument.Styles.LabelStyles.StructureLabelStyles property, which is a LabelStylesStructureRoot object. . NOTE The label style of a particular structure cannot be set using the .NET API.

Pipe Networks | 105

Interference Checks This section explains how to generate and examine an interference check. An interference check is used to determine when pipe network parts are either intersecting or are too close together.

Performing an Interference Check This functionality is not yet supported by the .NET API.

Listing the Interferences This functionality is not yet supported by the .NET API.

Interference Check Styles Either a symbol or a model of the actual intersection region can be drawn at each interference location. The display of these intersections is controlled by an InterferenceStyle object. The collection of all interference style objects in the document are stored in the CivilDocument.Styles.InterferenceStyles collection. There are three different styles of interference displays you can chose from. First, you can display a 3D model of the intersection region. This is done by setting the ModelOptions style property to InterferenceModelType.TrueSolid. The GetDisplayStyleModel() method returns an object of type DisplayStyle which controls the visible appearance of the model such as color and line types. Make sure the DisplayStyle.Visible property is set to True. Another possibility is to draw a 3D sphere at the location of the intersection. This is done by setting the ModelOptions style property to InterferenceModelType.Sphere. If the ModelSizeType property is set to InterferenceModelSizeType.SolidExtents, then the sphere is automatically sized to just circumscribe the region of intersection (that is, it is the smallest sphere that still fits the model of the intersection region). You can set the size of the sphere by setting the ModelSizeType property to InterferenceModelSizeType.UserSpecified, setting the ModelSizeOptions property to use either absolute units or drawing units, and setting the

106 | Chapter 1 API Developer's Guide

corresponding AbsoluteModelSize or DrawingScaleModelSize property to the desired value. Again, the DisplayStyle object returned by GetDisplayStyleModel() controls the visual features such as color and line type. The third option is to place a symbol at the location of intersection. Set the GetDisplayStylePlan(InterferenceDisplayStyleType.Symbol).Visible property to True to make symbols visible. The style property MarkerStyle, an object of type MarkerStyle, controls all aspects of how the symbol is drawn.

This sample creates a new interference style object that displays an X symbol with a superimposed circle at points of intersection: public void InterfStyle() { CivilDocument doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { ObjectId intStyleId; intStyleId = doc.Styles.InterferenceStyles.Add("Interference style 01"); InterferenceStyle oIntStyle = ts.GetObject(intStyleId, OpenMode.ForWrite) as InterferenceStyle; // Draw a symbol of a violet X with circle with a specified // drawing size at the points of intersection. oIntStyle.GetDisplayStylePlan(InterferenceDisplayStyleType.Symbol).Visible = true; ObjectId markerStyleId = oIntStyle.MarkerStyle; MarkerStyle oMarkerStyle = ts.GetObject(markerStyleId, OpenMode.ForWrite) as MarkerStyle; oMarkerStyle.MarkerType = MarkerDisplayType.UseCustomMarker; oMarkerStyle.CustomMarkerStyle = CustomMarkerType.CustomMarkerX; oMarkerStyle.CustomMarkerSuperimposeStyle = CustomMarkerSuperimposeType.Circle; oMarkerStyle.MarkerDisplayStylePlan.Color =

Pipe Networks | 107

Color.FromColorIndex(ColorMethod.ByAci, 200); oMarkerStyle.MarkerDisplayStylePlan.Visible = true; oMarkerStyle.SizeType = MarkerSizeType.AbsoluteUnits; oMarkerStyle.MarkerSize = 5.5; // Hide any model display at intersection points. oIntStyle.GetDisplayStyleModel(InterferenceDisplayStyleType.Solid).Visible = false; ts.Commit(); } }

Sample Program PipeSample \Sample\Civil 3D API\DotNet\VB.NET\PipeSample Some of the sample code from this chapter can be found in context in the PipeSample project. This sample creates a simple pipe network, creates pipe, structure and interference styles, creates a parts list, and prints a hierarchy of part types available in a document, separated into pipe and structure domains.

Corridors This chapter covers creating and managing corridor objects using the AutoCAD Civil 3D .NET API.

Root Objects Accessing Corridor-Specific Base Objects The .NET API does not use separate root objects for getting roadway-related objects. Unlike the COM API, you only need to use the CivilApplication and CivilDocument classes to access corridor root objects.

108 | Chapter 1 API Developer's Guide

Ambient Settings Ambient settings allow you to get and set the unit and default property settings of roadway objects. Ambient settings for a corridor are accessed with the SettingsCorridor object returned by CivilDocument.Settings.GetFeatureSettings() method.

Corridor Ambient Settings The corridor ambient settings object allows you to set the default name formats and default styles for corridor-related objects. The name templates allow you to set how new corridors, corridor surfaces, profiles from feature lines, or alignments from feature lines are named. Each format can use elements from the following property fields: Valid property fields for SettingsCorridor.SettingsNameFormat.Corridor

... for SettingsCorridor.SettingsNameFormat.CorridorSurface

...for SettingsCorridor.SettingsNameformat.ProfileFromFeatureLine

... for SettingsCorridor.SettingsNameFormat.AlignmentFromFeatureLine

Corridors | 109

... for SettingsCorridor.SettingsNameFormat.AlignmentFromFeatureLine

This sample sets the corridor name format: // Get the Corridor ambient settings root object CivilDocument doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; SettingsCorridor oCorridorSettings = doc.Settings.GetFeatureSettings() as SettingsCorridor; // Set the template so new corridors are named "Corridor" // followed by a unique number followed by the name of the // corridor's first assembly in parenthesis. oCorridorSettings.NameFormat.Corridor.Value = "Corridor ()"; Default styles are set through the SettingsCorridor.StyleSettings property. The styles for corridor alignments, alignment labels, code sets, surfaces, feature lines, profiles, profile labels, and slope pattern are accessed through a series of string properties. This sample sets the style of alignments in a corridor to the first alignment style in the document’s collection of styles: using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { // Get the name of the first alignment style in the collection. ObjectId alignId = doc.Styles.AlignmentStyles[0]; Alignment oAlignment = ts.GetObject(alignId,

110 | Chapter 1 API Developer's Guide

OpenMode.ForRead) as Alignment; // Assign the name to alignment style property. oCorridorSettings.Styles.Alignment.Value = oAlignment.Name; }

Assembly Ambient Settings The assembly ambient settings object allows you to set the default name formats and default styles for assemblies. The name formats allow you to set how new assemblies, offset assemblies, and assembly groups are named. Each format can use elements from the following property fields: Valid property fields for SettingsAssembly.NameFormat.Assembly

... for SettingsAssembly.NameFormat.Offset

...for SettingsAssembly.NameFormat.Group

Subassembly Ambient Settings The subassembly ambient settings object allows you to set the default name formats and default styles for subassembly objects. The name formats allow you to set how subassemblies created from entities and subassemblies created from macros are named. Each format can use elements from the following property fields: ... for SettingsSubassembly.SettingsNameFormat.CreateFromEntities

Corridors | 111

... for SettingsSubassembly.SettingsNameFormat.CreateFromEntities

... for SettingsSubassembly.SettingsNameFormat.CreateFromMacro

NOTE The name of the default code style set cannot be set with the .NET API.

Corridors Corridor Concepts A corridor represents a path, such as a road, trail, railroad, or airport runway. The geometry of a corridor is defined by a horizontal alignment and a profile. Together, these form the baseline - the centerline of the 3D path of the corridor. Along the length of the baselines are a series of assemblies which define the cross-sectional shape of the alignment. Common points in each assembly are connected to form feature lines. Together the assemblies and feature lines form the 3D shape of a corridor. A corridor also has one or more surfaces which can be compared against an existing ground surface to determine the amount of cut or fill required.

Listing Corridors The collection of all corridors in a document are held in the CivilDocument.CorridorCollection property.

112 | Chapter 1 API Developer's Guide

The following sample displays the name and the largest possible triangle side of every corridor in a document: public static void ListCorridors() { CivilDocument doc = CivilApplication.ActiveDocument; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { foreach (ObjectId objId in doc.CorridorCollection) { Corridor myCorridor = ts.GetObject(objId, OpenMode.ForRead) as Corridor; ed.WriteMessage("Corridor: {0}\nLargest possible triangle side: {1}\n", myCorridor.Name, myCorridor.MaximumTriangleSideLength); } } }

Creating Corridors You cannot add new Corridors to a document using the .NET API.

Baselines A baseline represents the centerline of the path of a corridor. It is based on an alignment (the horizontal component of the path) and a profile (the vertical component of the path). A corridor can contain more than one baseline if the corridor is modeling a complicated shape, such as an intersection. A baseline is made up of one or more baseline regions. Each region has its own assembly (its own cross section), so a corridor can have different shapes at different locations along its length.

Corridors | 113

Listing Baselines in a Corridor The collection of all baselines in a corridor are contained in the Corridor.Baselines property, which is type BaselineCollection. The following sample displays information about the underlying alignment and profile for every baseline in a corridor: foreach (Baseline oBaseline in oCorridor.Baselines) { Alignment oAlign = ts.GetObject(oBaseline.AlignmentId, OpenMode.ForRead) as Alignment; Profile oProfile = ts.GetObject(oBaseline.ProfileId, OpenMode.ForRead) as Profile; ed.WriteMessage(@"Baseline information Alignment : {0} Profile : {1} Start station : {2} End station : {3}", oAlign.Name, oProfile.Name, oBaseline.StartStation, oBaseline.EndStation); }

Adding a Baseline to a Corridor Adding baselines to a Corridor is not supported in the .NET API.

Listing Baseline Regions The collection of all the regions of a baseline are contained in the Baseline.BaselineRegions property. The AutoCAD Civil 3D API does not include methods for creating new baseline regions, or manipulating existing regions. The following sample displays the start and end station for every baseline region in a baseline:

114 | Chapter 1 API Developer's Guide

foreach (BaselineRegion oBaselineRegion in oBaseline.BaselineRegions) { ed.WriteMessage(@"Baseline region information Start station : {0} End station : {1}\n", oBaselineRegion.StartStation, oBaselineRegion.EndStation); }

Accessing and Modifying Baseline Stations Assembly cross sections are placed at regular intervals along a baseline. The list of all stations where assemblies are located along a baseline can be retrieved using the Baseline.SortedStations() method, while all stations along a baseline region can be retrieved using the BaselineRegion.SortedStations() method. double[] stations = oBaselineRegion.SortedStations(); ed.WriteMessage("Baseline Region stations: \n"); foreach (double station in stations){ ed.WriteMessage("\tStation: {0}\n", station); } New stations can be added to baseline regions using the AddStation() method. Existing stations can be deleted using the DeleteStation method. DeleteStation includes an optional tolerance parameter, letting you specify a station within a range. You can list all of the stations added to a baseline region with the BaselineRegion.GetAdditionalStation method. BaselineRegion.ClearAdditionalStations removes all added stations within a baseline region and leaves only the original stations created at regular intervals. // Add an assembly to the middle of the baseline region double newStation = oBaselineRegion.StartStation + ((oBaselineRegion.EndStation oBaselineRegion.StartStation) / 2); oBaselineRegion.AddStation(newStation, "New Station"); ed.WriteMessage("Added New Station: {0}", newStation);

Corridors | 115

// Remove the station located at the beginning of the baseline region: oBaselineRegion.DeleteStation(oBaselineRegion.StartStation);

Listing Offset Baselines Within a baseline region, it is possible to have secondary baselines that are offset from the main baseline. The collection of these offset baselines are contained in the BaselineRegion.OffsetBaselines property. The collection contains two kinds of baselines derived from the BaseBaseline class. One is the hardcoded offset baseline (an instances of the HardcodedOffsetBaseline class) which is a constant distance from the main baseline for the entire length of the offset baseline. The other is the offset baseline (an instance of the OffsetBaseline class), which is a variable distance from the main baseline. NOTE The AutoCAD Civil 3D .NET API does not include methods for creating new offset baselines or hardcoded offset baselines. This code examines each offset baseline within a baseline region: foreach (BaseBaseline ob in oBaselineRegion.OffsetBaselines) { ed.WriteMessage("Offset baseline: \n"); switch (ob.BaselineType) { case CorridorBaselineType.OffsetBaseline: OffsetBaseline offb = (OffsetBaseline)ob; ed.WriteMessage("Offset baseline, station {0} to {1}\n", offb.StartStationOnMainBaseline, offb.EndStationOnMainBaseline); ed.WriteMessage(" is offset by: {0} horizontal and {1} vertical at start\n", offb.GetOffsetElevationFromMainBaselineStation(offb.StartStationOnMainBaseline).X, offb.GetOffsetElevationFromMainBaselineStation(offb.StartStationOnMainBaseline).Y); ed.WriteMessage(" is offset by: {0} horizontal and {1} vertical at end\n", offb.GetOffsetElevationFromMainBaselineStation(offb.EndStationOnMainBaseline).X, offb.GetOffsetElevationFromMainBaselineStation(offb.EndStationOnMainBaseline).Y);

116 | Chapter 1 API Developer's Guide

break; case CorridorBaselineType.HardcodedOffsetBaseline: HardcodedOffsetBaseline hob = (HardcodedOffsetBaseline)ob; ed.WriteMessage("Hardcoded offset baseline {0} \n", hob.Name); ed.WriteMessage(" is offset by: {0} horizontal and {1} vertical\n", hob.OffsetElevationFromMainBaseline.X, hob.OffsetElevationFromMainBaseline.Y); break;

default: break; } }

Assemblies and Subassemblies An assembly is a pattern for the cross section of a corridor at a particular station. An assembly consists of a connected set of subassemblies, each of which are linked to a centerpoint or to other subassemblies. A subassembly consists of a series of shapes, links, and points. When an assembly is used to define the cross-section of a corridor, a series of applied assemblies (an object of type AppliedAssembly) is added to the corridor. Each applied assembly consists of a collection of applied subassemblies, which in turn consist of shapes, links, and points that have been positioned relative to a specific station along the corridor baseline (CalculatedShape, CalculatedLink, and CalculatedPoint respectively). An applied assembly also has direct access to all the calculated shapes, links, and points of its constituent applied subassemblies. NOTE The AutoCAD Civil 3D .NET API does not include methods for creating or modifying assemblies.

Corridors | 117

Listing Applied Assemblies in a Baseline Region The collection of all applied assemblies used in a baseline region are contained in the BaselineRegion.AppliedAssemblies property. The following sample displays information about the construction of an assembly for every assembly in a baseline region: // List the applied assemblies in the baseline region foreach (AppliedAssembly oAppliedAssembly in oBaselineRegion.AppliedAssemblies) { ed.WriteMessage("Applied Assembly, num shapes: {0}, num links: {1}, num points: {2}\n", oAppliedAssembly.Shapes.Count, oAppliedAssembly.Links.Count, oAppliedAssembly.Points.Count);

} An AppliedAssembly object does not contain its baseline station position. Instead, each calculated point contains a property for determining its position with a baseline station, offset, and elevation called CalculatedPoint.StationOffsetElevationToBaseline. Each calculated shape contains a collection of all links that form the shape, and each calculated link contains a collection of all points that define the link. Finally, each shape, link, and point contain an array of all corridor codes that apply to that element. This sample retrieves all calculated points in an applied assembly and prints their locations: foreach (CalculatedPoint oPoint in oAppliedAssembly.Points) { ed.WriteMessage("Point position: Station: {0}, Offset: {1}, Elevation: {2}\n", oPoint.StationOffsetElevationToBaseline.X, oPoint.StationOffsetElevationToBaseline.Y, oPoint.StationOffsetElevationToBaseline.Z); }

118 | Chapter 1 API Developer's Guide

Getting Applied Subassembly Information An applied subassembly consists of a series of calculated shapes, links, and points, represented by objects of type CalculatedShape, CalculatedLink, and CalculatedPoint respectivly. foreach (AppliedSubassembly oSubassembly in oASC) { ed.WriteMessage("Applied subassembly: Station to baseline: {0}, Offset to baseline: {1}, Elevation to baseline: {2}\n", oSubassembly.OriginStationOffsetElevationToBaseline.X, oSubassembly.OriginStationOffsetElevationToBaseline.Y, oSubassembly.OriginStationOffsetElevationToBaseline.Z); } Applied subassemblies also contain an ObjectId reference to the archetype subassembly (of type Subassembly) in the subassembly database. // Get information about the subassembly template: ObjectId oID = oAppliedSubassembly.SubassemblyId; Subassembly oSubassembly = ts.GetObject(oID, OpenMode.ForRead) as Subassembly; ed.WriteMessage("Subassembly name: {0}\n", oSubassembly.Name);

Feature Lines Feature lines are formed by connecting related points in each assembly along the length of a corridor baseline. These lines represent some aspect of the roadway, such as a sidewalk edge or one side of a corridor surface. Points become related by sharing a common code, a string property usually describing the corridor feature. Each baseline has two sets of feature lines, one for lines that are positioned along the main baseline and one for lines that are positioned along any of the offset baselines.

Corridors | 119

NOTE Creating feature lines from polylines is not supported in the .NET API. However, you can use the COM API IAeccLandFeatureLine:: AddFromPolyline() method.

Listing Feature Lines Along a Baseline The set of all feature lines along a main baseline are held in the Baseline.MainBaselineFeatureLines property, an object of type BaselineFeatureLines. This object contains information about all the feature lines, such as a list of all codes used. The BaselineFeatureLines.FeatureLinesCol property is a collection of feature line collections. Each feature line (an object of type FeatureLine) contains the code string used to create the feature line and a collection of all feature line points. This sample lists all the feature line collections and feature lines along the main baseline. It also lists the code and every point location for each feature line. // Get all the feature lines: foreach (FeatureLineCollection oFeatureLineCollection in oBaseline.MainBaselineFeatureLines.FeatureLineCollectionMap) { ed.WriteMessage("Feature Line Collection\n# Lines in collection: {0}\n", oFeatureLineCollection.Count); foreach (FeatureLine oFeatureLine in oFeatureLineCollection) { ed.WriteMessage("Feature line code: {0}\n", oFeatureLine.CodeName); // print out all point locations on the feature line foreach (FeatureLinePoint oFeatureLinePoint in oFeatureLine.FeatureLinePoints) { ed.WriteMessage("Point: {0},{1},{2}\n", oFeatureLinePoint.XYZ.X, oFeatureLinePoint.XYZ.Y, oFeatureLinePoint.XYZ.Z );

120 | Chapter 1 API Developer's Guide

} } }

Listing Feature Lines Along Offset Baselines As there can be many offset baselines in a single main baseline, the list of all feature lines along all offset baselines contains an extra layer. The Baseline.OffsetBaselineFeatureLinesCol property contains a collection of BaselineFeatureLines objects. These BaselineFeatureLines objects not only contain the feature lines just as for the main baseline, but also contain properties identifying which offset baseline each group of feature lines belong to. This sample shows how to modify the previous sample for feature lines along offset baselines: // Get all the offset feature lines: foreach (BaselineFeatureLines oBaselineFeaturelines in oBaseline.OffsetBaselineFeatureLinesCol) { foreach (FeatureLineCollection oFeatureLineCollection in oBaselineFeaturelines.FeatureLineCollectionMap) { ed.WriteMessage("Feature Line Collection\n# Lines in collection: {0}\n", oFeatureLineCollection.Count); foreach (FeatureLine oFeatureLine in oFeatureLineCollection) { ed.WriteMessage("Feature line code: {0}\n", oFeatureLine.CodeName); // print out all point locations on the feature line foreach (FeatureLinePoint oFeatureLinePoint in oFeatureLine.FeatureLinePoints) { ed.WriteMessage("Point: {0},{1},{2}\n", oFeatureLinePoint.XYZ.X, oFeatureLinePoint.XYZ.Y, oFeatureLinePoint.XYZ.Z); }

Corridors | 121

} } } Each offset baseline and hardcoded offset baseline also has direct access to the feature lines related to itself. The BaselineFeatureLines collection is accessed through the RelatedOffsetBaselineFeatureLines property in both types of offset baselines.

Corridor Surfaces Corridor surfaces can represent the base upon which the corridor is constructed, the top of the finished roadway, or other aspects of the corridor. Such surfaces are represented by the Surface class and by the unrelated CorridorSurface class. CorridorSurface objects contain corridor-specific information about the surfaces, such as which feature line, point, and link codes were used to create it.

Listing Corridor Surfaces The collection of all corridor surfaces for each corridor is held in the the Corridor.CorridorSurfaces property. Each corridor surface contains the boundary of the surface and a list of all point, link, and feature line codes used in the construction of the surface. Corridor surfaces also contain read-only references to the surface style ID and section style ID used in drawing the surface. NOTE The AutoCAD Civil 3D .NET API does not include methods for creating new corridor surfaces or modifying existing corridor surfaces. This sample lists all the corridor surfaces within a corridor and specifies the point codes that make up each surface: // List surfaces foreach (CorridorSurface oCorridorSurface in oCorridor.CorridorSurfaces) {

122 | Chapter 1 API Developer's Guide

ed.WriteMessage("Corridor surface: {0}\n", oCorridorSurface.Name); // Get the point codes for the surface. String[] oPointCodes = oCorridorSurface.PointCodes(); ed.WriteMessage("Surface point codes:\n"); foreach (String s in oPointCodes) { ed.WriteMessage("{0}\n", s); } }

Listing Surface Boundaries Two different objects are used to define the limits of a corridor surface: boundaries and masks. A boundary is a polygon representing the outer edge of a surface or the inside edge of a hole in a surface. A mask is a polygon representing the part of the surface that can be displayed. The collection of all the boundaries of a surface are stored in the CorridorSurface.Boundaries property and the collection of all masks are stored in the CorridorSurface.Masks property. Boundaries (of type CorridorSurfaceBoundary) and masks (of type CorridorSurfaceMask) are both derived from the same base class (CorridorSurfaceBaseMask) and both have similar methods and properties. The array of points making up the border polygon is retrieved by calling the PolygonPoints() method. If the border was originally defined by selecting segments of feature lines, the collection of all such feature line components are contained in the FeatureLineComponents property. NOTE The AutoCAD Civil 3D .NET API does not include methods for creating or modifying corridor boundaries or masks. This sample loops through all the boundaries of a corridor surface and displays information about each: // List boundaries foreach (CorridorSurfaceBoundary oCorridorSurfaceBoundary

Corridors | 123

in oCorridorSurface.Boundaries) { if (oCorridorSurfaceBoundary.BoundaryType == CorridorSurfaceBoundaryType.InsideBoundary) ed.WriteMessage("Inner Boundary: "); else ed.WriteMessage("Outer Boundary: "); ed.WriteMessage(oCorridorSurfaceBoundary.Name); // Get the points of the boundary polygon Point3d[] oPoints = oCorridorSurfaceBoundary.PolygonPoints(); ed.WriteMessage("\nNumber of points: {0}\n", oPoints.Length); // Print the location of the first point. Usually corridors // have a large number of boundary points, so we will not // bother printing all of them. ed.WriteMessage("Point 1: {0},{1},{2}\n", oPoints[0][0], oPoints[0][1], oPoints[0][2]); // Display information about each feature // line component in this surface boundary. ed.WriteMessage("Feature line components \n Count: {0}\n", oCorridorSurfaceBoundary.FeatureLineComponents.Count); foreach (FeatureLineComponent oFeatureLineComponent in oCorridorSurfaceBoundary.FeatureLineComponents) { ed.WriteMessage("Code: {0}, Start station: {1}, End station: {2}\n", oFeatureLineComponent.FeatureLine.CodeName, oFeatureLineComponent.StartStation, oFeatureLineComponent.EndStation); } }

124 | Chapter 1 API Developer's Guide

Computing Cut and Fill The .NET API doesn notexpose surface functionality, so it isn’t possible to calculate cut and fill volumes by comparing surfaces. However, you can perform this task with the COM API. See for more information.

Styles These style objects control the visual appearance of applied assemblies.

Assembly Style The collection of all assembly style objects are found in the CivilDocument.Styles.AssemblyStyles property. The assembly style object contains properties for adjusting the marker types for the assembly attachment points, and each of the standard MarkerType properties. While you can create new styles and edit existing styles, you cannot assign a style to an existing assembly using the AutoCAD Civil 3D .NET API. using (Transaction ts = Application.DocumentManager.MdiActiveDocument. Database.TransactionManager.StartTransaction()) { ObjectId objId = doc.Styles.AssemblyStyles.Add("Style1"); AssemblyStyle oAssemblyStyle = ts.GetObject(objId, OpenMode.ForWrite) as AssemblyStyle; objId = oAssemblyStyle.MarkerStyleAtMainBaselineId; MarkerStyle oMarker = ts.GetObject(objId, OpenMode.ForWrite) as MarkerStyle; oMarker.CustomMarkerStyle = CustomMarkerType.CustomMarkerX; oMarker.MarkerDisplayStylePlan.Color = Color.FromColorIndex(ColorMethod.ByAci, 10); oMarker.MarkerDisplayStylePlan.Visible = true; ts.Commit(); }

Corridors | 125

Link Style The collection of all link style objects are found in the CivilDocument.Styles.LinkStyles property. This style object contains properties for adjusting the visual display of assembly and subassembly links. NOTE Link style objects are not used directly with link objects, but are instead used with roadway style sets.

// Add a new link style to the document: objId = doc.Styles.LinkStyles.Add("Style2"); LinkStyle oLinkStyle = ts.GetObject(objId, OpenMode.ForWrite) as LinkStyle; oLinkStyle.LinkDisplayStylePlan.Color = Color.FromColorIndex(ColorMethod.ByAci, 80); oLinkStyle.LinkDisplayStylePlan.Visible = true; ts.Commit();

Shape Style The collection of all shape style objects are found in the CivilDocument.ShapeStyles property. This style object contains properties for adjusting the visual display of assembly and subassembly shapes, including the outline and the inside area. NOTE Shape style objects are not used directly with shape objects, but are instead used with roadway style sets.

// Create a new shape style and change it so that it has // an orange border and a yellow hatch fill. objId = doc.Styles.ShapeStyles.Add("Style3"); ShapeStyle oShapeStyle = ts.GetObject(objId, OpenMode.ForWrite) as ShapeStyle; // 50 = yellow oShapeStyle.AreaFillDisplayStylePlan.Color =

126 | Chapter 1 API Developer's Guide

Color.FromColorIndex(ColorMethod.ByAci, 50); oShapeStyle.AreaFillDisplayStylePlan.Visible = true; oShapeStyle.AreaFillHatchDisplayStylePlan.HatchType = HatchType.PreDefined; oShapeStyle.AreaFillHatchDisplayStylePlan.Pattern = "LINE"; // 30 = orange oShapeStyle.BorderDisplayStylePlan.Color = Color.FromColorIndex(ColorMethod.ByAci, 30); oShapeStyle.BorderDisplayStylePlan.Visible = true; ts.Commit();

Roadway Style Sets The visual display of applied assemblies is defined by roadway style sets, which are a set of shape styles and link styles assigned to shapes and links that use specified code strings. The collection of all style sets are found in the CivilDocument.Styles.CodeSetStyles property. A style set is itself a collection of CodeSetStyleItem objects. Each style set item has a CodeSetStyleItem.CodeStyle property that can reference either an existing shape style object or link shape object. New style set items are added to a style set though the CodeSetStyleCollection.Add() method which takes parameters describing the kind of style object, the code string, and the style object itself. NOTE You cannot set the CodeSetStyle to be the currently used style with the .NET API. However, you can get the ObjectId for the currently used style by calling CodeSetStyle.GetCurrentStyleSetId().

// Create a new style set using our previously created styles. objId = doc.Styles.ShapeStyles.Add("Style Set 1"); CodeSetStyle oCodeSetStyle = ts.GetObject(objId, OpenMode.ForWrite) as CodeSetStyle; oCodeSetStyle.Add("TOP", doc.Styles.LinkStyles["Style2"]); oCodeSetStyle.Add("BASE", doc.Styles.ShapeStyles["Style3"]); ts.Commit();

Corridors | 127

Points This chapter covers creating and using Coordinated Geometry (COGO) Points, exposed by the CogoPoint class, with the .NET API. It describes accessing the collection of all points in the CivilDocument, adding and removing points, assigning User Defined Properties (UDPs) to points, and working with PointGroup objects to organize points. It also describes creating and applying point styles and label styles to points.

Using the Points Collection All points in a document are held in a CogoPointCollection object accessed through the CivilDocument.CogoPoints property. In addition to the common collection properties and methods, this collection also exposes methods for working with large numbers of points at once. For example, points can be added to the collection either individually, or from a Point3dCollection. The following sample adds a collection of randomly generated points to the document’s point collection, and then accesses each point in the collection directly to calculate the average elevation: [CommandMethod("C3DSAMPLES", "AverageCogoElevation", CommandFlags.Modal)] public void AverageCogoElevation() { using (Transaction tr = startTransaction()) { // _civildoc is the active CivilDocument instance. CogoPointCollection cogoPoints = _civildoc.CogoPoints; Point3d[] points = { new Point3d(4927, 3887, 150), new Point3d(5101, 3660, 250), new Point3d(5144, 3743, 350) }; Point3dCollection locations = new Point3dCollection(points); cogoPoints.Add(locations); // Compute the average elevation of all the points

128 | Chapter 1 API Developer's Guide

in a document. double avgElevation = 0; foreach (ObjectId pointId in cogoPoints) { CogoPoint cogoPoint = pointId.GetObject(OpenMode.ForRead) as CogoPoint; avgElevation += cogoPoint.Elevation; } avgElevation /= cogoPoints.Count; _editor.WriteMessage("Average elevation: {0} \nNumber of points: {1}", avgElevation, cogoPoints.Count); tr.Commit(); } }

Using Points Coordinated Geometry Points (COGO points) are more complex than AutoCAD point nodes, which have only coordinate data. A CogoPoint object, in addition to a location, also has properties such as a unique ID number, name, raw (field) description, and full (expanded) description. The point number is unique, and is automatically assigned when the point is created. You can change the point number either by setting the PointNumber property directly, or by using the Renumber() method. Setting the property directly will throw an exception if another point exists with the specified value, while the Renumber() method will use the settings for resolving point numbering conflicts to choose another point number. You can also set the PointNumber property for multiple points using the CogoPointCollection.SetPointNumber() method. The full description property is read-only once a point is created. The CogoPoint object’s Location property is read-only. However, you can read and change the local position using the Easting, Northing and Elevation properties. The point’s location can also be specified by using the Grideasting and GridNorthing properties or the Latitude and Longitude properties, depending on the coordinate and transformation settings of the drawing. A CogoPoint object is either a drawing point or a project point. Project points have the isProjectPoint property set to true, and have additional project information contained by the IsCheckedOut and ProjectVersion properties.

Points | 129

Attempting to get or set these properties for points that have isProjectPoint == false raises an exception, so it is a good idea to check that property first. CogoPoint objects also have several read-only override properties that contain values that have been overridden by PointGroup settings. These include ElevationOverride, FullDescriptionOverride, LabelStyleIdOverride, RawDescriptionOverride, and StyleIdOverride.

This sample adds a new point to the document’s collection of points and sets some of its properties. [CommandMethod("C3DSAMPLES", "CreatePoint", CommandFlags.Modal)] public void CreatePoint() { using (Transaction tr = startTransaction()) { // _civildoc is the active CivilDocument instance. Point3d location = new Point3d(4958, 4079, 200); CogoPointCollection cogoPoints = _civildoc.CogoPoints; ObjectId pointId = cogoPoints.Add(location); CogoPoint cogoPoint = pointId.GetObject(OpenMode.ForWrite) as CogoPoint; cogoPoint.PointName = "point1"; cogoPoint.RawDescription = "Point description"; tr.Commit(); } }

Bulk Editing Points The CogoPointCollection class provides several methods for changing the properties of multiple points with a single action. Most of the Set() methods have three versions: 1 Set the property for a single CogoPoint (identified by ObjectId) in the collection . 2 Set the property for all points in a list to a single value .

130 | Chapter 1 API Developer's Guide

3 Set the property for all points in a list to a value in a corresponding list of values. The list of points can be a sub-collection of points, or you can pass the CivilDocument.CogoPoints collection to process all points in the document. Here is an example of setting Elevation, RawDescription and DescriptionFormat properties for all points in the drawing using the bulk editing methods. // Change a couple of properties using bulk editing methods cogoPoints.SetElevationByOffset(cogoPoints, 3.00); cogoPoints.SetRawDescription(cogoPoints, "NEW_DESC"); // Sets Full Description = Raw Description: cogoPoints.SetDescriptionFormat(cogoPoints, "$*");

Point User-Defined Properties User-defined properties (UDPs) allow users to attach additional data to points. (UDPs can also be assigned to Parcels). UDPs are organized into groups called UDP Classifications, which in turn are assigned to PointGroups. UDPs can also be “unclassified”. When a UDPClassification is associated with a PointGroup object (using its UseCustomClassification() method), all the UDP definitions in the UDPClassification are assigned to each point in the PointGroup. UDP values are unique for each point, and can be changed individually. Each point gets a UDP’s default value (if UseDefault is true for the UDP) upon assignment. Each instance of a UDP is a class derived from the UDP base class, with additional properties depending on the data type. UDPDouble and UDPInteger types have upper and lower bound properties, to express a range. UDPEnumeration types have a GetEnumerationValues() method to get an array of all defined values. To create a UDP, you must first create and populate an AttributeTypeInfo object (there is one for each UDP type), and pass it to the CreateUDP() method for an existing UDPClassification. UDPs have a GUID as well as a name, and the name+GUID combination is guaranteed to be a unique identifier. The CreateUDP() method takes an optional GUID parameter, so that you can create UDPs with a specific name+GUID combination. This allows you to create identical UDPs in multiple drawings. For more information about user-defined properties and classifications, see User-Defined Property Classifications in the AutoCAD Civil 3D User Guide.

Points | 131

This sample creates a new user-defined property classification for points called “Example”, and then adds a new user-defined property with upper and lower bounds and a default value: [CommandMethod("C3DSAMPLES", "UDPExample", CommandFlags.Modal)] public void UDPExample() { using (Transaction tr = startTransaction()) { // _civildoc is the active CivilDocument instance. UDPClassification udpClassification = _civildoc.PointUDPClassifications.Add("Example"); AttributeTypeInfoInt attributeTypeInfoInt = new AttributeTypeInfoInt("Int UDP"); attributeTypeInfoInt.DefaultValue = 15; attributeTypeInfoInt.UpperBoundValue = 20; attributeTypeInfoInt.LowerBoundValue = 10; UDP udp = udpClassification.CreateUDP(attributeTypeInfoInt); // assign a point group ObjectId pointGroupId = _civildoc.PointGroups.AllPointGroupsId; PointGroup pointGroup = pointGroupId.GetObject(OpenMode.ForWrite) as PointGroup; pointGroup.UseCustomClassification("Example");

tr.Commit(); } } This example illustrates accessing the collection of all Point UDPClassifications in a document, and then reading each UDP in each UDPClassification. [CommandMethod("C3DSAMPLES", "ListUDPs", CommandFlags.Modal)] public void ListUDPs() { using (Transaction tr = startTransaction()) { // _civildoc is the active CivilDocument instance.

132 | Chapter 1 API Developer's Guide

foreach (UDPClassification udpClassification in _civildoc.PointUDPClassifications) { _editor.WriteMessage("\n\nUDP Classification: {0}\n", udpClassification.Name); foreach (UDP udp in udpClassification.UDPs) { _editor.WriteMessage(" * UDP name: {0} guid: {1}, description: {2}, default value: {3}, use default? {4}\n", udp.Name, udp.Guid, udp.Description, udp.DefaultValue, udp.UseDefaultValue); _editor.WriteMessage("\tUDP type: {0}\n", udp.GetType().ToString()); var udpType = udp.GetType().Name; switch (udpType) { // Booleans and String types do not define extra properties case "UDPBoolean": case "UDPString": break; case "UDPInteger": UDPInteger udpInteger = (UDPInteger)udp; _editor.WriteMessage("\tUpper value: {0}, inclusive? {1}, Lower bound value: {2}, inclusive? {3}\n", udpInteger.UpperBoundValue, udpInteger.UpperBoundInclusive, udpInteger.LowerBoundValue, udpInteger.LowerBoundInclusive); break; case "UDPDouble": UDPDouble udpDouble = (UDPDouble)udp; _editor.WriteMessage("\tUpper value: {0}, inclusive? {1}, Lower bound value: {2}, inclusive? {3}\n", udpDouble.UpperBoundValue,

Points | 133

udpDouble.UpperBoundInclusive, udpDouble.LowerBoundValue, udpDouble.LowerBoundInclusive); break; case "UDPEnumeration": UDPEnumeration udpEnumeration = (UDPEnumeration)udp; _editor.WriteMessage("\tEnumeration values: {0}\n", udpEnumeration.GetEnumValues()); break; } } } tr.Commit(); } }

Point Groups A point group is a collection that defines a subset of the points in a document. Points may be grouped for a number of reasons, such as points that share common characteristics or are used to perform a common task (for example, define a surface). A collection of all point groups in a drawing is held in a document‘s CivilDocument.PointGroups property. Add a new point group by using the CivilDocument.PointGroups.Add() method and specifying a unique identifying string name. The ObjectId for a new, empty point group is returned. // _civildoc is the active CivilDocument instance. ObjectId pointGroupId = _civildoc.PointGroups.Add("Example Point Group"); PointGroup pointGroup = pointGroupId.GetObject(OpenMode.ForRead) as PointGroup;

Using Point Groups Once a point group has been created, you can perform actions upon all the points in that group in a single operation. You can override point elevations, descriptions, styles, and label styles.

134 | Chapter 1 API Developer's Guide

// Check to see if a particular point exists in the PointGroup ObjectId pointId = promptForEntity("Select a point", typeof(CogoPoint)); CogoPoint cogoPoint = pointId.GetObject(OpenMode.ForRead) as CogoPoint; if (pointGroup.ContainsPoint(cogoPoint.PointNumber){ _editor.WriteMessage("Point {0} is not part of PointGroup {1}", cogoPoint.PointName, pointGroup.Name); } // Set the elevation of all points in the PointGroup to 100 pointGroup.ElevationOverride.FixedElevation=100; pointGroup.ElevationOverride.ActiveOverrideType = PointGroupOverrideType.FixedValue; pointGroup.IsElevationOverriden = true; Point groups can also be used to define or modify a TIN surface. The TinSurface.PointGroupsDefinition property is a collection of point groups. When a point group is added to the collection, every point in the point group is added to the TIN surface.

Adding Points to Point Groups with Queries Points can be selected and added to a PointGroup using either a standard (StandardPointGroupQuery object) or custom (CustomPointGroupQuery object) query, which both inherit from the PointGroupQuery base class. StandardPointGroupQuery encapsulates the “basic method” of creating a query

using the GUI, which is described in the “Creating a Point Group Using the Basic Method” section of the Civil 3D User Guide. This method uses the tabs on the Create Point Group dialog to match points by raw descriptions, include or exclude specific points or ranges of points, and to include points from other point groups. To accomplish the same effect using the API, you first create an instance of a StandardPointGroupQuery object, and then set the various Include* and Exclude* properties to create the query. StandardPointGroupQuery has these properties for building a query: StandardPointGroupQuery Properties

Points | 135

Properties

Possible Values

IncludeElevations ExcludeElevations

Any combination of, separated by commas: ■

A single number



An elevation range specified by a lower and upper bound separated by a hyphen (for example, 1-100)



A > or < followed by a number. This specifies all elevations greater-than or less-than the number.

Example: “200” – specifies all points whose elevations meet one of the following criteria: less than -100, equal to or between 1 and 100, equal to 110.01, or greater than 200. IncludeFullDescriptions ExcludeFullDescriptions IncludeRawDescriptions ExcludeRawDescriptions

One or more descriptions, separated by commas. The * is a wildcard matching any string. Example: “IP*” matches all descriptions that start with IP

IncludeNames ExcludeNames

One or more point names separated by commas. The * is a wildcard matching any string.

IncludeNumbers ExcludeNumbers

Any combination of, separated by commas: ■

An individual point number



A Point number range specified by a lower and upper bound separated by a hyphen (for example, 100-105).

All of the Include* properties are ORed together, and all the Exclude* properties are ORed together to create the final query string. The example below illustrates how the query string is built from properties: // _civildoc is the active CivilDocument instance. ObjectId pointGroupId = _civildoc.PointGroups.Add("Example Point Group1");

136 | Chapter 1 API Developer's Guide

PointGroup pointGroup = pointGroupId.GetObject(OpenMode.ForWrite) as PointGroup; StandardPointGroupQuery standardQuery = new StandardPointGroupQuery(); standardQuery.IncludeElevations = "100-200"; standardQuery.IncludeFullDescriptions = "FLO*"; standardQuery.IncludeNumbers = ">2200"; standardQuery.ExcludeElevations = "150-155"; standardQuery.ExcludeNames = "BRKL"; standardQuery.UseCaseSensitiveMatch = true; pointGroup.SetQuery(standardQuery); pointGroup.Update(); _editor.WriteMessage("Number of points selected: {0}\n Query string: {1}\n\n", pointGroup.PointsCount, standardQuery.QueryString); // output: // Query string: (FullDescription='FLO*' OR PointElevation=100-200 OR // PointNumber>2200) AND '' NOT (Name='BRKL' OR PointElevation=150-155) The CustomPointGroupQuery lets you specify a query string directly, and requires a more advanced knowledge of query operators. This method of query creation allows you to create nested queries that cannot be specified using the StandardPointGroupQuery object. Custom queries are made up of expressions, and expressions have three parts: 1 A property 2 A comparison operator: > < >=