This week is the turn to talk about description keys. As most of you know, description keys are a powerful concept in Civil 3D that allow users to control certain aspects of the representation of COGO points. To understand description keys, let’s first talk about some of the properties of the COGO point object.
COGO points contain a “raw description”, which embeds information about a specific point. You can specify a “description format” that allows you to format the “raw description” into a more readable string called “full description. All these properties can be specified in a per point basis, but if you think about how some drawings may contain thousands or millions of points, you can see that managing these properties per point is almost impossible.
To simplify the task, Civil 3D has the concept of description keys. The description keys match their code to the point description and override properties in the COGO point with those defined in the description key. Even more power is provided because description keys are part of a description key set, which allows you to group them and create some kind of standard. But I am probably telling you things that you already know as a user of Civil 3D, and what I really want to talk about is how to use these description keys through the API.
Creating Description Keys
First, I want to show you how to create some description keys, and then we will add some points to match them. I will start with a simple command that creates a description key set “CivilDev” and adds a couple of keys. The command looks like this:
C#
[CommandMethod("CDS_CreateCivilDevDescriptionKeys")]
public void CDS_CreateCivilDevDescriptionKeys()
{
using (Transaction tr = startTransaction())
{
ObjectId keySetId = createOrRetrieveDescriptionKeySet("CivilDev");
PointDescriptionKeySet keySet =
keySetId.GetObject(OpenMode.ForWrite)
as PointDescriptionKeySet;
ObjectId treesKeyId = createDescriptionKey(keySet, "TREE");
PointDescriptionKey key =
treesKeyId.GetObject(OpenMode.ForWrite)
as PointDescriptionKey;
customizeDescriptionKey(key, "Tree", "Description Only",
"$0 is a $1");
ObjectId wellsKeyId = createDescriptionKey(keySet, "WELL");
key = wellsKeyId.GetObject(OpenMode.ForWrite)
as PointDescriptionKey;
customizeDescriptionKey(key, "Well", "Point Number Only",
"The $0 is $1");
tr.Commit();
}
}
VB.NET
<CommandMethod("CDS_CreateCivilDevDescriptionKeys")> _
Public Sub CDS_CreateCivilDevDescriptionKeys()
Using tr As Transaction = startTransaction()
Dim keySetId As ObjectId =
createOrRetrieveDescriptionKeySet("CivilDev")
Dim keySet As PointDescriptionKeySet =
TryCast(keySetId.GetObject(OpenMode.ForWrite),
PointDescriptionKeySet)
Dim treesKeyId As ObjectId = createDescriptionKey(keySet, "TREE")
Dim key As PointDescriptionKey =
TryCast(treesKeyId.GetObject(OpenMode.ForWrite),PointDescriptionKey)
customizeDescriptionKey(key, "Tree", "Description Only", "$0 is a $1")
Dim wellsKeyId As ObjectId = createDescriptionKey(keySet, "WELL")
key =
TryCast(wellsKeyId.GetObject(OpenMode.ForWrite),PointDescriptionKey)
customizeDescriptionKey(key,
"Well", "Point Number Only", "The $0 is $1")
tr.Commit()
End Using
End Sub
The command first creates a description key set named “CivilDev”. Then, it uses it to add a couple of keys to represents trees (TREE) and wells (WELL). We customize the keys by passing the point style, label style and description format we want to use.
The process of creating a description key set is very simple. The Civil 3D API contains a ‘PointDescriptionKeySetCollection’ class, which exposes a static ‘GetPointDescriptionKeySets()’ method that takes the ‘Database’ object from where to retrieve the collection. Once we have the collection, we just need to create a new one by calling the ‘Add()’ method and passing it a name. Needless to say that the name should not exist, so you want to check for its existence before creating it. In this case, we return the existing set if it already exists.
C#
private ObjectId createOrRetrieveDescriptionKeySet(string name)
{
PointDescriptionKeySetCollection keySets =
PointDescriptionKeySetCollection
.GetPointDescriptionKeySets(_database);
if (keySets.Contains(name))
{
return keySets[name];
}
return keySets.Add(name);
}
VB.NET
Private Function createOrRetrieveDescriptionKeySet(name As String) _
As ObjectId
Dim keySets As PointDescriptionKeySetCollection =
PointDescriptionKeySetCollection.
GetPointDescriptionKeySets(_database)
If keySets.Contains(name) Then
Return keySets(name)
End If
Return keySets.Add(name)
End Function
Once we have our description key set, we can use it to add some keys. Adding a key is a simple as calling the ‘Add()’ method in the ‘PointDescriptionKeySet’ object. This method will also fail if the specified name/code already exists, so you want to check for it first.
C#
private ObjectId createDescriptionKey(
PointDescriptionKeySet keySet, string code)
{
if (keySet.Contains(code))
{
return keySet[code];
}
return keySet.Add(code);
}
VB.NET
Private Function createDescriptionKey(keySet As PointDescriptionKeySet,
code As String) As ObjectId
If keySet.Contains(code) Then
Return keySet(code)
End If
Return keySet.Add(code)
End Function
We created the key; now, we are going to customize other parameters. The ‘customizeDescriptionKey()’ method takes the ‘PointDescriptionKey’ object to customize, the name of the point style, the name of the label style, and the format we would like to use for the “full description”.
C#
private void customizeDescriptionKey(PointDescriptionKey key,
string pointStyle, string labelStyle, string format)
{
key.StyleId = getPointStyle(pointStyle);
key.LabelStyleId = getPointLabelStyle(labelStyle);
key.Format = format;
key.ApplyStyleId = true;
key.ApplyLabelStyleId = true;
}
VB.NET
Private Sub customizeDescriptionKey(key As PointDescriptionKey,
pointStyle As String,
labelStyle As String,
format As String)
key.StyleId = getPointStyle(pointStyle)
key.LabelStyleId = getPointLabelStyle(labelStyle)
key.Format = format
key.ApplyStyleId = True
key.ApplyLabelStyleId = True
End Sub
You want to insure that you turn on the ‘ApplyStyleId’ and ‘ApplyLabelStyleId’ in the ‘PointDescriptionKey’ object; otherwise, the style and label will be set, but the representation will be read from the actual point object.
Quick Trick to Access Styles by Name
In this post, I am using a little trick possible because of the Extension Methods feature supported in .NET. Extension Methods were introduced in .NET 3.5 and are a very helpful feature for .NET languages that allow to extend the interface of an object when you do not have access to the implementation. In this case, I am adding an extension method ‘GetStyle()’ to the implementation of ‘IEnumerable<ObjectId>’, which allows me to write the following code:
C#
private ObjectId getPointStyle(string name)
{
return _civildoc.Styles.PointStyles.GetStyle(name);
}
private ObjectId getPointLabelStyle(string name)
{
return _civildoc.Styles.LabelStyles
.PointLabelStyles.LabelStyles.GetStyle(name);
}
VB.NET
Private Function getPointStyle(name As String) As ObjectId
Return _civildoc.Styles.PointStyles.GetStyle(name)
End Function
Private Function getPointLabelStyle(name As String) As ObjectId
Return _civildoc.Styles.LabelStyles.
PointLabelStyles.LabelStyles.GetStyle(name)
End Function
This is possible because of the implementation of ‘ObjectIdEnumerableExtensions’, which is shown in the following snippet.
C#
internal static class ObjectIdEnumerableExtensions
{
public static ObjectId GetStyle(this IEnumerable<ObjectId> ids,
string name)
{
foreach (ObjectId id in ids)
{
StyleBase style = id.GetObject(OpenMode.ForRead)
as StyleBase;
if (style.Name == name)
{
return id;
}
}
return ObjectId.Null;
}
}
VB.NET
Module ObjectIdEnumerableExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function GetStyle(ids As IEnumerable(Of ObjectId),
name As String) As ObjectId
For Each id As ObjectId In ids
Dim style As StyleBase =
TryCast(id.GetObject(OpenMode.ForRead), StyleBase)
If style.Name = name Then
Return id
End If
Next
Return ObjectId.Null
End Function
End Module
Testing Our Command
The full implementation defines a second command “CDS_CreateCivilDevPoints’, which is very similar to what we already saw in COGO Point Basics. As always, you can download the full code from the Civilized Development repository at Bitbucket to refresh your memory.
Let’s run the ‘CDS_CreateCivilDevDescriptionKeys’ command. Once the command completes, you will see that a new description key set is created and that it contains two description keys.
Now, execute the command ‘CDS_CreateCivilDevPoints’. In the command, we create a total of 20 points; 10 of them should match the ‘TREE’ description key, and the other 10 the ‘WELL’ key. In ‘Prospector’, select the Points node. You will see the following result.
Civil 3D, by default, creates a description key set “Civil 3D”, which exposes a key that matches “TR*”. The points added with a description of “TREE MAPLE” match the default description key in Civil 3D, and the one we added is ignored. Civil 3D exposes functionality to modify the search order of key sets when matching descriptions keys, and so does the API. Let’s modify our command to force our new description key to be searched first. For that, we implement a ‘createOrRetrieveDescriptionKeySetAndForce()’ method that manipulates the order.
C#
private ObjectId createOrRetrieveDescriptionKeySetAndForce(string name)
{
PointDescriptionKeySetCollection keySets =
PointDescriptionKeySetCollection
.GetPointDescriptionKeySets(_database);
ObjectIdCollection searchOrder = keySets.SearchOrder;
if (keySets.Contains(name))
{
return keySets[name];
}
ObjectId newKeySetId = keySets.Add(name);
searchOrder.Insert(0, newKeySetId);
keySets.SearchOrder = searchOrder;
return newKeySetId;
}
VB.NET
Private Function createOrRetrieveDescriptionKeySetAndForce(
name As String) As ObjectId
Dim keySets As PointDescriptionKeySetCollection =
PointDescriptionKeySetCollection.
GetPointDescriptionKeySets(_database)
Dim searchOrder As ObjectIdCollection = keySets.SearchOrder
If keySets.Contains(name) Then
Return keySets(name)
End If
Dim newKeySetId As ObjectId = keySets.Add(name)
searchOrder.Insert(0, newKeySetId)
keySets.SearchOrder = searchOrder
Return newKeySetId
End Function
The ‘PointDescriptionKeySetCollection’ class exposes a property ‘SearchOrder’ that returns the object ids of the key set in the order they will be searched. By inserting the new key set at the beginning of the collection, we can modify the search order. Notice that the ‘ObjectIdCollection’ returned by the ‘SearchOrder’ property is what we call a disconnected collection, which means that a new instance is created when the property is accessed. What this means to you is that just calling ‘keySets.SearchOrder.Insert(0, newKeySetId);’ will not work and the search order will not be modified. You must first get the collection, then insert the object id at the beginning, and then set the property again as in the example above.
Some Final Thoughts
When working with description keys, you may write a couple of different types of applications. Some applications will want to read and work with any description keys and description key sets the drawing contains. In those cases, you want to write very generic code and allow your users to use the functionality of Civil 3D to create and manipulate behavior related to description keys.
Other applications will work with description keys under their own standard. In those cases, you want to make sure you don’t create conflicts with other possible description keys and description key sets. Prefix your key set names so that they are unique to your application. Do the same with your description key codes. Depending on your requirements this may not always be possible, but try to follow those guidelines everywhere you can.

Subscribe
Comments