How to collect elements from links visible on a view in the host model?

There are a lot of nodes working with links, e.g. in Bakery package, but no one does what I need. How to collect elements which are visible in a particular view in the host model? All collectors work either in a host OR in a linked model… How to combine them?

Hi @Nikolay_Gerasimov1 ,

here’s a way to do that :

import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import*
import System
from System.Collections.Generic import*

linkdoc = IN[0]
category = IN[1]
if isinstance(IN[2],list):
	views = IN[2]
else:
	views = [IN[2]]
result = []
filter = ElementCategoryFilter(System.Enum.ToObject(BuiltInCategory,category.Id))
ids = [v.Id for v in views]
idlist = [ElementId(i) for i in ids]
for i in idlist:
	result.append(FilteredElementCollector(linkdoc,i).WherePasses(filter).WhereElementIsNotElementType().ToElements())
OUT = result
3 Likes

Thnx for the quick reply, but FilteredElementCollector requires both document and the view to be in one model. If I try to ask elements from linked model in a view in the host model, Python returns an exception.
And I don’t need to filter elements - they are filtered on the view already.

I have used filteredelementcollector with linked document and host view with success . What exception do you get ?

Repeated your code and got the same:
The error is "viewId is not a view Parameter name: viewId"
Revit 2015, Dynamo 1.2

I am also repeated thisr code and got the same:
The error is “viewId is not a view Parameter name: viewId”.

Also this code take long time on linked model on the Revit server.

Hi @Swapnil_Raut ,

I just saw the two last messages in this thread… Can you please show a screenshot of the the views you use as your input?

Hi Mostafa,
I am working on a Mechanical model and I Want to select Active view elements from the Linked Architectural model for doing interference check with linked model active view elements.
While running script, I get following error messages .

@Swapnil_Raut
I’m having trouble reproducing your issue… It works on my side :


can you please share a dummy file with all the floorplan views and just a couple of elements?

Hi Mostafa,
Please find below link for Revit and dynamo file.

Thanks,
Swapnil

Okay I think I finally got it thanks to your files!
In order for the FilterdElementCollector method to work, the view needs to exist in the linked document as well.
You can use views from the host document as long as they exist in the link…
I always worked with views that exist in both files that’s why I never had issues with this.

1 Like

Here’s a workaround that combines category filter and boundingbox filter :

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import*
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
import System
from System.Collections.Generic import *

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import*
doc = DocumentManager.Instance.CurrentDBDocument

#Inputs
category = IN[2]
linkdoc = IN[1]
views = UnwrapElement(IN[0])

collector = []

#getting project base point elevation
basepointfilter = ElementCategoryFilter(BuiltInCategory.OST_ProjectBasePoint)
basepoint = FilteredElementCollector(doc).WherePasses(basepointfilter).ToElements()
bpelevation = [b.ParametersMap.get_Item('Elev').AsDouble() for b in basepoint]

#Category filter
catfilter = ElementCategoryFilter(System.Enum.ToObject(BuiltInCategory,category.Id))

UIunit = Document.GetUnits(doc).GetFormatOptions(UnitType.UT_Length).DisplayUnits
level = []
for v in views:

	#creating a boundingbox for each view from it's crop box and view range
	bb = v.CropBox
	vr = v.GetViewRange()
	topclip = PlanViewPlane.TopClipPlane
	bottomclip = PlanViewPlane.BottomClipPlane
	toplevel = (doc.GetElement(vr.GetLevelId(topclip)))
	topoffset = vr.GetOffset(topclip)
	topZ = toplevel.Elevation + topoffset - bpelevation[0]
	bottomlevel = (doc.GetElement(vr.GetLevelId(bottomclip)))
	bottomoffset = vr.GetOffset(bottomclip)
	bottomZ = bottomlevel.Elevation + bottomoffset - bpelevation[0]
	min = bb.Min
	max = bb.Max
	newmin = XYZ(UnitUtils.ConvertToInternalUnits(min.X,UIunit),UnitUtils.ConvertToInternalUnits(min.Y,UIunit),bottomZ)
	newmax = XYZ(UnitUtils.ConvertToInternalUnits(max.X,UIunit),UnitUtils.ConvertToInternalUnits(max.Y,UIunit),topZ)
	ol = Outline(newmin,newmax)

	#combining boundingbox and category filters
	bbfilter = BoundingBoxIntersectsFilter(ol)
	andfilter = LogicalAndFilter(catfilter,bbfilter)

	#collecting elements
	collector.append(FilteredElementCollector(linkdoc).WherePasses(andfilter).ToElements())

OUT = collector

let me know if it does the job for you. If it does I think i’ll add it to the Data-Shapes package

@Daniel_Woodcock1

1 Like

Hi Mostafa,

Thank you for your instant reply.

The new Python script is working perfectly with sample model, but whenever I run this script on live/Bigger project (All Revit Models is located in the Revit server) I get following error message.


My pleasure @Swapnil_Raut ,

I think the problem comes from the fact that som of your views have unlimited view ranges (no top, or no bottom or both).
I guess I could work something out with the cut plane for thoses cases…

Hi Moustafa,

Yes, you are right Some of views in our model have unlimited View ranges.

If you find out the way for cut planes,Please share with me, its very helpful for finish my dynamo.

Thank you again for your help.

Swapnil

Hi @Swapnil_Raut ,

try this (same inputs as before) :

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import*
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
import System
from System.Collections.Generic import *

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import*
doc = DocumentManager.Instance.CurrentDBDocument

#Inputs
category = IN[2]
linkdoc = IN[1]
views = UnwrapElement(IN[0])

collector = []

#getting project base point elevation
basepointfilter = ElementCategoryFilter(BuiltInCategory.OST_ProjectBasePoint)
basepoint = FilteredElementCollector(doc).WherePasses(basepointfilter).ToElements()
bpelevation = [b.ParametersMap.get_Item('Elev').AsDouble() for b in basepoint]

#Category filter
catfilter = ElementCategoryFilter(System.Enum.ToObject(BuiltInCategory,category.Id))

UIunit = Document.GetUnits(doc).GetFormatOptions(UnitType.UT_Length).DisplayUnits
level = []
for v in views:

	#creating a boundingbox for each view from it's crop box and view range
	bb = v.CropBox
	vr = v.GetViewRange()
	topclip = PlanViewPlane.TopClipPlane
	bottomclip = PlanViewPlane.BottomClipPlane
	cutclip = PlanViewPlane.CutPlane
	toplevel = (doc.GetElement(vr.GetLevelId(topclip)))
	topoffset = vr.GetOffset(topclip)
	cutlevel = (doc.GetElement(vr.GetLevelId(cutclip)))
	cutoffset = vr.GetOffset(cutclip)
	try:
		topZ = toplevel.Elevation + topoffset - bpelevation[0]
	except:
		topZ = cutlevel.Elevation + cutoffset - bpelevation[0]
	bottomlevel = (doc.GetElement(vr.GetLevelId(bottomclip)))
	bottomoffset = vr.GetOffset(bottomclip)
	try:
		bottomZ = bottomlevel.Elevation + bottomoffset - bpelevation[0]
	except :
		bottomZ = cutlevel.Elevation - bpelevation[0]
	min = bb.Min
	max = bb.Max
	newmin = XYZ(UnitUtils.ConvertToInternalUnits(min.X,UIunit),UnitUtils.ConvertToInternalUnits(min.Y,UIunit),bottomZ)
	newmax = XYZ(UnitUtils.ConvertToInternalUnits(max.X,UIunit),UnitUtils.ConvertToInternalUnits(max.Y,UIunit),topZ)
	ol = Outline(newmin,newmax)

	#combining boundingbox and category filters
	bbfilter = BoundingBoxIntersectsFilter(ol)
	andfilter = LogicalAndFilter(catfilter,bbfilter)

	#collecting elements
	collector.append(FilteredElementCollector(linkdoc).WherePasses(andfilter).ToElements())

OUT = collector 

please let me know how it goes!

What do you mean “the view needs to exist in in the linked document as well”? The same name? Id? Bounding box?
Anyway this approach will not work for me - I use a special “coordination” model which links all the rest to itself. I definitely don’t want to duplicate its views in the original models.

Same Id . Yes I totally undertand that it’s impossible have views with the same id unless it’s all set from the beginning… That’s why I suggested the workaround in my previous posts.

How can we control elements Ids?
Filtering by bounding box sounds promising. The only advantage would be to extract the category filter from the view visibility settings…

to my knoweledge, it’s not possible to control element ids.
What I suggest with the code posted above (you could try it) is to create bounding boxes representing views in the host model (using their crop box and view range). That bounding box will allow to collect linked elements in host views. There is also a category filter so you can be more specific with the collected elements.

I’m not sure I understand what you mean