The exploration of a question on setting the tag type for a newly created tag prompted me to put together a new little Building Coder sample command which may be useful for other purposes as well, since it can be started in an empty architectural project and performs the following steps:

  • Determine bottom and top level for placing a wall.
  • Create a new wall element and set its top bounding level.
  • Retrieve the wall thickness to later calculate an offset for the tag.
  • Retrieve a door symbol to use to insert a door in the wall.
  • Create a new door element at the wall midpoint.
  • Create a new door tag element associated with the door.
  • Retrieve an existing door tag type to duplicate.
  • Create a new door tag type by calling the Duplicate method.
  • Change the door tag’s type to the new door tag type.

The code to implement the first six steps has been extracted from the Revit API introduction Lab2_0_CreateLittleHouse external command, which we used repeatedly in the past to demonstrate various aspects, such as

selecting all walls
,

determining wall dimensions
,
creating

walls and doors on two levels
,
and in Revit 2011 showing the

immutability of the XYZ class
,

risks of the manual regeneration option
,
and a temporary problem requiring an explicit call to the

AutoJoinElements
method.

We already looked at the use of the Duplicate method to create a new family symbol for

walls and columns
,
later again for

columns
and

beams
,

family instances
,

dimensioning
,
and to create a new

material
.

The code also demonstrates some neat little element filtering collector usages.

So it is well worth extracting it and placing it into a self-contained Building Coder sample for easy immediate access.

Here is the question that prompted this update:

Question: I am writing some code to automatically tag an element.
Here is the line I am using to create the new tag:


  doc.Create.NewTag(
    doc.ActiveView,
    elem,
    False,
    TagMode.TM_ADDBY_CATEGORY,
    TagOrientation.TAG_HORIZONTAL,
    panelCenter );

This method allows me to choose the tagging mode, but not the type of tag that I want.

How can I specify the tag type to be used for the newly created tag?

In the user interface, I can define it in this dialogue:

Default tag type per category

Answer: You can set the tag type by calling ChangeTypeId with the desired tag type element id on the tag returned by the NewTag method.

I implemented a new Building Coder sample command CmdSetTypeTag to answer this and provide an executable example of implementing it.
It executes all of the steps listed in the introduction above, the last four of which address your specific question:

  • Create a door tag.
  • Retrieve an existing door tag type.
  • Duplicate it to create a new door tag type.
  • Assign the new type to the door tag.

The command makes use of a couple of constants and helper methods.

First of all, we define a conversion unit for converting meters to feet, since all length measurements within the Revit database make use of the latter.
Our recent exploration of the

voltage units
includes
an extensive list of back pointers to posts on this subject:


  const double MeterToFeet = 3.2808399;

This geometric helper method returns the midpoint between two points:


public static XYZ Midpoint( XYZ p, XYZ q )
{
  return p + 0.5 * ( q - p );
}

Then we have a group of filtered element collector routines to effectively retrieve the following sets of elements from the Revit database:

  • GetElementsOfType:
    Return all elements of the requested class,
    i.e. System.Type, matching the given built-in
    category in the given document.
  • GetFamilySymbols:
    Return all family symbols in the given document
    matching the given built-in category.
  • GetFirstFamilySymbol:
    Return the first family symbol found in the given document
    matching the given built-in category, or null if none is found.
  • GetBottomAndTopLevels:
    Determine bottom and top levels for creating walls.
    In a default empty Revit Architecture project,
    ‘Level 1’ and ‘Level 2’ will be returned.
    Returns true if the two levels are successfully determined.

static FilteredElementCollector
  GetElementsOfType(
    Document doc,
    Type type,
    BuiltInCategory bic )
{
  FilteredElementCollector collector
    = new FilteredElementCollector( doc );
 
  collector.OfCategory( bic );
  collector.OfClass( type );
 
  return collector;
}
 
static FilteredElementCollector
  GetFamilySymbols(
    Document doc,
    BuiltInCategory bic )
{
  return GetElementsOfType( doc,
    typeof( FamilySymbol ), bic );
}
 
static FamilySymbol GetFirstFamilySymbol(
  Document doc,
  BuiltInCategory bic )
{
  FamilySymbol s = GetFamilySymbols( doc, bic )
    .FirstElement() as FamilySymbol;
 
  Debug.Assert( null != s, string.Format(
    "expected at least one {0} symbol in project",
    bic.ToString() ) );
 
  return s;
}
 
static bool GetBottomAndTopLevels(
  Document doc,
  ref Level levelBottom,
  ref Level levelTop )
{
  FilteredElementCollector levels
    = GetElementsOfType( doc, typeof( Level ),
      BuiltInCategory.OST_Levels );
 
  foreach( Element e in levels )
  {
    if( null == levelBottom )
    {
      levelBottom = e as Level;
    }
    else if( null == levelTop )
    {
      levelTop = e as Level;
    }
    else
    {
      break;
    }
  }
 
  if( levelTop.Elevation < levelBottom.Elevation )
  {
    Level tmp = levelTop;
    levelTop = levelBottom;
    levelBottom = tmp;
  }
  return null != levelBottom && null != levelTop;
}

Putting these all together, here is the implementation of the external command mainline Execute method:


public Result Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  UIApplication app = commandData.Application;
  Document doc = app.ActiveUIDocument.Document;
 
  Autodesk.Revit.Creation.Application createApp
    = app.Application.Create;
 
  Autodesk.Revit.Creation.Document createDoc
    = doc.Create;
 
  // determine the wall endpoints:
 
  double length = 5 * MeterToFeet;
 
  XYZ [] pts = new XYZ[2];
 
  pts[0] = XYZ.Zero;
  pts[1] = new XYZ( length, 0, 0 );
 
  // determine the levels where 
  // the wall will be located:
 
  Level levelBottom = null;
  Level levelTop = null;
 
  if( !GetBottomAndTopLevels( doc,
    ref levelBottom, ref levelTop ) )
  {
    message = "Unable to determine "
      + "wall bottom and top levels";
 
    return Result.Failed;
  }
 
  // create a wall:
 
  BuiltInParameter topLevelParam
    = BuiltInParameter.WALL_HEIGHT_TYPE;
 
  ElementId topLevelId = levelTop.Id;
 
  Line line = createApp.NewLineBound(
    pts[0], pts[1] );
 
  Wall wall = createDoc.NewWall(
    line, levelBottom, false );
 
  Parameter param = wall.get_Parameter(
    topLevelParam );
 
  param.Set( topLevelId );
 
  // determine wall thickness for tag 
  // offset and profile growth:
 
  double wallThickness = wall.WallType
    .CompoundStructure.Layers.get_Item( 0 )
    .Thickness;
 
  // add door to wall;
  // note that the NewFamilyInstance method 
  // does not automatically add a door tag, 
  // like the ui command does:
 
  FamilySymbol doorSymbol = GetFirstFamilySymbol(
    doc, BuiltInCategory.OST_Doors );
 
  if( null == doorSymbol )
  {
    message = "No door symbol found.";
    return Result.Failed;
  }
 
  XYZ midpoint = Midpoint( pts[0], pts[1] );
 
  FamilyInstance door = createDoc
    .NewFamilyInstance( midpoint, doorSymbol,
      wall, levelBottom,
      StructuralType.NonStructural );
 
  // create door tag:
 
  View view = doc.ActiveView;
 
  double tagOffset = 3 * wallThickness;
 
  midpoint += tagOffset * XYZ.BasisY;
 
  IndependentTag tag = createDoc.NewTag(
    view, door, false, TagMode.TM_ADDBY_CATEGORY,
    TagOrientation.TAG_HORIZONTAL, midpoint );
 
  // create and assign new door tag type:
 
  FamilySymbol doorTagType
    = GetFirstFamilySymbol(
      doc, BuiltInCategory.OST_DoorTags );
 
  doorTagType = doorTagType.Duplicate(
    "New door tag type" ) as FamilySymbol;
 
  tag.ChangeTypeId( doorTagType.Id );
 
  return Result.Succeeded;
}

The result of running this in a new empty project is a wall, door, and door tag with the newly created door tag type assigned to it, looking like this:

New door tag type

Here is

version 2011.0.73.0
of The Building Coder samples including the complete source code and Visual Studio solution and the new command.


Comments

12 responses to “Set Tag Type”

  1. David Bartliff Avatar
    David Bartliff

    I guess from this that it is not possible to set the tag type before creation of a tag. Is it even possible to find which tag type would be used?
    In a similar manner I would like to know which level type will be used for a new level because I need to know whether the elevation to be set should be Project or Shared. In fact I want to set the ProjectElevation but it is Read-Only.
    The workaround will be
    a) Create the new level
    b) Check the ProjectElevation value is what I need
    c) If not change the elevation so that the ProjectElevation is correct

  2. Dear David,
    I am not sure that it is impossible to define the tag type before its creation, if it defined in a standard RFA file. You may be able to open the family file and both read and modify the default type to use. It might just mean opening an additional file in the background and manipulating that in addition to the project document that you may be more accustomed to be working in. Maybe there is even no need to save the modified library file to disk before reloading it. No guarantees, though.
    Another way to approach this issue is to use the manual regeneration option, insert the instance without caring exactly what type it is, modifying it after inserting it, and not regenerating between these two steps.
    I cannot really say anything about the levels, I have not looked at them from that point of view yet at all. If you really want to know that and have a concrete problem that you would like to solve, I would suggest submitting an ADN case so that we can have a look at it in more depth.
    Cheers, Jeremy.

  3. hello.my name is jude ,i want to create a new tag in the middle of a column.and the newtag() method need the xyz parameter, but i don’t know how to set the XYZ for the tag.would you please tell me how to do?

  4. in other words,i want to know how to get the middlepoint of a column.

  5. Dear Jude,
    That should be pretty easy. In all cases that I know of, the location point of the column family instance is in its center. You can access it though the Location property, casting the value to a LocationPoint. There are lots of samples of doing this on the blog.
    There are various other ways. That might be the simplest, though.
    Cheers, Jeremy.

  6. Dickins John Avatar
    Dickins John

    Dear Jeremy,
    Its feels great to read through your blog. Its a one stop solution for solution to most of the problems. Also description of your adventures are awesome.
    I need some help regarding rotating the Label in the tags and I want to keep it aligned with the tagged elements. This happens automatically in case of Wall Tags but not for Electrical Fixtures.
    TagOrientation property is available but can only be set to Vertical or Horizontal. Rotating tag rotates only the Leader line and not the label. Can you suggest a solution for the problem.
    Anyway awesome work. Keep going.

  7. Dickins John Avatar
    Dickins John

    I came to know that, tags are not rotating with all components because rotate with component property is associated only with curve based elements. Is there any way to force the tags (labels in tags) to rotate based on any imaginary angle found from the tagged element?

  8. Dear John,
    Thank you very much for your appreciation.
    Mostly, the API will only allow you to set up situations that can also be constructed manually through the UI somehow.
    Can you solve this task manually?
    One powerful API feature is the DMU, or Dynamic Model Update:
    http://lmgtfy.com/?q=revit+api+dmu
    Using that, you can align one element with another and ensure that it updates appropriately, as demonstrated, for example, by the Revit SDK DynamicModelUpdate sample.
    I hope this helps.
    Cheers, Jeremy.

  9. Jeremy,
    Can you explain in detail what it would take to make the Element.ChangeTypeId(Document, ICollection), ElementId) command work on a set of tags instead of just one. I am trying to change multiple tags all at once to avoid regeneration, but I am not sure how to use that method. if I call it on a element it returns an error saying that it was expecting just one argument. what is the proper way to use this method? thank you

  10. Dear Konrad,
    The Element.ChangeTypeId (Document, ICollection(Of ElementId), ElementId) method changes the element type of all elements whose ids are included in the collection.
    Here is a simple and senseless example assigning a wall the wall type that it already has:
    Wall wall = null;
    WallType wallType = wall.WallType;
    List[ElementId] ids = new ListElementId;
    ids.Add( wall.Id );
    Element.ChangeTypeId( wall.Document, ids, wallType.Id );
    (replace [] by ‘smaller than’ and ‘greater than’ signs)
    https://gist.github.com/jeremytammik/dda73fb51550cd7c1611
    Cheers, Jeremy.

  11. Preetesh Avatar
    Preetesh

    JEREMY,
    i want know if there is any way or API that can be create to add level detail of a door in door tag.
    for ex.. door D1 has a tag which shows D1 in the tag similary can i add level no or level detail of door into the tag…??

  12. Preetesh Avatar
    Preetesh

    DJ help me out with this….
    i want know if there is any way or API that can be create to add level detail of a door in door tag.
    for ex.. door D1 has a tag which shows D1 in the tag similary can i add level no or level detail of door into the tag…??

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading