Source code for revitron.filter

""" 
The ``filter`` submodule is one of the most essential one within the **Revitron** package. 
Its main purpose is taking over the heavy lifting of filtering elements in the **Revit** database and
complementing the standard **Revit API** ``FilteredElementCollector`` class with 
the ability to filter collections by parameter values::

	elements = (
	    revitron.Filter
	    .byStringEquals('param', 'value')
	    .noTypes()
	    .getElements()
	)
	
Note that you can **invert** a filter by providing a the third argument for a string filter as follows::

	elements = (
	    revitron.Filter
	    .byStringEquals('param', 'value', True)
	    .noTypes()
	    .getElements()
	)

.. note:: In order to filter elements in another model instead of the active one, it is possible to change 
	the document context using the ``with`` statement.

The document context can be changed as follows::

	with revitron.Document(anyOtherDoc):
	    fltr = revitron.Filter().noTypes()
	    elements = fltr.getElements()
"""
import re
from System.Collections.Generic import List


[docs]class Filter: """ A filter class based on the ``FilteredElementCollector`` class. """
[docs] def __init__(self, scope=None): """ Inits a new Filter instance. Args: scope (Element ID or list of elements, optional): The optional scope. It can be either a view Id or a list of elements. Defaults to None. """ import revitron if scope is not None: if scope: if type(scope) == list: elementIds = [] for element in scope: elementIds.append(element.Id) scope = List[revitron.DB.ElementId](elementIds) self.collector = revitron.DB.FilteredElementCollector(revitron.DOC, scope) else: logger = revitron.Log() logger.warning( 'Provided scope for filter is empty! Therefore the filter will be initialized with all elements included.' ) self.collector = revitron.DB.FilteredElementCollector(revitron.DOC) else: self.collector = revitron.DB.FilteredElementCollector(revitron.DOC) self.scope = scope
def _all(self): """ This is just a helper filter that doesn't modify the collection at all to allow for getting all elements of an instance without actually applying any filter before. Returns: object: The Filter instance """ import revitron db = revitron.DB f = db.LogicalOrFilter( db.ElementIsElementTypeFilter(False), db.ElementIsElementTypeFilter(True) ) self.collector = self.collector.WherePasses(f) return self def _applyFilter(self, filterRule, paramName, value, evaluator, invert=False): """ Applies a filter. Args: filterRule (class): A Revit filter rule class paramName (string): The parameter name value (number): the value evaluator (object): The evaluator object invert (boolean): Inverts the filter """ import revitron filters = [] # Since a visible parameter name could match multiple built-in parameters, # we get a list of value providers. # While iterating that list, the parameter filter is applied each time # to a fresh element collector that will be later merged or intersected with the others. for valueProvider in revitron.ParameterValueProviders(paramName).get(): # Build filter rule based on filter type. if 'Double' in str(filterRule): rule = filterRule(valueProvider, evaluator, value, 0.001) elif 'Integer' in str(filterRule): rule = filterRule(valueProvider, evaluator, value) else: try: rule = filterRule(valueProvider, evaluator, value, True) except: rule = filterRule(valueProvider, evaluator, value) _ids = self.getElementIds() if _ids: _filter = Filter() _filter.collector = revitron.DB.FilteredElementCollector( revitron.DOC, _ids ) _filter._parameterFilter(rule, invert) filters.append(_filter) if len(filters): self.collector = filters[0].collector if len(filters) > 1: for i in range(1, len(filters)): if not invert: self.collector.UnionWith(filters[i].collector) else: self.collector.IntersectWith(filters[i].collector) def _parameterFilter(self, rule, invert=False): """ Applies a parameter filter. Args: rule (object): The filter rule object invert (boolean): Inverts the filter """ import revitron parameterFilter = revitron.DB.ElementParameterFilter(rule, invert) self.collector = self.collector.WherePasses(parameterFilter) def _getNumericFilterValue(self, value, paramName): """ Get the correct numeric filter value based on the storage type of a given parameter. Args: value (integer|float): A filter value paramName (string): The parameter name Returns: object: A filter value of the correct type """ from revitron import ParameterUtils if ParameterUtils.getStorageType(paramName) == 'Integer': return int(value) return float(value) def _getNumericFilterRule(self, paramName): """ Get the correct numeric filter rule based on the storage type of a given parameter. Args: paramName (string): The parameter name Returns: object: A filter rule object for double or integer """ from revitron import DB, ParameterUtils if ParameterUtils.getStorageType(paramName) == 'Integer': return DB.FilterIntegerRule return DB.FilterDoubleRule
[docs] def byIntersection(self, element): """ Reduces the set of elements to the ones that are intersecting a given element. Args: element (objetc): A Revit element Returns: object: The Filter instance """ import revitron self.collector = self.collector.WherePasses( revitron.DB.ElementIntersectsElementFilter(element) ) return self
[docs] def byRegex(self, paramName, regex, invert=False): """ Filters a collection by a given regex. Args: paramName (string): The name of the parameter to be matched. regex (string): The regex. invert (bool, optional): Inverts the filter. Defaults to False. Returns: object: The Filter instance """ import revitron passed = [] failed = [] for element in self.getElements(): value = revitron.Parameter(element, paramName).getString() if not value: value = revitron.Parameter(element, paramName).getValueString() if value: if re.search(regex, value, re.IGNORECASE): passed.append(element) else: failed.append(element) if not invert: elements = passed else: elements = failed if elements: self.collector = Filter(elements).collector return self
[docs] def byCategory(self, name): """ Filters the collection by a category name or a built-in category name. Note that there are basically three valid types that can be used as the filter argument. The first two use the name of a built-in category (with or without the ``OST_`` prefix):: fltr = revitron.Filter().byCategory('Walls') fltr = revitron.Filter().byCategory('OST_Walls') The third type uses a `natural <https://docs.google.com/spreadsheets/d/1uNa77XYLjeN-1c63gsX6C5D5Pvn_3ZB4B0QMgPeloTw/edit#gid=1549586957>`_ category name to find a corresponding built-in category to filter, here ``OST_BeamAnalyticalTags``:: fltr = revitron.Filter().byCategory('Analytical Beam Tags') Args: name (string): A category or built-in category name Returns: object: The Filter instance """ import revitron try: self.collector = self.collector.OfCategory( revitron.BuiltInCategory(name).get() ) except: pass return self
[docs] def byClass(self, cls): """ Filters the collection by class. Example:: fltr = revitron.Filter() cls = Autodesk.Revit.DB.CurveElement ids = fltr.byClass(cls).noTypes().getElementIds() Alternatively it is also possible to use a class name as string instead:: fltr = revitron.Filter() ids = fltr.byClass('CurveElement').noTypes().getElementIds() Args: cls (class): A class or class name to filter the elements Returns: object: The Filter instance """ if isinstance(cls, basestring): import Autodesk.Revit.DB cls = getattr(Autodesk.Revit.DB, cls) self.collector = self.collector.OfClass(cls) return self
[docs] def byNumberIsGreater(self, paramName, value, invert=False): """ Filters the collection by parameter values greater than a given number. Example:: fltr = revitron.Filter() ids = fltr.byNumberIsGreater('Area', 5).noTypes().getElementIds() Args: paramName (string): The parameter name value (number): The numeric value to compare to invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( self._getNumericFilterRule(paramName), paramName, self._getNumericFilterValue(value, paramName), revitron.DB.FilterNumericGreater(), invert ) return self
[docs] def byNumberIsGreaterOrEqual(self, paramName, value, invert=False): """ Filters the collection by parameter values greater than or equal to a given number. Example:: fltr = revitron.Filter() fltr = fltr.byNumberIsGreaterOrEqual('Area', 5).noTypes() ids = fltr.getElementIds() Args: paramName (string): The parameter name value (number): The numeric value to compare to invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( self._getNumericFilterRule(paramName), paramName, self._getNumericFilterValue(value, paramName), revitron.DB.FilterNumericGreaterOrEqual(), invert ) return self
[docs] def byNumberIsEqual(self, paramName, value, invert=False): """ Filters the collection by parameter values equal to a given number. Example:: fltr = revitron.Filter() ids = fltr.byNumberIsEqual('Area', 5).noTypes().getElementIds() Args: paramName (string): The parameter name value (number): The numeric value to compare to invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( self._getNumericFilterRule(paramName), paramName, self._getNumericFilterValue(value, paramName), revitron.DB.FilterNumericEquals(), invert ) return self
[docs] def byNumberIsLess(self, paramName, value, invert=False): """ Filters the collection by parameter values smaller than a given number. Example:: fltr = revitron.Filter() ids = fltr.byNumberIsLess('Area', 5).noTypes().getElementIds() Args: paramName (string): The parameter name value (number): The numeric value to compare to invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( self._getNumericFilterRule(paramName), paramName, self._getNumericFilterValue(value, paramName), revitron.DB.FilterNumericLess(), invert ) return self
[docs] def byNumberIsLessOrEqual(self, paramName, value, invert=False): """ Filters the collection by parameter values smaller than or equal to a given number. Example:: fltr = revitron.Filter() ids = fltr.byNumberIsLessOrEqual('Area', 5).noTypes().getElementIds() Args: paramName (string): The parameter name value (number): The numeric value to compare to invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( self._getNumericFilterRule(paramName), paramName, self._getNumericFilterValue(value, paramName), revitron.DB.FilterNumericLessOrEqual(), invert ) return self
[docs] def byOneInCsv(self, evaluatorName, paramName, csv, invert=False): """ Filters the collection by testing whether a string contains at lease one ot the items in a CSV list. This method is the base method for the ``byStringContainsOneInCsv`` and ``byStringEqualsOneInCsv`` methods. .. note:: that by setting ``invert`` to ``True``, all elements that match one of the items will be removed from the collection. Args: evaluatorName (method): The filter method to be used to filter paramName (string): The name of the parameter csv (string): A comma separated list of items invert (bool, optional): Inverts the filter. Defaults to False. Returns: object: The Filter instance """ import revitron evaluator = getattr(Filter, evaluatorName) filters = [] for item in csv.split(','): _filter = Filter() try: _filter.collector = revitron.DB.FilteredElementCollector( revitron.DOC, self.getElementIds() ) except: _filter.collector = revitron.DB.FilteredElementCollector(revitron.DOC) _filter = evaluator(_filter, paramName, item.strip(), invert) filters.append(_filter) if len(filters): self.collector = filters[0].collector if len(filters) > 1: for i in range(1, len(filters)): if not invert: self.collector.UnionWith(filters[i].collector) else: self.collector.IntersectWith(filters[i].collector) return self
[docs] def byStringContains(self, paramName, value, invert=False): """ Filters the collection by a string contained in a parameter. Example:: fltr = revitron.Filter() fltr = fltr.byStringContains('param', 'value').noTypes() ids = fltr.getElementIds() Args: paramName (string): The parameter name value (string): The searched string invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( revitron.DB.FilterStringRule, paramName, value, revitron.DB.FilterStringContains(), invert ) return self
[docs] def byStringContainsOneInCsv(self, paramName, csv, invert=False): """ Filters the collection by testing whether a string contains at lease one ot the items in a CSV list. .. note:: that by setting ``invert`` to ``True``, all elements that match one of the items will be removed from the collection. Example:: fltr = revitron.Filter() fltr = fltr.byStringContainsOneInCsv('Family', 'some, words', False) fltr = fltr.noTypes() elements = fltr.getElements() Args: paramName (string): The name of the parameter csv (string): A comma separated list of items invert (bool, optional): Inverts the filter. Defaults to False. Returns: object: The Filter instance """ return self.byOneInCsv('byStringContains', paramName, csv, invert)
[docs] def byStringEquals(self, paramName, value, invert=False): """ Filters the collection by a string that equals a parameter value. Example:: fltr = revitron.Filter() ids = fltr.byStringEquals('param', 'value').noTypes().getElementIds() Args: paramName (string): The parameter name value (string): The searched string invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( revitron.DB.FilterStringRule, paramName, value, revitron.DB.FilterStringEquals(), invert ) return self
[docs] def byStringEqualsOneInCsv(self, paramName, csv, invert=False): """ Filters the collection by testing whether a string equals at lease one ot the items in a CSV list. .. note:: that by setting ``invert`` to ``True``, all elements that match one of the items will be removed from the collection. Example:: fltr = revitron.Filter() fltr = fltr.byStringEqualsOneInCsv('Family', 'some, words', False) fltr = fltr.noTypes() elements = fltr.getElements() Args: paramName (string): The name of the parameter csv (string): A comma separated list of items invert (bool, optional): Inverts the filter. Defaults to False. Returns: object: The Filter instance """ return self.byOneInCsv('byStringEquals', paramName, csv, invert)
[docs] def byStringBeginsWith(self, paramName, value, invert=False): """ Filters the collection by a string at the beginning of a parameter value. Example:: fltr = revitron.Filter() fltr = fltr.byStringBeginsWith('param', 'value').noTypes() ids = fltr.getElementIds() Args: paramName (string): The parameter name value (string): The searched string invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( revitron.DB.FilterStringRule, paramName, value, revitron.DB.FilterStringBeginsWith(), invert ) return self
[docs] def byStringEndsWith(self, paramName, value, invert=False): """ Filters the collection by a string at the end of a parameter. Args: paramName (string): The parameter name value (string): The searched string invert (boolean): Inverts the filter Returns: object: The collector """ import revitron self._applyFilter( revitron.DB.FilterStringRule, paramName, value, revitron.DB.FilterStringEndsWith(), invert ) return self
[docs] def getElements(self): """ Get the collection as elements. Returns: list: The list of excluded elements """ try: return self.collector.ToElements() except: self._all() return self.collector.ToElements()
[docs] def getElementIds(self): """ Get the collection as element IDs. Returns: list: The list of excluded element IDs """ try: return self.collector.ToElementIds() except: self._all() return self.collector.ToElementIds()
[docs] def noTypes(self): """ Removes all types from collection. Returns: object: The Filter instance """ self.collector = self.collector.WhereElementIsNotElementType() return self
[docs] def onlyTypes(self): """ Reduce to collection to types only. Returns: object: The Filter instance """ self.collector = self.collector.WhereElementIsElementType() return self