Foreground Image Import and Visible DWG Geometry

Today, we explore how to retrieve visible DWG geometry, i.e., geometry elements contained in a CAD import instance on a layer that is visible in the currently active view, and how to import an image to the foreground instead of the default background setting:

Retrieve CAD Import Geometry on Visible Layer

Ryan Goertzen of Goertzen Enterprises shared a nice sample showing how to retrieve visible DWG geometry, i.e., geometry elements contained in a CAD import instance on a layer that is visible in the currently active view, in the Revit API discussion forum thread on visibility of DWG layer:

Question: I am trying to extract geometry from an import instance whose layers are currently visible in the current view.

I pass the view into the geometry options, but it still returns all geometry in the instance.

I can get the name of the DWG layer from:

  (curDoc.GetElement(curObj.GraphicsStyleId)
    as GraphicsStyle).Name;

However, I cannot seem to find anywhere in the View class to check if this layer is on or off, which is achievable with the UI visibility dialog.

What would be the best way to approach this?

Answer: I looked at the description how to hide layers in CAD files (also in LT). It refers to 'imported categories'.

Therefore, you may be able to affect the visibility in the Revit view by controlling the category visibility.

The following two methods do so:

To test, you might call GetCategoryHidden, make a note of what it returns, modify the setting for a specific layer manually, and check the return value again to see whether it changed.

If so, it may be possible to use SetCategoryHidden to control the layer visibility.

Response: I see now that the layer categories that I was after to pass into GetCategoryHidden are nested one step further down in GraphicsStyleGraphicsStyleCategoryId.

In my testing, I was thinking that the API wanted a built-in category. For that, all I could find was the built-in category OST_ImportObjectStyles.

The following code works as expected, only adding the DWG geometry that is currently visible in view to the list:

  foreachGeometryObject gObject in gElement )
  {
    gInstance = (GeometryInstance) gObject;
    gElement2 = gInstance.GetInstanceGeometry();

    foreachGeometryObject obj in gElement2 )
    {
      var gStyle = curDoc.GetElement( obj.GraphicsStyleId )
        as GraphicsStyle;

      // Try Catch because i was getting some Object
      // Instance errors with the graphics style object.

      try
      {
        // Add object to list

        if( !view.GetCategoryHidden( gStyle.GraphicsStyleCategory.Id ) )
          list.Add( obj );
      }
      catch { continue; }
    }
  }

This required cleaning it up a bit to avoid calling GetInstanceGeometry without first checking whether gInstance is null, and eliminate the evil catch-all exception handler.

To try it out yourself at home, here is a simple demonstration project, dwg_visible_geo_2018.rvt (also for Revit 2019).

The project includes a basic macro that will draw a detailed region around the visible layer in the linked CAD file, dwg_visible_geo.dwg:

  /// <summary>
  /// Pick a DWG import instance, extract polylines 
  /// from it visible in the current view and create
  /// filled regions from them.
  /// </summary>
  public void ProcessVisible( UIDocument uidoc )
  {
    Document doc = uidoc.Document;
    View active_view = doc.ActiveView;

    List<GeometryObject> visible_dwg_geo 
      = new List<GeometryObject>();

    // Pick Import Instance

    Reference r = uidoc.Selection.PickObject( 
      ObjectType.Element,
      new JtElementsOfClassSelectionFilter<ImportInstance>() );

    var import = doc.GetElement( r ) as ImportInstance;

    // Get Geometry

    var ge = import.get_Geometry( new Options() );

    foreachvar go in ge )
    {
      if( go is GeometryInstance )
      {
        var gi = go as GeometryInstance;

        var ge2 = gi.GetInstanceGeometry();

        if( ge2 != null )
        {
          foreachvar obj in ge2 )
          {
            // Only work on PolyLines

            if( obj is PolyLine )
            {
              // Use the GraphicsStyle to get the 
              // DWG layer linked to the Category 
              // for visibility.

              var gStyle = doc.GetElement( 
                obj.GraphicsStyleId ) as GraphicsStyle;

              // Check if the layer is visible in the view.

              if( !active_view.GetCategoryHidden(
                gStyle.GraphicsStyleCategory.Id ) )
              {
                visible_dwg_geo.Add( obj );
              }
            }
          }
        }
      }
    }

    // Do something with the info

    if( visible_dwg_geo.Count > 0 )
    {
      // Retrieve first filled region type

      var filledType = new FilteredElementCollector( doc )
        .WhereElementIsElementType()
        .OfClass( typeofFilledRegionType ) )
        .OfType<FilledRegionType>()
        .First();

      usingvar t = new Transaction( doc ) )
      {
        t.Start( "ProcessDWG" );

        foreachvar obj in visible_dwg_geo )
        {
          var poly = obj as PolyLine;

          // Draw a filled region for each polyline

          ifnull != poly )
          {
            // Create loops for detail region

            var curveLoop = new CurveLoop();

            var points = poly.GetCoordinates();

            forint i = 0; i < points.Count - 1; ++i )
            {
              curveLoop.Append( Line.CreateBound( 
                points[i], points[i + 1] ) );
            }

            FilledRegion.Create( doc, 
              filledType.Id, active_view.Id, 
              new List<CurveLoop>() { curveLoop } );
          }
        }
      }
    }
  }

Visible DWG geometry

Many thanks to Ryan for raising, solving and sharing this!

I copied the macro code to The Building Coder samples release 2019.0.139.2 in order to format and preserve it for posterity in the module CmdProcessVisibleDwg.cs.

Import Image Using Foreground Option

Another import question, this time on images:

Question: When I insert a raster image into Revit, by default, it is displayed as 'background'.

Is there any way I can do it using the 'Foreground' option?

It would be a great help to know the property that I can set using the C# Revit API.

I am working on some automation projects, and all the images must be displayed as 'Foreground'.

Answer: I first took a look at the Revit API documentation on the Import method with ImageImportOptions, the ImageImportOptions class and its members. It does not answer your question off-hand.

Next, I asked the development team, who explained:

Not at the time of import.

However, you can set the built-in parameter IMPORT_BACKGROUND to 0 on the ImageInstance afterwards.

The element id of the image instance is returned from the Document.Import overloaded method for importing images.

Response: As you suggested, I set the parameter IMPORT_BACKGROUND to 0.

It worked.

Thanks a lot for your help.