Using ReferenceIntersector in Linked Files

Using ReferenceIntersector in Linked Files

IliaIvanov
Contributor Contributor
2,119 Views
6 Replies
Message 1 of 7

Using ReferenceIntersector in Linked Files

IliaIvanov
Contributor
Contributor

I have faced the problem - How to use ReferenceIntersector with elements of RevitLinkInstance. I have achieved some results using filters and etc - another words it works rather good, Below my solution 

0 Likes
Accepted solutions (2)
2,120 Views
6 Replies
Replies (6)
Message 2 of 7

IliaIvanov
Contributor
Contributor
Accepted solution

Here is some method for finding walls crossed by a pipe

public IList<Element> GetElementsFromIntersector (Document doc, Element pipe)
{
 IList<Element> walls = new List<Element>();
//Get Curve to provide start of reference Ray and direction
 LocationCurve lc = pipe.Location as LocationCurve;
 Curve curve = lc.Curve;
//Add custom equalityComparer to distinct List
 ReferenceComparer reference1 = new ReferenceComparer();
//Add Filter to find walls in linked project
 ElementFilter filter = new 
 ElementCategoryFilter(BuiltInCategory.OST_Walls);
//Find 3d view for ReferenceIntersector
 FilteredElementCollector collector = new FilteredElementCollector(doc);
 Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
 View3D view3D = collector.OfClass(typeof(View3D)).Cast<View3D>() 
 .First<View3D>(isNotTemplate);
//Set property of referenceIntersector to work with Links
 refIntersector.FindReferencesInRevitLinks = true;

 IList<ReferenceWithContext> referenceWithContext = 
 refIntersector.Find(curve.GetEndPoint(0), (curve as Line).Direction);
//Select uniq references from referenceWithContext
 IList<Reference> references = referenceWithContext.Select(p => 
 p.GetReference()).Distinct(reference1).ToList();    
 foreach(Reference reference in references)     
 {
  RevitLinkInstance instance = doc.GetElement(reference) as 
  RevitLinkInstance;
  Document linkDoc = instance.GetLinkDocument();
  Element wall = 
  linkDoc.GetElement(reference.LinkedElementId);
 }    

}

//Custom EqualityComparer
public class ReferenceComparer : IEqualityComparer<Reference>
 {
   public bool Equals(Reference x, Reference y)
    {              
      if (x.ElementId == y.ElementId)
      {                  
        if (x.LinkedElementId == y.LinkedElementId)         
        {
          return true;
        }               
          return false;            
      }             
     return false;
    }  
  public int GetHashCode(Reference obj)
    {
     int hashName = obj.ElementId.GetHashCode();
     int hashId = obj.LinkedElementId.GetHashCode();
     return hashId ^ hashId;
     }
    }          
                
               
           

          

 

0 Likes
Message 3 of 7

jeremytammik
Autodesk
Autodesk

Thank you very much for sharing your solution and code!

 

Could you please add a short description?

 

Optimally, describe the context, the problem, the solution, add an image and screen snapshot or two illustrating the situation and how the solution works, add a minimal sample model to run a test in, etc.

 

That would help a lot to understand what it is about and how useful it is.

 

Thank you!

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 4 of 7

IliaIvanov
Contributor
Contributor
Accepted solution

Sure!
My task was -  add opening family to the wall crossing pipe.  I decided to achieve it using IUpdater. My solution worked good with non linked walls, I used filters to make ReferenceIntersector find only walls. But when I started preparing solution for link walls I faced problem - References from ReferenceWithContext List contains id of RevitLinkInstance, but not the id of target walls,  it means I could`t gather linked walls. 

ilia77554046FJED_0-1589559811711.png
I have looked throw this post https://thebuildingcoder.typepad.com/blog/2015/07/using-referenceintersector-in-linked-files.html and found that we can`t get references from linked file. 
I started doing debug of my code and realized solution. The Reference class has property "LinkedElementId", if we have RevitLinkInstance and property of element in linked file - we can get the element in linked file. But when my pipe crosses the linked wall - in ReferenceWithContext  list   I have some amount of elements with the same ids("LinkedElementId","Id"), maybe because it also gather geometry  like faces and etc. To dictinct this list I had to create custom EqualityCompare. Finally the code started working perfect . Found some bugs in code above. Here is method to gather count of intersected walls. (Note to achieve good results - need to set 3d view without section boxes). Unfortunately no time to add comments to the code. Hope it may help somebody.

 

Bellow Project with macro to test

 

 

 

public void GetWalls()
	{
	Reference pipeRef = this.Selection.PickObject(ObjectType.Element);
	Element pipeElem = this.Document.GetElement(pipeRef);

	LocationCurve lc = pipeElem.Location as LocationCurve;
	Curve curve = lc.Curve;
	   	    
	ReferenceComparer reference1 = new ReferenceComparer();
			 
	ElementFilter filter = new 
        ElementCategoryFilter(BuiltInCategory.OST_Walls);
			 
	 FilteredElementCollector collector = new 
         FilteredElementCollector(this.Document);
         Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
         View3D view3D = collector.OfClass(typeof(View3D)).Cast<View3D>() 
         .First<View3D>(isNotTemplate);
        
          ReferenceIntersector refIntersector = new 
          ReferenceIntersector(filter, FindReferenceTarget.Element, 
          view3D);
          refIntersector.FindReferencesInRevitLinks = true;
          IList<ReferenceWithContext> referenceWithContext = 
          refIntersector.Find(curve.GetEndPoint(0), (curve as 
          Line).Direction);
          IList<Reference> references = referenceWithContext.Select(p => p.GetReference()).Distinct(reference1).Where(p=>p.GlobalPoint.DistanceTo(curve.GetEndPoint(0))<curve.Length).ToList();
          IList<Element>walls = new List<Element>();
          foreach (Reference reference in references)
          {
           RevitLinkInstance instance = this.Document.GetElement(reference) 
           as RevitLinkInstance;
           Document linkDoc = instance.GetLinkDocument();
           Element element = linkDoc.GetElement(reference.LinkedElementId);
           walls.Add(element);
           }
              TaskDialog.Show("Count of wall",walls.Count.ToString());
	}
public class ReferenceComparer : IEqualityComparer<Reference>
{
 public bool Equals(Reference x, Reference y)
 {              
  if (x.ElementId == y.ElementId)
  {                  
   if (x.LinkedElementId == y.LinkedElementId)         
   {
    return true;
   }               
    return false;            
   }             
   return false;
  }  
  public int GetHashCode(Reference obj)
  {
   int hashName = obj.ElementId.GetHashCode();
   int hashId = obj.LinkedElementId.GetHashCode();
   return hashId ^ hashId;
  }
}          

 

 

 

 

 

0 Likes
Message 5 of 7

IliaIvanov
Contributor
Contributor

If there is a reason - here is code to get StableRepresentation of linked wall exterior face 

 

public string GetFaceRefRepresentation(Wall wall, Document doc, RevitLinkInstance instance)
{
 Reference faceRef = HostObjectUtils.GetSideFaces(wall, 
 ShellLayerType.Exterior).FirstOrDefault();
 Reference stRef = faceRef.CreateLinkReference(instance);
 string stable = stRef.ConvertToStableRepresentation(doc);
 return stable;
}

 

 

0 Likes
Message 6 of 7

jeremytammik
Autodesk
Autodesk

Thank you very much for sharing and documenting this!

 

I published it for posterity in The Building Coder:

 

https://thebuildingcoder.typepad.com/blog/2020/05/using-referenceintersector-with-a-linked-file.html

 

Best regards,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 7 of 7

IliaIvanov
Contributor
Contributor

You are welcome😃

0 Likes