30 août 2020

[Dynamo+=Python]Rechercher et Filtrer des Éléments (FilteredElementCollector)







La Classe FilteredElementCollector permet de rechercher une collection d'éléments à l'aide de méthodes et/ou de filtres. 
Voici un récapitulatif et quelques exemples sur son utilisation.

  • Les constructeurs

  • Les Méthodes

  • Les QuickFilter
Les filtres rapides fonctionnent sur une classe à faible mémoire qui a une interface limitée pour lire les propriétés des éléments.

  • Les SlowFilter
Les filtres lents exigent que l'élément soit obtenu et développé en mémoire d'abord.
  • Les LogicalFilter
Il s'agit de filtres qui ont pour but de combiner des Filtres entre eux (ET / OU)




Quelques exemples en Python


  • Exemple 1 : Convertir l'Enumerable en liste (Element ou ElementId)
méthodes ToElementIds() ou ToElements()
 
listElems = FilteredElementCollector(doc).OfClass(FamilyInstance).WhereElementIsNotElementType().ToElements()
listElemIds = FilteredElementCollector(doc).OfClass(FamilyInstance).WhereElementIsNotElementType().ToElementIds()


  • Exemple 2 : Obtenir le 1er Élément d'une recherche
méthode FirstElement()
 
firstElem = FilteredElementCollector(doc).OfClass(FamilyInstance).WhereElementIsNotElementType().FirstElement()


  • Exemple 3 : (QuickFilter) Recherche par sous projet
 
elementWorksetFilter = ElementWorksetFilter(workset.Id)
fecbyWkset = FilteredElementCollector(doc).WherePasses(elementWorksetFilter).ToElements()


  • Exemple 4 : (QuickFilter) Recherche Elements rattachées à une Vue (Owned)
 
myelectrical_view = doc.ActiveView
# get all elements view dependent from this view
# wires, tags, textNote, etc..
fecbyWkset = FilteredElementCollector(doc).OwnedByView(myelectrical_view.Id).ToElements()

  • Exemple 5 : (QuickFilter) Recherche par multi-catégories et par multi-Classe
#multi-Catégorie
 
#Create Multi category filter
#need import List from System.Collections.Generic
cat_list = [BuiltInCategory.OST_ElectricalFixtures, BuiltInCategory.OST_GenericModel]
typed_list = List[BuiltInCategory](cat_list)
filtercat = ElementMulticategoryFilter(typed_list)

fecMultiCat = FilteredElementCollector(doc).WherePasses(filtercat).WhereElementIsNotElementType().ToElements()
#multi-Classe
 
TypedClasses = List[System.Type]()
TypedClasses.Add(DB.MEPCurve)
# or
TypedClasses = List[System.Type]([clr.GetClrType([DB.MEPCurve)])
multiClassFilter = ElementMulticlassFilter(TypedClasses)
wantedElementes = FilteredElementCollector(doc).WherePasses(multiClassFilter).ToElements()
OUT = wantedElementes  


  • Exemple 6 : (SlowFilter) Recherche en construisant un filtre de paramètre
 
#Create parameter value filter
pvp = ParameterValueProvider(ElementId(BuiltInParameter.ALL_MODEL_TYPE_NAME))
evaluator = FilterStringContains()
rule = FilterStringRule(pvp, evaluator, "Reservation", False)
filter = ElementParameterFilter(rule)

fecparafilter = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()

  •  Exemple 7 : Utilisation de la classe ParameterFilterRuleFactory
https://www.revitapidocs.com/2018.2/0637b53b-96aa-7683-6535-8a1217c5809e.htm
comparaison des 2 méthodes 
   
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

phases = List[Phase](doc.Phases)
phaseSelect = phases.Find(lambda x : x.Name == "Nouvelle construction")

pvp = ParameterValueProvider(ElementId(BuiltInParameter.PHASE_CREATED))
evaluator = FilterNumericEquals()
rule = FilterElementIdRule(pvp, evaluator, phaseSelect.Id)
filter = ElementParameterFilter(rule)

## OR USING ParameterFilterRuleFactory CLASS ##

rule = ParameterFilterRuleFactory.CreateEqualsRule(ElementId(BuiltInParameter.PHASE_CREATED), phaseSelect.Id)
filter = ElementParameterFilter(rule)
fecmultiplefilter = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()

OUT = fecmultiplefilter 
. .
  • Exemple 8 : (QuickFilter + SlowFilter) Recherche par cumul de filtres
 
#Create Multi category filter
cat_list = [BuiltInCategory.OST_ElectricalFixtures, BuiltInCategory.OST_GenericModel]
typed_list = List[BuiltInCategory](cat_list)
filtercat = ElementMulticategoryFilter(typed_list)

#Create parameter value filter
pvp = ParameterValueProvider(ElementId(BuiltInParameter.ALL_MODEL_TYPE_NAME))
evaluator = FilterStringContains()
rule = FilterStringRule(pvp, evaluator, "Reservation", False)
filter = ElementParameterFilter(rule)

fecmultiplefilter = FilteredElementCollector(doc).WherePasses(filtercat).WherePasses(filter).WhereElementIsNotElementType().ToElements()


  • Exemple 9 : Filtrer des éléments ou un paramètre partagé est présent
               
     import clr
    clr.AddReference('ProtoGeometry')
    from Autodesk.DesignScript.Geometry import *
    
    clr.AddReference('RevitAPI')
    import Autodesk
    from Autodesk.Revit.DB import *
    
    clr.AddReference('RevitServices')
    import RevitServices
    from RevitServices.Persistence import DocumentManager
    doc = DocumentManager.Instance.CurrentDBDocument
    
    
    fRule = SharedParameterApplicableRule("nameOfSharedParameter")
    filter = ElementParameterFilter(fRule)
    fec = FilteredElementCollector(doc).WherePasses(filter).ToElements()
    OUT = fec         
    

  • Exemple 10 : Filtre inversé avec multi-Classes et Catégories
-
           
 import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

inverted = True
class_list = [DB.RevitLinkInstance, DB.ImportInstance]
typed_list = List[System.Type]([clr.GetClrType(t) for t in class_list])
filterClass = ElementMulticlassFilter(typed_list, inverted)
#
filterCam = ElementCategoryFilter(BuiltInCategory.OST_Cameras, inverted)
#
filterAnd = LogicalAndFilter(filterClass, filterCam)
#
allElements = FilteredElementCollector(doc, c_view.Id).WhereElementIsNotElementType().WherePasses(filterAnd).ToElements()       


-
  • Exemple 11 : Rajouter des filtres avec des fonctions
En Python généralement lorsque l'on veut filtrer une collection on utilise les compréhensions de liste ou la fonction filter(), mais avec IronPython ou Pythonnet (.Net) on peut aller un peu plus loin.
2 alternatives à ceci (avec une potentielle erreur si la collection est vide, "IndexError: list index out of range")
  
scheduleFiltereds = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).ToElements()
scheduleFiltered = [x for in scheduleFiltereds if x.IsTemplate ][0]  

    • La première solution consiste à utiliser les Predicates :Predicate[Primitive](lambda x: True)

#filtered List 
schedulesFiltered = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).ToElements().FindAll(lambda x :  x.IsTemplate)
#équivalent en c# avec System.Linq
#schedulesFiltered = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).ToElements(x =>  x.IsTemplate)

#filtered List and get the first element
scheduleFiltered = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).ToElements().Find(lambda x : x.IsTemplate)

#filtered List and get the last element
scheduleFiltered = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).ToElements().FindLast(lambda x : x.IsTemplate)

ainsi cette fonction

def getfilterByName(nameFilter):
    collfilter = FilteredElementCollector(doc).OfClass(FilterElement).ToElements()
    if collfilter:
        filterSelect = [x for x in collfilter if x.Name == nameFilter]
        if filterSelect:
            return filterSelect[0]
        else: return None
peut s'écrire comme ceci

def getfilterByName(nameFilter):
	filterSelect = FilteredElementCollector(doc).OfClass(FilterElement).ToElements().Find(lambda x :  x.Name == nameFilter)
	return filterSelect
    
OU Comme ceci avec un itérateur et une valeur par défaut

filtersElems = FilteredElementCollector(doc).OfClass(FilterElement).ToElements()
filterSelect = next((x for x in filtersElems if x.Name == nameFilter), None)
if filterSelect is not None:
	OUT = filterSelect  
Note 1 :
les méthodes suivantes, 
Exists(Predicate)
Find(Predicate)
FindAll(Predicate)
FindLast(Predicate)

sont des méthodes de la classe List (qui est une implémentation de l’interface IList), il est donc nécessaire d'établir une conversion ou bien d'appliquer les méthodes ToElements()   ou ToElementIds() qui font le boulot.



Note 2 
petite aparté sur PythonNet
avec le moteur CPython3 et le paquet PythonNet il est nécessaire de passer par un cast pour utiliser ces méthodes (IronPython fait  automatiquement la conversion)

#need import Predicate Class
import System
from System import Predicate

def getfilterByName(nameFilter):
    #cast lambda Function 
    filterFunc = Predicate[System.Object](lambda x :  x.Name == nameFilter) 
    filterSelect = FilteredElementCollector(doc).OfClass(FilterElement).ToElements().Find(filterFunc)
return filterSelect

un exemple avec des conversions de Type et utilisation de la méthode FindAll() :


Cependant même avec le Package PythonNet l'ajout de conversion et de méthodes peuvent être possible en modifiant le package


    • La deuxième solution consiste à utiliser les LINQ types comme en C#, il est nécessaire pour cela d'importer l'extension System.Linq

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
newdoc = DocumentManager.Instance.CurrentDBDocument

import clr
clr.AddReference("System.Core")
import System
clr.ImportExtensions(System.Linq)

scheduleFiltered = FilteredElementCollector(newdoc).OfCategory(BuiltInCategory.OST_Schedules).Where(lambda x : x.IsTemplate).ToList()
OUT = scheduleFiltered  
Note cette extension n'existe pas à ce jour avec le package PythonNnet


  • Exemple 11 : Recherche d'éléments dans une maquette liée

linkinstance = UnwrapElement(IN[0])
doclink = linkinstance.GetLinkDocument()

colllink = FilteredElementCollector(doclink).WhereElementIsNotElementType().ToElements()


  • Exemple 12 : Recherche avec combinaison de filtres logique OU (OR)
dans l'exemple ci-dessous on recherche les éléments d'une maquette liée seulement dans la vue active
    
#written by Cyril P
import clr
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

def gen_PairPts(*args):
	"""generator for point pair loop
	"""
	for idx, pt in enumerate(args):
		try: yield pt, args[idx + 1]
		except : yield pt, args[0]

def solid_fromBBX(bbox):
	pt0 = XYZ( bbox.Min.X, bbox.Min.Y, bbox.Min.Z )
	pt1 = XYZ( bbox.Max.X, bbox.Min.Y, bbox.Min.Z )
	pt2 = XYZ( bbox.Max.X, bbox.Max.Y, bbox.Min.Z )
	pt3 = XYZ( bbox.Min.X, bbox.Max.Y, bbox.Min.Z )
	#create generator
	pairPts = gen_PairPts(pt0, pt1, pt2, pt3) 
	#Create loop, still in BBox coords
	edges = List[Curve]()
	for pt1, pt2 in pairPts:
		edges.Add(Line.CreateBound( pt1, pt2 ))

	height = bbox.Max.Z - bbox.Min.Z
	baseLoop = CurveLoop.Create( edges )
	loopList = List[CurveLoop]()
	loopList.Add( baseLoop )
	preTransformBox = GeometryCreationUtilities.CreateExtrusionGeometry( loopList, XYZ.BasisZ, height )
	transformBox = SolidUtils.CreateTransformed( preTransformBox, bbox.Transform )
	return transformBox

lnkInstance = UnwrapElement(IN[0])
transf =lnkInstance.GetTotalTransform()

actView = doc.ActiveView
doclnk = lnkInstance.GetLinkDocument()
#get bootom and top Elevation of active viewrange 
viewRange = actView.GetViewRange()
topClipPlaneId = viewRange.GetLevelId(PlanViewPlane.TopClipPlane) 
topClipPlane = doc.GetElement(topClipPlaneId)
topOffset = viewRange.GetOffset(PlanViewPlane.TopClipPlane)

bttmClipPlaneId = viewRange.GetLevelId(PlanViewPlane.BottomClipPlane) 
bttmClipPlane = doc.GetElement(bttmClipPlaneId)
bttmOffset = viewRange.GetOffset(PlanViewPlane.BottomClipPlane)

bbxActView = actView.CropBox 
#set min and max from active ViewRange
bbxActView.Min = XYZ(bbxActView.Min.X, bbxActView.Min.Y, bttmClipPlane.ProjectElevation + bttmOffset)
bbxActView.Max = XYZ(bbxActView.Max.X, bbxActView.Max.Y, topClipPlane.ProjectElevation + topOffset)
bbxActView.Transform = transf.Inverse
# method 1 (better result but slow)
solidbbx = solid_fromBBX(bbxActView)
filterSolid = ElementIntersectsSolidFilter(solidbbx)
fecWalls = FilteredElementCollector(doclnk).OfClass(Wall).WherePasses(filterSolid).ToElements()
# method 2 (faster but less precision)
myOutLn  = Outline(bbxActView.Min, bbxActView.Max)   
# #create LogicalOrFilter with BoundingBox 
filterBbxInside = BoundingBoxIsInsideFilter(myOutLn)
filterBbxInters = BoundingBoxIntersectsFilter(myOutLn)
filterBbx = LogicalOrFilter(filterBbxInside, filterBbxInters)
fecWalls = FilteredElementCollector(doclnk).OfClass(Wall).WherePasses(filterBbx).ToElements()

OUT = fecRoom 

  • Exemple 13 : Utiliser le filtre directement sur un Élément
  • dans l'exemple ci-dessous on teste le filtre directement un élément sans passer un FilteredElementCollector avec la méthode PassesFilter  
        
    #written by Cyril P
    my_element = UnwrapElement(IN[0])
    myOutLn  = Outline(bbxActView.Min, bbxActView.Max)   
    #create LogicalOrFilter with BoundingBox 
    filterBbxInside = BoundingBoxIsInsideFilter(myOutLn)
    filterBbxInters = BoundingBoxIntersectsFilter(myOutLn)
    filterBbx = LogicalOrFilter(filterBbxInside, filterBbxInters)
    # test the filter
    if filterBbx.PassesFilter(my_element):
        # do this
        #
    
    .
  • Et pour finir un exemple pour sélectionner une collection d'éléments.
Pour cela :
  1.  on récupère la Sélection Active du projet courant avec la propriété Selection (objet UIDocument )
  2.  puis on lui applique la méthode SetElementIds()

Exemple : 
 
import clr
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument

workset = UnwrapElement(IN[0])
elementWorksetFilter = ElementWorksetFilter(workset.Id)
fecbyWksetId = FilteredElementCollector(doc).WherePasses(elementWorksetFilter).ToElementIds()
selectElemId = uidoc.Selection.GetElementIds(fecbyWksetId)
OUT = selectElemId 

0 commentaires:

Enregistrer un commentaire