mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-19 03:33:48 +00:00
110 lines
4.9 KiB
Python
110 lines
4.9 KiB
Python
# IfcPatch - IFC patching utiliy
|
|
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
|
#
|
|
# This file is part of IfcPatch.
|
|
#
|
|
# IfcPatch is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# IfcPatch is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import ifcopenshell
|
|
import ifcopenshell.util.pset
|
|
import ifcopenshell.util.element
|
|
|
|
|
|
class Patcher:
|
|
def __init__(self, src, file, logger, property_name=None, quantity_name=None):
|
|
"""Converts a property to a standardised quantity
|
|
|
|
IFC can store arbitrary key value metadata associated with a elements
|
|
known as properties and quantities. The difference between the two is
|
|
that quantities specifically measure physical dimensions, and can be
|
|
used parametrically. A collection of standardised quantities have also
|
|
been published by buildingSMART. Using standardised quantities can help
|
|
automate BIM workflows, instead of arbitrary properties.
|
|
|
|
Sometimes, proprietary BIM software incorrectly stores quantities and
|
|
properties. This patch lets you convert these incorrect properties to
|
|
standardised quantities instead.
|
|
|
|
The existing property will not be removed.
|
|
|
|
:param property_name: The name of the property to convert into a
|
|
quantity. The name of the property set is not considered.
|
|
:type property_name: str
|
|
:param quantity_name: The name of the quantity that this property should
|
|
be stored in. This should be a standard name that is one of the
|
|
quantity names of a buildingSMART quantity template. For example, it
|
|
may be "NetSideArea" for walls, which exists in
|
|
Qto_WallBaseQuantities. The quantity set name will be based on the
|
|
standard buildingSMART quantity template.
|
|
:type quantity_name: str
|
|
|
|
Example:
|
|
|
|
.. code:: python
|
|
|
|
# Any property named "Area" is converted to a quantity named
|
|
# "NetSideArea", if that standardised quantity exists.
|
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertPropertiesToQuantities", "arguments": ["Area", "NetSideArea"]})
|
|
"""
|
|
self.src = src
|
|
self.file = file
|
|
self.logger = logger
|
|
self.source_property_name = property_name
|
|
self.destination_quantity_name = quantity_name
|
|
|
|
def patch(self):
|
|
self.qto_template_cache = {}
|
|
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
|
|
|
for product in self.file.by_type("IfcTypeProduct"):
|
|
self.process_product(product, product.HasPropertySets or [])
|
|
|
|
for product in self.file.by_type("IfcProduct"):
|
|
self.process_product(
|
|
product,
|
|
[r.RelatingPropertyDefinition for r in product.IsDefinedBy if r.is_a("IfcRelDefinesByProperties")],
|
|
)
|
|
|
|
def process_product(self, product, definitions):
|
|
value = None
|
|
has_quantity = False
|
|
qtos = {}
|
|
for definition in definitions or []:
|
|
if definition.is_a("IfcPropertySet"):
|
|
for prop in definition.HasProperties:
|
|
if prop.is_a("IfcPropertySingleValue") and prop.Name == self.source_property_name:
|
|
value = prop.NominalValue.wrappedValue if prop.NominalValue else None
|
|
elif definition.is_a("IfcElementQuantity"):
|
|
qtos[definition.Name] = definition
|
|
for quantity in definition.Quantities:
|
|
if quantity.is_a("IfcPhysicalSimpleQuantity") and quantity.Name == self.destination_quantity_name:
|
|
has_quantity = True
|
|
|
|
if value and not has_quantity:
|
|
qto_name = self.get_qto_name(product.is_a())
|
|
qto = qtos.get(qto_name, ifcopenshell.api.run("pset.add_qto", self.file, product=product, Name=qto_name))
|
|
ifcopenshell.api.run(
|
|
"pset.edit_qto", self.file, qto=qto, Properties={self.destination_quantity_name: value}
|
|
)
|
|
|
|
def get_qto_name(self, ifc_class):
|
|
for template in self.get_qto_templates(ifc_class):
|
|
if self.destination_quantity_name in [t.Name for t in template.HasPropertyTemplates]:
|
|
return template.Name
|
|
|
|
def get_qto_templates(self, ifc_class):
|
|
if ifc_class not in self.qto_template_cache:
|
|
self.qto_template_cache[ifc_class] = self.psetqto.get_applicable(ifc_class, qto_only=True)
|
|
return self.qto_template_cache[ifc_class]
|