mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Merge remote-tracking branch 'origin/v0.7.0' into v08attempt1
This commit is contained in:
@@ -87,6 +87,36 @@ ARGUMENTS_DEPRECATION = {
|
||||
"material.unassign_material": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"classification.add_reference": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"classification.remove_reference": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"library.assign_reference": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"library.unassign_reference": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"document.assign_document": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"document.unassign_document": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"spatial.reference_structure": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"spatial.dereference_structure": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"constraint.assign_constraint": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
"constraint.unassign_constraint": partial(
|
||||
batching_argument_deprecation, prev_argument="product", new_argument="products"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@ class Usecase:
|
||||
self.settings["product"].PredefinedType = "USERDEFINED"
|
||||
elif hasattr(self.settings["product"], "ObjectType"):
|
||||
relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
|
||||
if relating_type and relating_type.PredefinedType != "NOTDEFINED":
|
||||
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
|
||||
if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
|
||||
self.settings["product"].ObjectType = None
|
||||
self.settings["product"].PredefinedType = None
|
||||
elif (
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.date
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, classification=None):
|
||||
def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]):
|
||||
"""Adds a new classification system to the project
|
||||
|
||||
External classification systems such as Uniclass or Omniclass are
|
||||
@@ -81,7 +82,7 @@ class Usecase:
|
||||
"classification": classification,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
def execute(self) -> ifcopenshell.entity_instance:
|
||||
if isinstance(self.settings["classification"], str):
|
||||
classification = self.file.createIfcClassification(Name=self.settings["classification"])
|
||||
self.relate_to_project(classification)
|
||||
|
||||
@@ -17,12 +17,24 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.schema
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, reference=None, identification=None, name=None, classification=None, is_lightweight=True):
|
||||
"""Adds a new classification reference and assigns it to a product
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
reference: Optional[ifcopenshell.entity_instance] = None,
|
||||
identification: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
classification: Optional[ifcopenshell.entity_instance] = None,
|
||||
is_lightweight=True,
|
||||
):
|
||||
"""Adds a new classification reference and assigns it to the list of products
|
||||
|
||||
A classification reference is a single entry such as "Pr_12_23_34" that
|
||||
is part of an external classification system (such as Uniclass or
|
||||
@@ -33,7 +45,7 @@ class Usecase:
|
||||
resources such as profiles, documents, libraries, and so on.
|
||||
|
||||
Classification references can be added in two ways. Option 1) specify a
|
||||
custom arbitrary reference, where you have the manually specify the
|
||||
custom arbitrary reference, where you have to manually specify the
|
||||
identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
|
||||
Option 2) add a reference from an IFC classification library. The latter
|
||||
is preferred if you are using a common classification system such as
|
||||
@@ -52,13 +64,13 @@ class Usecase:
|
||||
assigned to both the type and an occurrence, then the assignment at the
|
||||
occurrence will override the type classification.
|
||||
|
||||
:param product: The IFC object, property, or resource you want to
|
||||
:param product: The list of IFC objects, properties, or resources you want to
|
||||
associate the classification reference to.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param reference: The classification reference entity taken from an
|
||||
IFC classification library. If you supply this parameter, you will
|
||||
use option 2.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance, optional
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance, optional
|
||||
:param identification: If you choose option 1 and do not specify a
|
||||
reference, you may manually specify an identification code. The code
|
||||
is typically a short identifier and may have punctuation to separate
|
||||
@@ -70,7 +82,7 @@ class Usecase:
|
||||
:param classification: The IfcClassification entity in your IFC model
|
||||
(not the library, if you are doing option 2) that the reference is
|
||||
part of.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:type classification: ifcopenshell.entity_instance.entity_instance
|
||||
:param is_lightweight: If you are doing option 2, choose whether or not
|
||||
to only add that particular reference (lighweight) or also add all
|
||||
of its parent references in the classification hierarchy (not
|
||||
@@ -81,8 +93,12 @@ class Usecase:
|
||||
is generally unnecessary. Using lightweight classifications are
|
||||
recommended and is the default.
|
||||
:type is_lightweight: bool, optional
|
||||
|
||||
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
|
||||
|
||||
:return: The newly added IfcClassificationReference
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||
or `None` if `products` was empty list.
|
||||
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -93,7 +109,7 @@ class Usecase:
|
||||
classification = ifcopenshell.api.run("classification.add_classification",
|
||||
model, classification="MyCustomClassification")
|
||||
ifcopenshell.api.run("classification.add_reference", model,
|
||||
product=wall_type, classification=classification,
|
||||
products=[wall_type], classification=classification,
|
||||
identification="W_01", name="Interior Walls")
|
||||
|
||||
# Option 2: adding a popular classification from a library
|
||||
@@ -104,12 +120,12 @@ class Usecase:
|
||||
reference = [r for r in library.by_type("IfcClassificationReference")
|
||||
if r.Identification == "XYZ"][0]
|
||||
ifcopenshell.api.run("classification.add_reference", model,
|
||||
product=wall_type, classification=classification,
|
||||
products=[wall_type], classification=classification,
|
||||
reference=reference)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"reference": reference,
|
||||
"identification": identification,
|
||||
"name": name,
|
||||
@@ -117,8 +133,27 @@ class Usecase:
|
||||
"is_lightweight": is_lightweight,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
self.is_rooted = self.settings["product"].is_a("IfcRoot")
|
||||
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not self.settings["products"]:
|
||||
return
|
||||
|
||||
if self.settings["reference"]:
|
||||
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||
if set(self.settings["products"]).issubset(referenced):
|
||||
# nothing to do, all elements already have this reference assigned
|
||||
return self.settings["reference"]
|
||||
|
||||
self.rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||
self.non_rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||
for product in self.settings["products"]:
|
||||
if product.is_a("IfcRoot"):
|
||||
self.rooted_products.add(product)
|
||||
else:
|
||||
self.non_rooted_products.add(product)
|
||||
|
||||
if self.non_rooted_products and self.file.schema == "IFC2X3":
|
||||
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {self.non_rooted_products}.")
|
||||
|
||||
if self.settings["reference"]:
|
||||
return self.add_from_library()
|
||||
return self.add_from_identification()
|
||||
@@ -134,14 +169,10 @@ class Usecase:
|
||||
else:
|
||||
reference.Identification = self.settings["identification"]
|
||||
|
||||
relationship = self.get_existing_relationship(reference)
|
||||
if relationship:
|
||||
self.add_to_existing_relationship(relationship)
|
||||
else:
|
||||
self.add_new_relationship(reference)
|
||||
self.update_relationships(reference)
|
||||
return reference
|
||||
|
||||
def add_from_library(self):
|
||||
def add_from_library(self) -> ifcopenshell.entity_instance:
|
||||
if hasattr(self.settings["reference"], "ItemReference"):
|
||||
identification = self.settings["reference"].ItemReference # IFC2X3
|
||||
else:
|
||||
@@ -155,8 +186,9 @@ class Usecase:
|
||||
old_referenced_source = self.settings["reference"].ReferencedSource
|
||||
self.settings["reference"].ReferencedSource = None
|
||||
else:
|
||||
classification_name = self.settings["classification"].Name
|
||||
existing_classification = [
|
||||
c for c in self.file.by_type("IfcClassification") if c.Name == self.settings["classification"].Name
|
||||
c for c in self.file.by_type("IfcClassification") if c.Name == classification_name
|
||||
]
|
||||
|
||||
reference = migrator.migrate(self.settings["reference"], self.file)
|
||||
@@ -174,15 +206,10 @@ class Usecase:
|
||||
for element in to_delete:
|
||||
self.file.remove(element)
|
||||
|
||||
relationship = self.get_existing_relationship(reference)
|
||||
if relationship:
|
||||
self.add_to_existing_relationship(relationship)
|
||||
else:
|
||||
self.add_new_relationship(reference)
|
||||
|
||||
self.update_relationships(reference)
|
||||
return reference
|
||||
|
||||
def get_existing_reference(self, identification):
|
||||
def get_existing_reference(self, identification: Optional[str] = None) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for reference in self.file.by_type("IfcClassificationReference"):
|
||||
if self.file.schema == "IFC2X3":
|
||||
if reference.ItemReference == identification:
|
||||
@@ -191,39 +218,39 @@ class Usecase:
|
||||
if reference.Identification == identification:
|
||||
return reference
|
||||
|
||||
def add_new_relationship(self, reference):
|
||||
if self.is_rooted:
|
||||
self.file.create_entity(
|
||||
"IfcRelAssociatesClassification",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatedObjects=[self.settings["product"]],
|
||||
RelatingClassification=reference,
|
||||
)
|
||||
else:
|
||||
self.file.create_entity(
|
||||
"IfcExternalReferenceRelationship",
|
||||
RelatingReference=reference,
|
||||
RelatedResourceObjects=[self.settings["product"]],
|
||||
)
|
||||
|
||||
def add_to_existing_relationship(self, rel):
|
||||
if self.is_rooted:
|
||||
related_objects = set(rel.RelatedObjects)
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
else:
|
||||
related_objects = set(rel.RelatedResourceObjects)
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedResourceObjects = list(related_objects)
|
||||
|
||||
def get_existing_relationship(self, reference):
|
||||
if self.is_rooted:
|
||||
def update_relationships(self, reference: ifcopenshell.entity_instance) -> None:
|
||||
root_rel, non_root_rel = None, None
|
||||
if self.rooted_products:
|
||||
if self.file.schema == "IFC2X3":
|
||||
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
||||
if rel.RelatingClassification == reference:
|
||||
return rel
|
||||
elif reference.ClassificationRefForObjects:
|
||||
return reference.ClassificationRefForObjects[0]
|
||||
elif self.file.schema != "IFC2X3":
|
||||
if reference.ExternalReferenceForResources:
|
||||
return reference.ExternalReferenceForResources[0]
|
||||
root_rel = rel
|
||||
break
|
||||
else:
|
||||
root_rel = next(iter(reference.ClassificationRefForObjects), None)
|
||||
|
||||
if root_rel:
|
||||
related_objects = set(root_rel.RelatedObjects) | self.rooted_products
|
||||
root_rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": root_rel})
|
||||
else:
|
||||
self.file.create_entity(
|
||||
"IfcRelAssociatesClassification",
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatedObjects=list(self.rooted_products),
|
||||
RelatingClassification=reference,
|
||||
)
|
||||
|
||||
if self.non_rooted_products:
|
||||
# NOTE: Only Ifc4+. Ifc2x3 is already handled by raising TypeError
|
||||
non_root_rel = next(iter(reference.ExternalReferenceForResources), None)
|
||||
if non_root_rel:
|
||||
related_objects = set(non_root_rel.RelatedResourceObjects) | self.non_rooted_products
|
||||
non_root_rel.RelatedResourceObjects = list(related_objects)
|
||||
else:
|
||||
self.file.create_entity(
|
||||
"IfcExternalReferenceRelationship",
|
||||
RelatingReference=reference,
|
||||
RelatedResourceObjects=list(self.non_rooted_products),
|
||||
)
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, reference=None, product=None):
|
||||
"""Removes a classification reference from a product
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
reference: ifcopenshell.entity_instance,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
):
|
||||
"""Removes a classification reference from the list of products
|
||||
|
||||
If the classification reference is no longer associated to any products,
|
||||
the classification reference itself is also removed.
|
||||
@@ -30,9 +36,12 @@ class Usecase:
|
||||
:param reference: The IfcClassificationReference entity of the
|
||||
relationship you want to remove.
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||
:param product: The object entity of the relationship you want to
|
||||
:param product: The list fo object entities of the relationship you want to
|
||||
remove.
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||
|
||||
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
|
||||
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
@@ -44,42 +53,75 @@ class Usecase:
|
||||
classification = ifcopenshell.api.run("classification.add_classification",
|
||||
model, classification="MyCustomClassification")
|
||||
reference = ifcopenshell.api.run("classification.add_reference", model,
|
||||
product=wall_type, classification=classification,
|
||||
products=[wall_type], classification=classification,
|
||||
identification="W_01", name="Interior Walls")
|
||||
ifcopenshell.api.run("classification.remove_reference", model,
|
||||
reference=reference, product=wall_type)
|
||||
reference=reference, products=[wall_type])
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {"reference": reference, "product": product}
|
||||
self.settings = {"reference": reference, "products": products}
|
||||
|
||||
def execute(self):
|
||||
if self.settings["product"].is_a("IfcRoot"):
|
||||
for rel in self.file.by_type("IfcRelAssociatesClassification"):
|
||||
if rel.RelatingClassification == self.settings["reference"] and rel.RelatedObjects:
|
||||
if self.settings["product"] in rel.RelatedObjects:
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(self.settings["product"])
|
||||
if len(related_objects):
|
||||
rel.RelatedObjects = related_objects
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
else:
|
||||
for rel in self.file.by_type("IfcExternalReferenceRelationship"):
|
||||
if rel.RelatingReference == self.settings["reference"] and rel.RelatedResourceObjects:
|
||||
if self.settings["product"] in rel.RelatedResourceObjects:
|
||||
related_objects = list(rel.RelatedResourceObjects)
|
||||
related_objects.remove(self.settings["product"])
|
||||
if len(related_objects):
|
||||
rel.RelatedResourceObjects = related_objects
|
||||
else:
|
||||
self.file.remove(rel)
|
||||
def execute(self) -> None:
|
||||
is_ifc2x3 = self.file.schema == "IFC2X3"
|
||||
products = set(self.settings["products"])
|
||||
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||
products -= products.difference(referenced)
|
||||
|
||||
# all products are already unassigned from a reference
|
||||
if not products:
|
||||
return
|
||||
|
||||
rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||
non_rooted_products: set[ifcopenshell.entity_instance] = set()
|
||||
for product in self.settings["products"]:
|
||||
if product.is_a("IfcRoot"):
|
||||
rooted_products.add(product)
|
||||
else:
|
||||
non_rooted_products.add(product)
|
||||
|
||||
if non_rooted_products and is_ifc2x3:
|
||||
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.")
|
||||
|
||||
if rooted_products:
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
for product in rooted_products:
|
||||
reference_rels.update(product.HasAssociations)
|
||||
|
||||
reference_rels = {
|
||||
rel
|
||||
for rel in reference_rels
|
||||
if rel.is_a("IfcRelAssociatesClassification")
|
||||
and rel.RelatingClassification == self.settings["reference"]
|
||||
}
|
||||
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedObjects) - rooted_products
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
if non_rooted_products:
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
for product in non_rooted_products:
|
||||
rels = getattr(product, "HasExternalReferences", None)
|
||||
if rels is None:
|
||||
rels = getattr(product, "HasExternalReference", [])
|
||||
reference_rels.update(rels)
|
||||
|
||||
reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]}
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
|
||||
if related_objects:
|
||||
rel.RelatedResourceObjects = list(related_objects)
|
||||
else:
|
||||
self.file.remove(rel)
|
||||
|
||||
# TODO: we only handle lightweight classifications here
|
||||
if (
|
||||
not self.settings["reference"].ClassificationRefForObjects
|
||||
and not self.settings["reference"].ExternalReferenceForResources
|
||||
):
|
||||
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||
if not referenced_elements:
|
||||
self.file.remove(self.settings["reference"])
|
||||
|
||||
@@ -17,11 +17,18 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, constraint=None):
|
||||
"""Assigns a constraint to a product
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
constraint: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Assigns a constraint to a list of products
|
||||
|
||||
This assigns a relationship between a product and a constraint, so that
|
||||
when a product's properties and quantities do not match the requirements
|
||||
@@ -31,36 +38,58 @@ class Usecase:
|
||||
constraints are inherited from the type. This way, it is not necessary
|
||||
to create lots of constraint assignments.
|
||||
|
||||
:param product: The product the constraint applies to. This is anything
|
||||
:param products: The list of products the constraint applies to. This is anything
|
||||
which can have properties or quantities.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param constraint: The IfcObjective constraint
|
||||
:type constraint: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The new or updated IfcRelAssociatesConstraint relationship
|
||||
or `None` if `products` was an empty list.
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"constraint": constraint,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
rel = self.get_constraint_rel()
|
||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
return rel
|
||||
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||
products = set(self.settings["products"])
|
||||
if not products:
|
||||
return
|
||||
|
||||
self.constraint = self.settings["constraint"]
|
||||
|
||||
rels = self.get_constraint_rels()
|
||||
related_objects = set()
|
||||
for rel in rels:
|
||||
related_objects.update(rel.RelatedObjects)
|
||||
|
||||
products_to_assign = products - related_objects
|
||||
if not products_to_assign:
|
||||
return rels[0]
|
||||
|
||||
rel = next(iter(rels), None)
|
||||
|
||||
if rel:
|
||||
related_objects = set(rel.RelatedObjects) | products_to_assign
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
return rel
|
||||
|
||||
def get_constraint_rel(self):
|
||||
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
|
||||
if rel.RelatingConstraint == self.settings["constraint"]:
|
||||
return rel
|
||||
return self.file.create_entity(
|
||||
"IfcRelAssociatesConstraint",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
# TODO: owner history
|
||||
"RelatingConstraint": self.settings["constraint"],
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatingConstraint": self.constraint,
|
||||
"RelatedObjects": list(products_to_assign),
|
||||
}
|
||||
)
|
||||
|
||||
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
|
||||
rels = []
|
||||
for rel in self.file.get_inverse(self.constraint):
|
||||
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||
rels.append(rel)
|
||||
return rels
|
||||
|
||||
@@ -17,18 +17,24 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, constraint=None):
|
||||
"""Unassigns a constraint to a product
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
constraint: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Unassigns a constraint from a list of products
|
||||
|
||||
The constraint will not be deleted and is available to be assigned to
|
||||
other products.
|
||||
|
||||
:param product: The product the constraint applies to.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:param products: The list of products the constraint applies to.
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param constraint: The IfcObjective constraint
|
||||
:type constraint: ifcopenshell.entity_instance.entity_instance
|
||||
:return: None
|
||||
@@ -36,14 +42,42 @@ class Usecase:
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"constraint": constraint,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesConstraint") and rel.RelatingConstraint == self.settings["constraint"]:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
products = set(self.settings["products"])
|
||||
if not products:
|
||||
return
|
||||
|
||||
self.constraint = self.settings["constraint"]
|
||||
rels = self.get_constraint_rels()
|
||||
related_objects = set()
|
||||
for rel in rels:
|
||||
related_objects.update(rel.RelatedObjects)
|
||||
|
||||
if not related_objects.intersection(products):
|
||||
return
|
||||
|
||||
for rel in rels:
|
||||
related_objects = set(rel.RelatedObjects)
|
||||
if not related_objects.intersection(products):
|
||||
continue
|
||||
related_objects -= products
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
continue
|
||||
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
|
||||
rels = []
|
||||
for rel in self.file.get_inverse(self.constraint):
|
||||
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||
rels.append(rel)
|
||||
return rels
|
||||
|
||||
@@ -69,10 +69,12 @@ class Usecase:
|
||||
|
||||
def execute(self) -> ifcopenshell.entity_instance:
|
||||
if self.file.schema == "IFC2X3":
|
||||
reference = self.file.create_entity("IfcDocumentReference")
|
||||
reference = self.file.create_entity("IfcDocumentReference", ItemReference="X")
|
||||
if self.settings["information"]:
|
||||
references = list(self.settings["information"].DocumentReferences or [])
|
||||
references.append(reference)
|
||||
self.settings["information"].DocumentReferences = references
|
||||
return reference
|
||||
return self.file.create_entity("IfcDocumentReference", ReferencedDocument=self.settings["information"])
|
||||
return self.file.create_entity(
|
||||
"IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X"
|
||||
)
|
||||
|
||||
@@ -17,11 +17,19 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, document=None):
|
||||
"""Assigns a document to a product
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
document: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Assigns a document to a list of products
|
||||
|
||||
An object may be assigned to zero, one, or multiple documents. Almost
|
||||
any object or property may be assigned to a document, though typically
|
||||
@@ -32,14 +40,16 @@ class Usecase:
|
||||
consistent with other external relationships (such as classification
|
||||
systems or libraries).
|
||||
|
||||
:param product: The object to associate the document to. This could be
|
||||
:param product: The list of objects to associate the document to. This could be
|
||||
almost any sensible object in IFC.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param document: The IfcDocumentReference to associate to, or
|
||||
alternatively an IfcDocumentInformation, though this is not
|
||||
recommended.
|
||||
:type document: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The IfcRelAssociatesDocument relationship
|
||||
or `None` if `products` was an empty list or all products were
|
||||
already assigned to the `document`.
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||
|
||||
Example:
|
||||
@@ -54,42 +64,51 @@ class Usecase:
|
||||
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
||||
|
||||
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
||||
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
|
||||
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"document": document,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
rel = self.get_document_rel()
|
||||
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
# NOTE: reuses code from `library.assign_reference`
|
||||
|
||||
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"])
|
||||
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
|
||||
products = products - referenced_elements
|
||||
|
||||
if not products:
|
||||
return
|
||||
|
||||
def get_document_rel(self):
|
||||
if self.file.schema == "IFC2X3":
|
||||
for rel in self.file.by_type("IfcRelAssociatesDocument"):
|
||||
if rel.RelatingDocument == self.settings["document"]:
|
||||
return rel
|
||||
rel = next(
|
||||
(
|
||||
r
|
||||
for r in self.file.by_type("IfcRelAssociatesDocument")
|
||||
if r.RelatingDocument == self.settings["document"]
|
||||
),
|
||||
None,
|
||||
)
|
||||
else:
|
||||
if (
|
||||
hasattr(self.settings["document"], "DocumentRefForObjects")
|
||||
and self.settings["document"].DocumentRefForObjects
|
||||
):
|
||||
return self.settings["document"].DocumentRefForObjects[0]
|
||||
elif (
|
||||
hasattr(self.settings["document"], "DocumentInfoForObjects")
|
||||
and self.settings["document"].DocumentInfoForObjects
|
||||
):
|
||||
return self.settings["document"].DocumentInfoForObjects[0]
|
||||
ifc_class = self.settings["document"].is_a()
|
||||
if ifc_class == "IfcDocumentReference":
|
||||
rel = next(iter(self.settings["document"].DocumentRefForObjects), None)
|
||||
elif ifc_class == "IfcDocumentInformation":
|
||||
rel = next(iter(self.settings["document"].DocumentInfoForObjects), None)
|
||||
|
||||
return self.file.create_entity(
|
||||
"IfcRelAssociatesDocument",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatingDocument": self.settings["document"],
|
||||
}
|
||||
)
|
||||
if not rel:
|
||||
return self.file.create_entity(
|
||||
"IfcRelAssociatesDocument",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
RelatedObjects=list(products),
|
||||
RelatingDocument=self.settings["document"],
|
||||
)
|
||||
|
||||
related_objects = set(rel.RelatedObjects) | products
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
|
||||
return rel
|
||||
|
||||
@@ -17,16 +17,22 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, document=None):
|
||||
"""Unassigns a document and a product association
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
document: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Unassigns a document and an association to the list of products
|
||||
|
||||
:param product: The object that the document reference or information is
|
||||
:param product: The list of objects that the document reference or information is
|
||||
related to.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:type product: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param document: The IfcDocumentReference (typically) or in rare cases
|
||||
the IfcDocumentInformation that is associated with the product
|
||||
:type document: ifcopenshell.entity_instance.entity_instance
|
||||
@@ -45,24 +51,39 @@ class Usecase:
|
||||
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
|
||||
|
||||
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
|
||||
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
|
||||
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
|
||||
|
||||
# Now let's change our mind and remove the association
|
||||
ifcopenshell.api.run("document.unassign_document", model, product=storey, document=reference)
|
||||
ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"document": document,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]:
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
else:
|
||||
rel.RelatedObjects = [o for o in rel.RelatedObjects if o != self.settings["product"]]
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
# NOTE: reuses code from `library.un assign_reference`
|
||||
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
products = set(self.settings["products"])
|
||||
for product in products:
|
||||
reference_rels.update(product.HasAssociations)
|
||||
|
||||
reference_rels = {
|
||||
rel
|
||||
for rel in reference_rels
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]
|
||||
}
|
||||
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedObjects) - products
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
@@ -26,7 +26,7 @@ class Usecase:
|
||||
surveyor, and a third-party digital engineer with expertise in IFC to
|
||||
moderate. For more information, read the BlenderBIM Add-on documentation
|
||||
for Georeferencing:
|
||||
https://blenderbim.org/docs/users/georeferencing.html
|
||||
https://docs.blenderbim.org/users/georeferencing.html
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMapConversion, consult the IFC documentation.
|
||||
|
||||
@@ -17,23 +17,30 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, reference=None):
|
||||
"""Associates a product with a library reference
|
||||
def __init__(
|
||||
self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
|
||||
):
|
||||
"""Associates a list products with a library reference
|
||||
|
||||
A product may be associated with zero, one, or many references across
|
||||
multiple libraries. See ifcopenshell.api.library.add_reference for more
|
||||
detail about how references work.
|
||||
|
||||
:param product: The IfcProduct you want to associate with the reference
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:param products: The list of IfcProducts you want to associate with the reference
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param reference: The IfcLibraryReference you want the product to be
|
||||
associated with.
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The IfcRelAssociatesLibrary relationship entity
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||
or `None` if `products` was an empty list or all products were
|
||||
already assigned to the `reference`.
|
||||
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -51,40 +58,46 @@ class Usecase:
|
||||
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
||||
|
||||
# And now assign the IFC model's AHU with its Brickschema counterpart
|
||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
|
||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"reference": reference,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
|
||||
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
|
||||
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
|
||||
products = products - referenced_elements
|
||||
|
||||
if not products:
|
||||
return
|
||||
|
||||
if self.file.schema == "IFC2X3":
|
||||
rels = self.get_ifc2x3_rels()
|
||||
rel = next(
|
||||
(
|
||||
r
|
||||
for r in self.file.by_type("IfcRelAssociatesLibrary")
|
||||
if r.RelatingLibrary == self.settings["reference"]
|
||||
),
|
||||
None,
|
||||
)
|
||||
else:
|
||||
rels = self.settings["reference"].LibraryRefForObjects
|
||||
if not rels:
|
||||
rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
|
||||
|
||||
if not rel:
|
||||
return self.file.create_entity(
|
||||
"IfcRelAssociatesLibrary",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
RelatedObjects=[self.settings["product"]],
|
||||
RelatedObjects=list(products),
|
||||
RelatingLibrary=self.settings["reference"],
|
||||
)
|
||||
|
||||
for rel in rels:
|
||||
if self.settings["product"] in rel.RelatedObjects:
|
||||
return rel
|
||||
|
||||
rel = rels[0]
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.append(self.settings["product"])
|
||||
rel.RelatedObjects = related_objects
|
||||
related_objects = set(rel.RelatedObjects) | products
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
|
||||
return rel
|
||||
|
||||
def get_ifc2x3_rels(self):
|
||||
return [
|
||||
r for r in self.file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == self.settings["reference"]
|
||||
]
|
||||
|
||||
@@ -18,18 +18,24 @@
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, reference=None, product=None):
|
||||
"""Unassigns a product from a reference
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
reference: ifcopenshell.entity_instance,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
):
|
||||
"""Unassigns a product of products from a reference
|
||||
|
||||
If the product isn't assigned to the reference, nothing will happen.
|
||||
|
||||
:param reference: The IfcLibraryReference to unassign from
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||
:param product: A IfcProduct element to unassign from the reference
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:param products: A list of IfcProduct elements to unassign from the reference
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
@@ -49,27 +55,36 @@ class Usecase:
|
||||
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
|
||||
|
||||
# And now assign the IFC model's AHU with its Brickschema counterpart
|
||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
|
||||
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
|
||||
|
||||
# Let's change our mind and unassign it.
|
||||
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, product=ahu)
|
||||
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
|
||||
"""
|
||||
|
||||
self.file = file
|
||||
self.settings = {"reference": reference, "product": product}
|
||||
self.settings = {"reference": reference, "products": products}
|
||||
|
||||
def execute(self):
|
||||
rels = self.settings["reference"].LibraryRefForObjects
|
||||
if not rels:
|
||||
return
|
||||
for rel in rels:
|
||||
if self.settings["product"] in rel.RelatedObjects:
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
continue
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(self.settings["product"])
|
||||
rel.RelatedObjects = related_objects
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
products = set(self.settings["products"])
|
||||
for product in products:
|
||||
reference_rels.update(product.HasAssociations)
|
||||
|
||||
reference_rels = {
|
||||
rel
|
||||
for rel in reference_rels
|
||||
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"]
|
||||
}
|
||||
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedObjects) - products
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
@@ -38,7 +38,7 @@ def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instanc
|
||||
if not app and ifc.schema == "IFC2X3":
|
||||
raise Exception(
|
||||
"Please create an application to continue. See the owner.create_owner_history docs for more info."
|
||||
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
)
|
||||
return (app or [None])[0]
|
||||
|
||||
@@ -58,7 +58,7 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
||||
if not pao and ifc.schema == "IFC2X3":
|
||||
raise Exception(
|
||||
"Please create a user to continue. See the owner.create_owner_history docs for more info."
|
||||
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
)
|
||||
return (pao or [None])[0]
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@ import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, relating_structure=None):
|
||||
"""Dereferences the a product and space
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
relating_structure: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Dereferences a list of products and space
|
||||
|
||||
:param product: The physical IfcElement that exists in the space.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:param products: The list of physical IfcElements that exists in the space.
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param relating_structure: The IfcSpatialStructureElement element, such
|
||||
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
||||
exists in.
|
||||
@@ -59,23 +64,24 @@ class Usecase:
|
||||
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
||||
|
||||
# And referenced in the others
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2)
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3)
|
||||
|
||||
# Actually, it only goes up to storey 2.
|
||||
ifcopenshell.api.run("spatial.dereference_structure", model, product=column, relating_structure=storey3)
|
||||
ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {"product": product, "relating_structure": relating_structure}
|
||||
self.settings = {"products": products, "relating_structure": relating_structure}
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].ReferencedInStructures:
|
||||
if rel.RelatingStructure != self.settings["relating_structure"]:
|
||||
def execute(self) -> None:
|
||||
products = set(self.settings["products"])
|
||||
for rel in self.settings["relating_structure"].ReferencesElements:
|
||||
related_elements = set(rel.RelatedElements)
|
||||
if not related_elements.intersection(products):
|
||||
continue
|
||||
related_elements = list(rel.RelatedElements)
|
||||
related_elements.remove(self.settings["product"])
|
||||
related_elements = related_elements - products
|
||||
if related_elements:
|
||||
rel.RelatedElements = related_elements
|
||||
rel.RelatedElements = list(related_elements)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
|
||||
@@ -18,11 +18,18 @@
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, product=None, relating_structure=None):
|
||||
"""Denote that a product is related to a spatial structure
|
||||
def __init__(
|
||||
self,
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
relating_structure: ifcopenshell.entity_instance,
|
||||
):
|
||||
"""Denote that a list products is related to a list of spatial structures
|
||||
|
||||
This is similar to ifcopenshell.api.spatial.assign_container, except
|
||||
that containment can only occur between a product and a single spatial
|
||||
@@ -39,13 +46,15 @@ class Usecase:
|
||||
Referencing is non-hierarchical, so a door may be referenced in multiple
|
||||
spaces simultaneously.
|
||||
|
||||
:param product: The physical IfcElement that exists in the space.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:param products: The list of physical IfcElements that exists in the space.
|
||||
:type products: list[ifcopenshell.entity_instance.entity_instance]
|
||||
:param relating_structure: The IfcSpatialStructureElement element, such
|
||||
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
||||
exists in.
|
||||
:type relating_structure: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The IfcRelReferencedInSpatialStructure relationship instance
|
||||
:rtype: ifcopenshell.entity_instance.entity_instance
|
||||
or `None` if `products` was an empty list.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -73,37 +82,43 @@ class Usecase:
|
||||
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
|
||||
|
||||
# And referenced in the others
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
|
||||
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
|
||||
ifcopenshell.api.run(
|
||||
"spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3]
|
||||
)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": product,
|
||||
"products": products,
|
||||
"relating_structure": relating_structure,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
referenced_in_structures = self.settings["product"].ReferencedInStructures
|
||||
references_elements = self.settings["relating_structure"].ReferencesElements
|
||||
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
|
||||
structure = self.settings["relating_structure"]
|
||||
products = set(self.settings["products"])
|
||||
|
||||
for rel in referenced_in_structures:
|
||||
if rel.RelatingStructure == self.settings["relating_structure"]:
|
||||
return
|
||||
if not products:
|
||||
return
|
||||
|
||||
if references_elements:
|
||||
related_elements = list(references_elements[0].RelatedElements)
|
||||
related_elements.append(self.settings["product"])
|
||||
references_elements[0].RelatedElements = related_elements
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": references_elements[0]})
|
||||
else:
|
||||
references_elements = self.file.create_entity(
|
||||
referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure)
|
||||
products_to_assign = products - referenced
|
||||
rel = next(iter(structure.ReferencesElements), None)
|
||||
|
||||
if not products_to_assign:
|
||||
return rel
|
||||
|
||||
if rel is None:
|
||||
rel = self.file.create_entity(
|
||||
"IfcRelReferencedInSpatialStructure",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatedElements": [self.settings["product"]],
|
||||
"RelatingStructure": self.settings["relating_structure"],
|
||||
"RelatedElements": list(products_to_assign),
|
||||
"RelatingStructure": structure,
|
||||
}
|
||||
)
|
||||
else:
|
||||
related_elements = set(rel.RelatedElements) | products_to_assign
|
||||
rel.RelatedElements = list(related_elements)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
|
||||
return references_elements
|
||||
return rel
|
||||
|
||||
@@ -30,7 +30,7 @@ import functools
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from typing import Union, Any, Callable, TypeVar
|
||||
from typing import Union, Any, Callable, TypeVar, overload
|
||||
|
||||
from . import ifcopenshell_wrapper
|
||||
from . import settings
|
||||
@@ -317,15 +317,25 @@ class entity_instance(object):
|
||||
|
||||
return self.wrapped_data.to_string(valid_spf)
|
||||
|
||||
def is_a(self, *args) -> Union[str, bool]:
|
||||
@overload
|
||||
def is_a(self) -> str: ...
|
||||
@overload
|
||||
def is_a(self, ifc_class: str) -> bool: ...
|
||||
@overload
|
||||
def is_a(self, with_schema: bool) -> str: ...
|
||||
def is_a(self, *args: Union[str, bool]) -> Union[str, bool]:
|
||||
"""Return the IFC class name of an instance, or checks if an instance belongs to a class.
|
||||
|
||||
The check will also return true if a parent class name is provided.
|
||||
|
||||
:param args: If specified, is a case insensitive IFC class name to check
|
||||
:type args: string
|
||||
or if specified as a boolean then will define whether
|
||||
returned IFC class name should include schema name
|
||||
(e.g. "IFC4.IfcWall" if `True` and "IfcWall" if `False`).
|
||||
If omitted will act as `False`.
|
||||
:type args: Union[str, bool]
|
||||
:returns: Either the name of the class, or a boolean if it passes the check
|
||||
:rtype: string|bool
|
||||
:rtype: Union[str, bool]
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ from typing import Optional
|
||||
def get_references(element: ifcopenshell.entity_instance, should_inherit=True) -> set[ifcopenshell.entity_instance]:
|
||||
results = set()
|
||||
if not element.is_a("IfcRoot"):
|
||||
if hasattr(element, "HasExternalReferences"):
|
||||
return {r.RelatingReference for r in element.HasExternalReferences or []}
|
||||
elif hasattr(element, "HasExternalReference"): # Seriously, IFC?
|
||||
return {r.RelatingReference for r in element.HasExternalReference or []}
|
||||
if (references := getattr(element, "HasExternalReferences", None)) is not None or (
|
||||
references := getattr(element, "HasExternalReference", None)
|
||||
) is not None:
|
||||
return {r.RelatingReference for r in references}
|
||||
if should_inherit:
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type and element_type != element:
|
||||
|
||||
@@ -18,20 +18,59 @@
|
||||
#
|
||||
#
|
||||
|
||||
def get_constraints(product):
|
||||
import ifcopenshell
|
||||
from typing import Union
|
||||
|
||||
|
||||
def get_constraints(product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves the constraints assigned to the `product`.
|
||||
|
||||
:param product: The IFC element.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:return: List of assigned constraints.
|
||||
:rtype: list[ifcopenshell.entity_instance.entity_instance]
|
||||
"""
|
||||
constraints = []
|
||||
for rel in product.HasAssociations or []:
|
||||
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||
constraints.append(rel.RelatingConstraint)
|
||||
return constraints
|
||||
|
||||
def get_metrics(constraint):
|
||||
|
||||
def get_constrained_elements(constraint: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves the elements constrained by a `constraint`.
|
||||
|
||||
:param product: The IFC element.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:return: Set of elements constrained by a `constrant`.
|
||||
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||
"""
|
||||
elements = set()
|
||||
for rel in constraint.file.get_inverse(constraint):
|
||||
if rel.is_a("IfcRelAssociatesConstraint"):
|
||||
elements.update(rel.RelatedObjects)
|
||||
return elements
|
||||
|
||||
|
||||
def get_metrics(constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves the list of nested constraints for a IfcObjective `constraint`.
|
||||
|
||||
:param product: IfcObjective constraint.
|
||||
:type product: ifcopenshell.entity_instance.entity_instance
|
||||
:return: List of nested constraints.
|
||||
:rtype: list[ifcopenshell.entity_instance.entity_instance]
|
||||
"""
|
||||
|
||||
metrics = []
|
||||
for metric in constraint.BenchmarkValues or []:
|
||||
metrics.append(metric)
|
||||
return metrics
|
||||
|
||||
def get_metric_reference(metric, is_deep=True):
|
||||
|
||||
def get_metric_reference(metric: ifcopenshell.entity_instance, is_deep=True):
|
||||
def get_reference_Attribute(ref, path):
|
||||
if ref:
|
||||
if is_deep:
|
||||
@@ -47,7 +86,10 @@ def get_metric_reference(metric, is_deep=True):
|
||||
reference = metric.ReferencePath
|
||||
return get_reference_Attribute(reference, "")
|
||||
|
||||
def get_metric_constraints(resource, attribute):
|
||||
|
||||
def get_metric_constraints(
|
||||
resource: ifcopenshell.entity_instance, attribute
|
||||
) -> Union[list[ifcopenshell.entity_instance], None]:
|
||||
metrics = []
|
||||
for constraint in get_constraints(resource) or []:
|
||||
for metric in get_metrics(constraint) or []:
|
||||
@@ -60,15 +102,14 @@ def get_metric_constraints(resource, attribute):
|
||||
return metrics
|
||||
return None
|
||||
|
||||
def is_hard_constraint(metric):
|
||||
if metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO":
|
||||
return True
|
||||
|
||||
def is_attribute_locked(product, attribute):
|
||||
def is_hard_constraint(metric: ifcopenshell.entity_instance) -> bool:
|
||||
return metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO"
|
||||
|
||||
|
||||
def is_attribute_locked(product: ifcopenshell.entity_instance, attribute) -> bool:
|
||||
is_locked = False
|
||||
metrics = get_metric_constraints(
|
||||
product, attribute
|
||||
)
|
||||
metrics = get_metric_constraints(product, attribute)
|
||||
for metric in metrics or []:
|
||||
if is_hard_constraint(metric):
|
||||
is_locked = True
|
||||
|
||||
@@ -20,16 +20,17 @@ from __future__ import annotations
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
from typing import Any, Callable, Optional, Union, Literal, overload
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
def get_pset(
|
||||
element: ifcopenshell.entity_instance,
|
||||
name: str,
|
||||
prop: Optional[str] = None,
|
||||
psets_only=False,
|
||||
qtos_only=False,
|
||||
should_inherit=True,
|
||||
verbose=False,
|
||||
psets_only: bool = False,
|
||||
qtos_only: bool = False,
|
||||
should_inherit: bool = True,
|
||||
verbose: bool = False,
|
||||
) -> Union[Any, dict[str, Any]]:
|
||||
"""Retrieve a single property set or single property
|
||||
|
||||
@@ -429,8 +430,7 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
|
||||
element = ifcopenshell.by_type("IfcWall")[0]
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
"""
|
||||
element_type = get_type(element)
|
||||
if element_type:
|
||||
if element_type := get_type(element):
|
||||
predefined_type = getattr(element_type, "PredefinedType", None)
|
||||
if predefined_type == "USERDEFINED" or not predefined_type:
|
||||
predefined_type = getattr(element_type, "ElementType", ...)
|
||||
@@ -563,7 +563,9 @@ def get_material(
|
||||
return get_material(relating_type, should_skip_usage)
|
||||
|
||||
|
||||
def get_materials(element: ifcopenshell.entity_instance, should_inherit=True) -> list[ifcopenshell.entity_instance]:
|
||||
def get_materials(
|
||||
element: ifcopenshell.entity_instance, should_inherit: bool = True
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""Gets individual materials of an element
|
||||
|
||||
If the element has a material set, the individual materials of that set are
|
||||
@@ -826,7 +828,7 @@ def get_layers(
|
||||
|
||||
|
||||
def get_container(
|
||||
element: ifcopenshell.entity_instance, should_get_direct=False, ifc_class: Optional[str] = None
|
||||
element: ifcopenshell.entity_instance, should_get_direct: bool = False, ifc_class: Optional[str] = None
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""
|
||||
Retrieves the spatial structure container of an element.
|
||||
@@ -902,6 +904,27 @@ def get_referenced_structures(element: ifcopenshell.entity_instance) -> list[ifc
|
||||
return [r.RelatingStructure for r in getattr(element, "ReferencedInStructures", [])]
|
||||
|
||||
|
||||
def get_structure_referenced_elements(structure: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Retreives a set of elements referenced by a structure
|
||||
|
||||
:param structure: IfcSpatialElement
|
||||
:type element: ifcopenshell.entity_instance.entity_instance
|
||||
:return: A set of referenced elements, IfcSpatialReferenceSelect
|
||||
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
element = file.by_type("IfcBuildingStorey")[0]
|
||||
print(ifcopenshell.util.element.get_structure_referenced_elements(element))
|
||||
"""
|
||||
referenced = set()
|
||||
for rel in structure.ReferencesElements:
|
||||
referenced.update(rel.RelatedElements)
|
||||
return referenced
|
||||
|
||||
|
||||
def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves all subelements of an element based on the spatial decomposition
|
||||
@@ -1092,6 +1115,66 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) -
|
||||
return is_decomposed_by[0].RelatedObjects
|
||||
|
||||
|
||||
ReferenceData = namedtuple("ReferenceData", "inverse_attribute, rel_class, relating_element_attribute")
|
||||
|
||||
# References below are omitted because they do not introduce
|
||||
# any additional referenced objects besides the objects
|
||||
# from their supertype IfcExternalReference
|
||||
# - IfcExternallyDefinedHatchStyle
|
||||
# - IfcExternallyDefinedSurfaceStyle
|
||||
# - IfcExternallyDefinedTextFont
|
||||
|
||||
REFERENCE_TYPES: dict[str, ReferenceData] = {
|
||||
"IfcClassificationReference": ReferenceData(
|
||||
"ClassificationRefForObjects",
|
||||
"IfcRelAssociatesClassification",
|
||||
"RelatingClassification",
|
||||
),
|
||||
"IfcDocumentReference": ReferenceData("DocumentRefForObjects", "IfcRelAssociatesDocument", "RelatingDocument"),
|
||||
"IfcLibraryReference": ReferenceData("LibraryRefForObjects", "IfcRelAssociatesLibrary", "RelatingLibrary"),
|
||||
}
|
||||
|
||||
|
||||
def get_referenced_elements(reference: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Get all elements with assigned `reference`
|
||||
|
||||
:param reference: IfcExternalReference subtype reference
|
||||
:type reference: ifcopenshell.entity_instance.entity_instance
|
||||
:return: The elements with assigned `reference`
|
||||
:rtype: set[ifcopenshell.entity_instance.entity_instance]
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
reference = file.by_type("IfcClassificationReference")[0]
|
||||
elements = ifcopenshell.util.element.get_referenced_elements(reference)
|
||||
"""
|
||||
|
||||
related_objects: set[ifcopenshell.entity_instance] = set()
|
||||
ifc_file = reference.file
|
||||
ifc_class = reference.is_a()
|
||||
|
||||
if ifc_file.schema == "IFC2X3":
|
||||
reference_data = REFERENCE_TYPES.get(ifc_class)
|
||||
if reference_data:
|
||||
for rel in ifc_file.by_type(reference_data.rel_class):
|
||||
if getattr(rel, reference_data.relating_element_attribute) == reference:
|
||||
related_objects.update(rel.RelatedObjects)
|
||||
|
||||
else:
|
||||
# IfcExternalReference
|
||||
for external_rel in reference.ExternalReferenceForResources:
|
||||
related_objects.update(external_rel.RelatedResourceObjects)
|
||||
|
||||
reference_data = REFERENCE_TYPES.get(ifc_class)
|
||||
if reference_data:
|
||||
for rel in getattr(reference, reference_data.inverse_attribute):
|
||||
related_objects.update(rel.RelatedObjects)
|
||||
|
||||
return related_objects
|
||||
|
||||
|
||||
def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None:
|
||||
for i, attribute_value in enumerate(element):
|
||||
if has_element_reference(attribute_value, old):
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import shapely
|
||||
import shapely.ops
|
||||
import numpy as np
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
|
||||
Reference in New Issue
Block a user