mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0
# Conflicts: # src/ifcgeom/IfcGeomTree.h # src/ifcgeom_schema_agnostic/IfcGeomIterator.h # src/ifcgeom_schema_agnostic/IteratorImplementation.cpp # src/ifcgeom_schema_agnostic/IteratorImplementation.h # src/ifcparse/Ifc4x3_rc3.cpp # src/ifcparse/Ifc4x3_rc3.h # src/serializers/SvgSerializer.cpp
This commit is contained in:
@@ -97,3 +97,53 @@ def remove_post_listener(usecase_path, name, callback):
|
||||
def remove_all_listeners():
|
||||
pre_listeners.clear()
|
||||
post_listeners.clear()
|
||||
|
||||
|
||||
def extract_docs(module, usecase):
|
||||
import typing
|
||||
import inspect
|
||||
import collections
|
||||
|
||||
results = []
|
||||
|
||||
inputs = collections.OrderedDict()
|
||||
|
||||
function_init = getattr(getattr(ifcopenshell.api, module), usecase).Usecase.__init__
|
||||
function_execute = getattr(getattr(ifcopenshell.api, module), usecase).Usecase.execute
|
||||
|
||||
node_data = {"module": module, "usecase": usecase}
|
||||
|
||||
signature = inspect.signature(function_init)
|
||||
for name, parameter in signature.parameters.items():
|
||||
if name == "self":
|
||||
continue
|
||||
inputs[name] = {"name": name}
|
||||
if isinstance(parameter.default, (str, float, int, bool)):
|
||||
inputs[name]["default"] = parameter.default
|
||||
|
||||
type_hints = typing.get_type_hints(function_init)
|
||||
for name, socket_data in inputs.items():
|
||||
type_hint = type_hints[name]
|
||||
if isinstance(type_hint, typing._UnionGenericAlias):
|
||||
inputs[name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
|
||||
else:
|
||||
inputs[name]["type"] = type_hint.__name__
|
||||
|
||||
description = ""
|
||||
for i, line in enumerate(function_init.__doc__.split("\n")):
|
||||
line = line.strip()
|
||||
if i == 0:
|
||||
node_data["name"] = line
|
||||
elif line.startswith(":return:"):
|
||||
node_data["output"] = {"name": line.split(":")[2].strip(), "description": line.split(":")[3].strip()}
|
||||
elif line.startswith(":param"):
|
||||
param_name = line.split(":")[1].strip().replace("param ", "")
|
||||
inputs[param_name]["description"] = line.split(":")[2].strip()
|
||||
elif i >= 2:
|
||||
description += line
|
||||
|
||||
if "output" in node_data:
|
||||
node_data["output"]["type"] = typing.get_type_hints(function_execute)["return"].__name__
|
||||
node_data["description"] = description.strip()
|
||||
node_data["inputs"] = inputs
|
||||
return node_data
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
class Data:
|
||||
@@ -7,6 +8,7 @@ class Data:
|
||||
cost_items = {}
|
||||
physical_quantities = {}
|
||||
cost_values = {}
|
||||
categories = []
|
||||
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
@@ -15,6 +17,11 @@ class Data:
|
||||
cls.cost_items = {}
|
||||
cls.physical_quantities = {}
|
||||
cls.cost_values = {}
|
||||
cls.categories = []
|
||||
|
||||
@classmethod
|
||||
def set_categories(cls, categories):
|
||||
cls.categories = categories
|
||||
|
||||
@classmethod
|
||||
def load(cls, file):
|
||||
@@ -63,20 +70,32 @@ class Data:
|
||||
del quantity_data["Unit"]
|
||||
cls.physical_quantities[quantity.id()] = quantity_data
|
||||
data["CostQuantities"].append(quantity.id())
|
||||
data["Unit"] = None
|
||||
data["UnitSymbol"] = "?"
|
||||
if cost_item.CostQuantities:
|
||||
quantity = cost_item.CostQuantities[0]
|
||||
unit = ifcopenshell.util.unit.get_property_unit(quantity, cls.file)
|
||||
if unit:
|
||||
data["Unit"] = unit.id()
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
else:
|
||||
data["Unit"] = None
|
||||
data["UnitSymbol"] = None
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_values(cls, cost_item, data):
|
||||
data["CostValues"] = []
|
||||
data["TotalCostValue"] = 0.0
|
||||
data["TotalAppliedValue"] = 0.0
|
||||
data["CategoryValues"] = {}
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
cls.load_cost_item_value(cost_item, cost_value)
|
||||
cls.load_cost_item_value(data, cost_item, cost_value)
|
||||
data["CostValues"].append(cost_value.id())
|
||||
data["TotalAppliedValue"] += cls.cost_values[cost_value.id()]["AppliedValue"]
|
||||
data["TotalCostValue"] = data["TotalCostQuantity"] * data["TotalAppliedValue"]
|
||||
|
||||
@classmethod
|
||||
def load_cost_item_value(cls, cost_item, cost_value):
|
||||
def load_cost_item_value(cls, cost_item_data, cost_item, cost_value):
|
||||
value_data = cost_value.get_info()
|
||||
del value_data["AppliedValue"]
|
||||
del value_data["UnitBasis"]
|
||||
@@ -86,9 +105,14 @@ class Data:
|
||||
value_data["FixedUntilDate"] = ifcopenshell.util.date.ifc2datetime(value_data["FixedUntilDate"])
|
||||
value_data["Components"] = [c.id() for c in value_data["Components"] or []]
|
||||
value_data["AppliedValue"] = cls.calculate_applied_value(cost_item, cost_value)
|
||||
|
||||
if cost_value.Category not in [None, "*"]:
|
||||
cost_item_data["CategoryValues"].setdefault(cost_value.Category, 0)
|
||||
cost_item_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"]
|
||||
|
||||
cls.cost_values[cost_value.id()] = value_data
|
||||
for component in cost_value.Components or []:
|
||||
cls.load_cost_item_value(cost_item, component)
|
||||
cls.load_cost_item_value(cost_item_data, cost_item, component)
|
||||
|
||||
@classmethod
|
||||
def calculate_applied_value(cls, cost_item, cost_value, category_filter=None):
|
||||
|
||||
@@ -9,5 +9,5 @@ class Usecase:
|
||||
for name, value in self.settings["attributes"].items():
|
||||
if name == "AppliedValue" and value is not None:
|
||||
# TODO: support all applied value select types
|
||||
value = self.file.createIfcReal(value)
|
||||
value = self.file.createIfcMonetaryMeasure(value)
|
||||
setattr(self.settings["cost_value"], name, value)
|
||||
|
||||
@@ -22,8 +22,6 @@ class Usecase:
|
||||
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
||||
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
||||
"should_force_triangulation": False, # If we should force triangulation for meshes
|
||||
"is_wireframe": False, # If the geometry is a wireframe
|
||||
"is_curve": False, # If the geometry is a Blender curve
|
||||
"is_point_cloud": False, # If the geometry is a point cloud
|
||||
# Possible IFC representation classes:
|
||||
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
|
||||
@@ -204,14 +202,14 @@ class Usecase:
|
||||
)
|
||||
|
||||
def create_variable_representation(self):
|
||||
if self.settings["is_wireframe"]:
|
||||
return self.create_wireframe_representation()
|
||||
elif self.settings["is_curve"]:
|
||||
if isinstance(self.settings["geometry"], bpy.types.Curve):
|
||||
return self.create_curve3d_representation()
|
||||
elif isinstance(self.settings["geometry"], bpy.types.Camera):
|
||||
return self.create_camera_block_representation()
|
||||
elif not len(self.settings["geometry"].polygons):
|
||||
return self.create_curve3d_representation()
|
||||
elif self.settings["is_point_cloud"]:
|
||||
return self.create_point_cloud_representation()
|
||||
elif isinstance(self.settings["geometry"], bpy.types.Camera):
|
||||
return self.create_camera_block_representation()
|
||||
elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcRectangleProfileDef":
|
||||
return self.create_rectangle_extrusion_representation()
|
||||
elif self.settings["ifc_representation_class"] == "IfcExtrudedAreaSolid/IfcCircleProfileDef":
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
|
||||
@@ -37,12 +38,10 @@ class Usecase:
|
||||
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
|
||||
|
||||
placement = self.file.createIfcLocalPlacement(placement_rel_to, self.get_relative_placement(placement_rel_to))
|
||||
if self.settings["product"].ObjectPlacement:
|
||||
old_placement = self.settings["product"].ObjectPlacement
|
||||
old_placement = self.settings["product"].ObjectPlacement
|
||||
if old_placement and len(self.file.get_inverse(old_placement)) == 1:
|
||||
old_placement.PlacementRelTo = None
|
||||
self.settings["product"].ObjectPlacement = None
|
||||
for inverse in self.file.get_inverse(old_placement):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_placement, placement)
|
||||
ifcopenshell.util.element.remove_deep(self.file, old_placement)
|
||||
self.settings["product"].ObjectPlacement = placement
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
|
||||
@@ -16,13 +16,13 @@ class Usecase:
|
||||
if material:
|
||||
ifcopenshell.api.run("material.unassign_material", self.file, product=self.settings["product"])
|
||||
if self.settings["type"] == "IfcMaterial":
|
||||
self.assign_ifc_material()
|
||||
return self.assign_ifc_material()
|
||||
elif self.settings["type"] == "IfcMaterialConstituentSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
self.create_material_association(material_set)
|
||||
return self.create_material_association(material_set)
|
||||
elif self.settings["type"] == "IfcMaterialLayerSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
self.create_material_association(material_set)
|
||||
return self.create_material_association(material_set)
|
||||
elif self.settings["type"] == "IfcMaterialLayerSetUsage":
|
||||
element_type = ifcopenshell.util.element.get_type(self.settings["product"])
|
||||
if element_type:
|
||||
@@ -34,10 +34,10 @@ class Usecase:
|
||||
else:
|
||||
material_set = self.file.create_entity("IfcMaterialLayerSet")
|
||||
material_set_usage = self.create_layer_set_usage(material_set)
|
||||
self.create_material_association(material_set_usage)
|
||||
return self.create_material_association(material_set_usage)
|
||||
elif self.settings["type"] == "IfcMaterialProfileSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
self.create_material_association(material_set)
|
||||
return self.create_material_association(material_set)
|
||||
elif self.settings["type"] == "IfcMaterialProfileSetUsage":
|
||||
element_type = ifcopenshell.util.element.get_type(self.settings["product"])
|
||||
if element_type:
|
||||
@@ -51,11 +51,11 @@ class Usecase:
|
||||
|
||||
self.update_representation_profile(material_set)
|
||||
material_set_usage = self.create_profile_set_usage(material_set)
|
||||
self.create_material_association(material_set_usage)
|
||||
return self.create_material_association(material_set_usage)
|
||||
elif self.settings["type"] == "IfcMaterialList":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
material_set.Materials = [self.settings["material"]]
|
||||
self.create_material_association(material_set)
|
||||
return self.create_material_association(material_set)
|
||||
|
||||
def update_representation_profile(self, material_set):
|
||||
profile = material_set.CompositeProfile
|
||||
@@ -93,6 +93,7 @@ class Usecase:
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.append(self.settings["product"])
|
||||
rel.RelatedObjects = related_objects
|
||||
return rel
|
||||
|
||||
def create_material_association(self, relating_material):
|
||||
return self.file.create_entity(
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"material": None, "element": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
ifcopenshell.api.run("material.unassign_material", self.file, product=self.settings["element"])
|
||||
if self.settings["material"].is_a("IfcMaterial"):
|
||||
ifcopenshell.api.run(
|
||||
"material.assign_material",
|
||||
self.file,
|
||||
product=self.settings["element"],
|
||||
type="IfcMaterial",
|
||||
material=self.settings["material"],
|
||||
)
|
||||
# No other material type can be copied right now.
|
||||
# 1. Material lists and constituents may have shape aspects and I
|
||||
# haven't implemented it yet.
|
||||
# 2. Material layer and profile sets implicitly define parametric
|
||||
# geometry and we have no way of guaranteeing that this constraint is
|
||||
# satisfied.
|
||||
# 3. Material set usages follow an unofficial constraint that all
|
||||
# instances must have a usage of their type's material set. We cannot
|
||||
# guarantee that constraint.
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"usage": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["usage"], name, value)
|
||||
@@ -1,4 +1,5 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
@@ -9,11 +10,26 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if self.settings["product"].is_a("IfcTypeObject"):
|
||||
material = ifcopenshell.util.element.get_material(self.settings["product"])
|
||||
if material.is_a() in ["IfcMaterialLayerSet", "IfcMaterialProfileSet"]:
|
||||
for inverse in self.file.get_inverse(material):
|
||||
if self.file.schema == "IFC2X3":
|
||||
if not inverse.is_a("IfcMaterialLayerSetUsage"):
|
||||
continue
|
||||
for inverse2 in self.file.get_inverse(inverse):
|
||||
if inverse2.is_a("IfcRelAssociatesMaterial"):
|
||||
self.file.remove(inverse2)
|
||||
else:
|
||||
if not inverse.is_a("IfcMaterialUsageDefinition"):
|
||||
continue
|
||||
for rel in inverse.AssociatedTo:
|
||||
self.file.remove(rel)
|
||||
self.file.remove(inverse)
|
||||
|
||||
for rel in self.settings["product"].HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesMaterial"):
|
||||
if rel.RelatingMaterial.is_a("IfcMaterialLayerSetUsage") or rel.RelatingMaterial.is_a(
|
||||
"IfcMaterialProfileSetUsage"
|
||||
):
|
||||
if rel.RelatingMaterial.is_a() in ["IfcMaterialLayerSetUsage", "IfcMaterialProfileSetUsage"]:
|
||||
self.file.remove(rel.RelatingMaterial)
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
self.file.remove(rel)
|
||||
|
||||
@@ -6,6 +6,8 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
address = self.file.create_entity(self.settings["ifc_class"], "OFFICE")
|
||||
addresses = list(self.settings["assigned_object"].Addresses) if self.settings["assigned_object"].Addresses else []
|
||||
addresses.append(self.file.create_entity(self.settings["ifc_class"], "OFFICE"))
|
||||
addresses.append(address)
|
||||
self.settings["assigned_object"].Addresses = addresses
|
||||
return address
|
||||
|
||||
@@ -6,6 +6,8 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
element = self.file.createIfcActorRole("ARCHITECT")
|
||||
roles = list(self.settings["assigned_object"].Roles) if self.settings["assigned_object"].Roles else []
|
||||
roles.append(self.file.createIfcActorRole("ARCHITECT"))
|
||||
roles.append(element)
|
||||
self.settings["assigned_object"].Roles = roles
|
||||
return element
|
||||
|
||||
@@ -13,8 +13,32 @@ class Usecase:
|
||||
self.added_elements = set()
|
||||
if self.settings["element"].is_a("IfcTypeProduct"):
|
||||
return self.append_type_product()
|
||||
elif self.settings["element"].is_a("IfcMaterial"):
|
||||
return self.append_material()
|
||||
elif self.settings["element"].is_a("IfcCostSchedule"):
|
||||
return self.append_cost_schedule()
|
||||
|
||||
def is_already_appended(self):
|
||||
try:
|
||||
self.file.by_guid(self.settings["element"].GlobalId)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def append_material(self):
|
||||
if [e for e in self.file.by_type("IfcMaterial") if e.Name == self.settings["element"].Name]:
|
||||
return
|
||||
return self.file.add(self.settings["element"])
|
||||
|
||||
def append_cost_schedule(self):
|
||||
if self.is_already_appended():
|
||||
return
|
||||
self.whitelisted_inverse_attributes = {"IfcCostSchedule": ["Controls"], "IfcCostItem": ["IsNestedBy"]}
|
||||
return self.add_element(self.settings["element"])
|
||||
|
||||
def append_type_product(self):
|
||||
if self.is_already_appended():
|
||||
return
|
||||
self.whitelisted_inverse_attributes = {
|
||||
"IfcObjectDefinition": ["HasAssociations"],
|
||||
"IfcMaterialDefinition": ["HasExternalReferences", "HasProperties"],
|
||||
|
||||
@@ -17,6 +17,9 @@ class Usecase:
|
||||
if self.settings["relating_context"].Declares:
|
||||
declares = self.settings["relating_context"].Declares[0]
|
||||
|
||||
if not hasattr(self.settings["definition"], "HasContext"):
|
||||
return
|
||||
|
||||
has_context = None
|
||||
if self.settings["definition"].HasContext:
|
||||
has_context = self.settings["definition"].HasContext[0]
|
||||
|
||||
@@ -3,14 +3,18 @@ import ifcopenshell
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, **settings):
|
||||
self.settings = {"version": "IFC4"}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
def __init__(self, version: str = "IFC4"):
|
||||
"""Create File
|
||||
|
||||
def execute(self):
|
||||
Create a new IFC file object
|
||||
|
||||
:param version: The schema version of the IFC file. Choose from "IFC2X3" or "IFC4".
|
||||
:return: file: The created IFC file object.
|
||||
"""
|
||||
self.settings = {"version": version}
|
||||
|
||||
def execute(self) -> ifcopenshell.file:
|
||||
self.file = ifcopenshell.file(schema=self.settings["version"])
|
||||
# TODO: add all metadata, pending bug #747
|
||||
self.file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe
|
||||
self.file.wrapped_data.header.file_name.time_stamp = (
|
||||
datetime.datetime.utcnow()
|
||||
@@ -22,5 +26,5 @@ class Usecase:
|
||||
self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||
self.file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||
self.file.wrapped_data.header.file_name.authorization = "Nobody"
|
||||
self.file.wrapped_data.header.file_description.description = ('ViewDefinition[DesignTransferView]',)
|
||||
self.file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",)
|
||||
return self.file
|
||||
|
||||
@@ -22,7 +22,7 @@ class Data:
|
||||
return
|
||||
product = file.by_id(product_id)
|
||||
cls.products[product_id] = {"psets": set(), "qtos": set()}
|
||||
if product.is_a("IfcElementType"):
|
||||
if product.is_a("IfcTypeObject"):
|
||||
cls.add_type_product_psets(product, product_id)
|
||||
elif product.is_a("IfcMaterialDefinition"):
|
||||
cls.add_material_psets(product, product_id)
|
||||
|
||||
@@ -5,7 +5,7 @@ import ifcopenshell.util.pset
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"pset": None, "name": None, "properties": {}}
|
||||
self.settings = {"pset": None, "name": None, "properties": {}, "pset_template": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
@@ -21,9 +21,12 @@ class Usecase:
|
||||
self.settings["pset"].Name = self.settings["name"]
|
||||
|
||||
def load_pset_template(self):
|
||||
# TODO: add IFC2X3 PsetQto template support
|
||||
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
self.pset_template = self.psetqto.get_by_name(self.settings["pset"].Name)
|
||||
if self.settings["pset_template"]:
|
||||
self.pset_template = self.settings["pset_template"]
|
||||
else:
|
||||
# TODO: add IFC2X3 PsetQto template support
|
||||
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
self.pset_template = self.psetqto.get_by_name(self.settings["pset"].Name)
|
||||
|
||||
def update_existing_properties(self):
|
||||
for prop in self.get_properties():
|
||||
|
||||
@@ -19,7 +19,7 @@ class Usecase:
|
||||
self.file,
|
||||
ifc_class=self.settings["ifc_class"],
|
||||
predefined_type=self.settings["predefined_type"],
|
||||
name=self.settings["name"],
|
||||
name=self.settings["name"] or "Unammed",
|
||||
)
|
||||
# TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ?
|
||||
# https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import ifcopenshell.util.date
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"resource": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
resource_time = self.file.create_entity("IfcResourceTime")
|
||||
self.settings["resource"].Usage = resource_time
|
||||
return resource_time
|
||||
@@ -1,23 +1,57 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.date
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
resources = {}
|
||||
resource_times = {}
|
||||
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
cls.is_loaded = False
|
||||
cls.resources = {}
|
||||
cls.resource_times = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, file):
|
||||
cls._file = file
|
||||
if not cls._file:
|
||||
return
|
||||
cls.load_resources()
|
||||
cls.load_resource_times()
|
||||
cls.is_loaded=True
|
||||
|
||||
@classmethod
|
||||
def load_resources(cls):
|
||||
cls.resources = {}
|
||||
for resource in file.by_type("IfcResource"):
|
||||
for resource in cls._file.by_type("IfcResource"):
|
||||
data = resource.get_info()
|
||||
del data["OwnerHistory"]
|
||||
data["IsNestedBy"] = []
|
||||
for rel in resource.IsNestedBy:
|
||||
[data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects]
|
||||
data["Nests"] = []
|
||||
for rel in resource.Nests:
|
||||
[data["Nests"].append(rel.RelatingObject.id())]
|
||||
data["ResourceOf"] = []
|
||||
for rel in resource.ResourceOf:
|
||||
[data["ResourceOf"].append(o.id()) for o in rel.RelatedObjects]
|
||||
data["HasContext"] = resource.HasContext[0].RelatingContext.id() if resource.HasContext else None
|
||||
if resource.Usage:
|
||||
data["Usage"] = data["Usage"].id()
|
||||
cls.resources[resource.id()] = data
|
||||
cls.is_loaded=True
|
||||
|
||||
@classmethod
|
||||
def load_resource_times(cls):
|
||||
cls.resource_times = {}
|
||||
for resource_time in cls._file.by_type("IfcResourceTime"):
|
||||
data = resource_time.get_info()
|
||||
for key, value in data.items():
|
||||
if not value:
|
||||
continue
|
||||
if "Start" in key or "Finish" in key or key == "StatusTime":
|
||||
data[key] = ifcopenshell.util.date.ifc2datetime(value)
|
||||
elif "Work" in key or key =="LevelingDelay":
|
||||
data[key] = ifcopenshell.util.date.ifc2datetime(value)
|
||||
cls.resource_times[resource_time.id()] = data
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import datetime
|
||||
import ifcopenshell.util.date
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"resource_time": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.resource = self.get_resource()
|
||||
|
||||
# If the user specifies both an end date and a duration, the duration takes priority
|
||||
if (
|
||||
self.settings["attributes"].get("ScheduleWork", None)
|
||||
and "ScheduleFinish" in self.settings["attributes"].keys()
|
||||
):
|
||||
del self.settings["attributes"]["ScheduleFinish"]
|
||||
if (
|
||||
self.settings["attributes"].get("ActualWork", None)
|
||||
and "ActualFinish" in self.settings["attributes"].keys()
|
||||
):
|
||||
del self.settings["attributes"]["ActualFinish"]
|
||||
|
||||
for name, value in self.settings["attributes"].items():
|
||||
if value:
|
||||
if "Start" in name or "Finish" in name or name == "StatusTime":
|
||||
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
|
||||
elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime":
|
||||
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
|
||||
setattr(self.settings["resource_time"], name, value)
|
||||
|
||||
def get_resource(self):
|
||||
return [e for e in self.file.get_inverse(self.settings["resource_time"]) if e.is_a("IfcResource")][0]
|
||||
@@ -1,4 +1,5 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase:
|
||||
@@ -9,29 +10,33 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
result = self.file.create_entity(self.settings["product"].is_a())
|
||||
self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema)
|
||||
self.copy_attributes(self.settings["product"], result)
|
||||
for inverse in self.file.get_inverse(self.settings["product"]):
|
||||
for i, value in enumerate(inverse):
|
||||
if value == self.settings["product"]:
|
||||
new_inverse = self.file.create_entity(inverse.is_a())
|
||||
self.copy_attributes(inverse, new_inverse)
|
||||
new_inverse[i] = result
|
||||
elif isinstance(value, (tuple, list)) and self.settings["product"] in value:
|
||||
new_value = list(value)
|
||||
new_value.append(result)
|
||||
inverse[i] = new_value
|
||||
if result.is_a("IfcProduct"):
|
||||
result.Representation = None
|
||||
elif result.is_a("IfcTypeProduct"):
|
||||
result.RepresentationMaps = None
|
||||
result = ifcopenshell.util.element.copy(self.file, self.settings["product"])
|
||||
self.copy_indirect_attributes(self.settings["product"], result)
|
||||
# Copying representations is too hard, so for now we just don't do it.
|
||||
self.remove_representations(result)
|
||||
return result
|
||||
|
||||
def copy_attributes(self, from_element, to_element):
|
||||
declaration = self.schema.declaration_by_name(from_element.is_a())
|
||||
for attribute in declaration.all_attributes():
|
||||
if attribute.name() == "GlobalId":
|
||||
setattr(to_element, attribute.name(), ifcopenshell.guid.new())
|
||||
def copy_indirect_attributes(self, from_element, to_element):
|
||||
for inverse in self.file.get_inverse(from_element):
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
inverse.RelatedObjects = [to_element]
|
||||
pset = ifcopenshell.util.element.copy_deep(self.file, inverse.RelatingPropertyDefinition)
|
||||
inverse.RelatingPropertyDefinition = pset
|
||||
else:
|
||||
setattr(to_element, attribute.name(), getattr(from_element, attribute.name()))
|
||||
# TODO: Consider whether this general approach is good or not. Maybe it isn't.
|
||||
for i, value in enumerate(inverse):
|
||||
if value == from_element:
|
||||
new_inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
new_inverse[i] = to_element
|
||||
elif isinstance(value, (tuple, list)) and from_element in value:
|
||||
new_value = list(value)
|
||||
new_value.append(to_element)
|
||||
inverse[i] = new_value
|
||||
|
||||
def remove_representations(self, element):
|
||||
if element.is_a("IfcProduct"):
|
||||
element.Representation = None
|
||||
elif element.is_a("IfcTypeProduct"):
|
||||
element.RepresentationMaps = None
|
||||
|
||||
@@ -25,7 +25,10 @@ class Usecase:
|
||||
element.PredefinedType = self.settings["predefined_type"]
|
||||
except:
|
||||
element.PredefinedType = "USERDEFINED"
|
||||
element.ObjectType = self.settings["predefined_type"]
|
||||
if hasattr(element, "ObjectType"):
|
||||
element.ObjectType = self.settings["predefined_type"]
|
||||
elif hasattr(element, "ElementType"):
|
||||
element.ElementType = self.settings["predefined_type"]
|
||||
elif hasattr(element, "ObjectType"):
|
||||
element.ObjectType = self.settings["predefined_type"]
|
||||
if self.file.schema == "IFC2X3":
|
||||
|
||||
@@ -12,9 +12,9 @@ class Usecase:
|
||||
|
||||
def execute(self):
|
||||
self.calendar_cache = {}
|
||||
self.cascade_task(self.settings["task"])
|
||||
self.cascade_task(self.settings["task"], is_first_task=True)
|
||||
|
||||
def cascade_task(self, task):
|
||||
def cascade_task(self, task, is_first_task=False):
|
||||
if not task.TaskTime:
|
||||
return
|
||||
|
||||
@@ -88,13 +88,13 @@ class Usecase:
|
||||
)
|
||||
if potential_finish > finish:
|
||||
start_ifc = ifcopenshell.util.date.datetime2ifc(start, "IfcDateTime")
|
||||
if task.TaskTime.ScheduleStart == start_ifc:
|
||||
if task.TaskTime.ScheduleStart == start_ifc and not is_first_task:
|
||||
return
|
||||
task.TaskTime.ScheduleStart = start_ifc
|
||||
task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(potential_finish, "IfcDateTime")
|
||||
else:
|
||||
finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
|
||||
if task.TaskTime.ScheduleFinish == finish_ifc:
|
||||
if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task:
|
||||
return
|
||||
task.TaskTime.ScheduleFinish = finish_ifc
|
||||
task.TaskTime.ScheduleStart = ifcopenshell.util.date.datetime2ifc(
|
||||
@@ -109,7 +109,7 @@ class Usecase:
|
||||
elif finishes:
|
||||
finish = max(finishes)
|
||||
finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
|
||||
if task.TaskTime.ScheduleFinish == finish_ifc:
|
||||
if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task:
|
||||
return
|
||||
task.TaskTime.ScheduleFinish = finish_ifc
|
||||
task.TaskTime.ScheduleStart = ifcopenshell.util.date.datetime2ifc(
|
||||
@@ -124,7 +124,7 @@ class Usecase:
|
||||
elif starts:
|
||||
start = max(starts)
|
||||
start_ifc = ifcopenshell.util.date.datetime2ifc(start, "IfcDateTime")
|
||||
if task.TaskTime.ScheduleStart == start_ifc:
|
||||
if task.TaskTime.ScheduleStart == start_ifc and not is_first_task:
|
||||
return
|
||||
task.TaskTime.ScheduleStart = start_ifc
|
||||
task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(
|
||||
|
||||
@@ -16,12 +16,12 @@ class Usecase:
|
||||
|
||||
# If the user specifies both an end date and a duration, the duration takes priority
|
||||
if (
|
||||
"ScheduleDuration" in self.settings["attributes"].keys()
|
||||
self.settings["attributes"].get("ScheduleDuration", None)
|
||||
and "ScheduleFinish" in self.settings["attributes"].keys()
|
||||
):
|
||||
del self.settings["attributes"]["ScheduleFinish"]
|
||||
if (
|
||||
"ActualDuration" in self.settings["attributes"].keys()
|
||||
self.settings["attributes"].get("ActualDuration", None)
|
||||
and "ActualFinish" in self.settings["attributes"].keys()
|
||||
):
|
||||
del self.settings["attributes"]["ActualFinish"]
|
||||
|
||||
@@ -11,12 +11,13 @@ class Usecase:
|
||||
|
||||
def execute(self):
|
||||
contained_in_structure = self.settings["product"].ContainedInStructure
|
||||
if not contained_in_structure:
|
||||
return
|
||||
|
||||
if contained_in_structure:
|
||||
related_elements = list(contained_in_structure[0].RelatedElements)
|
||||
related_elements.remove(self.settings["product"])
|
||||
if related_elements:
|
||||
contained_in_structure[0].RelatedElements = related_elements
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": contained_in_structure[0]})
|
||||
else:
|
||||
self.file.remove(contained_in_structure)
|
||||
related_elements = list(contained_in_structure[0].RelatedElements)
|
||||
related_elements.remove(self.settings["product"])
|
||||
if related_elements:
|
||||
contained_in_structure[0].RelatedElements = related_elements
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": contained_in_structure[0]})
|
||||
else:
|
||||
self.file.remove(contained_in_structure[0])
|
||||
|
||||
@@ -23,7 +23,7 @@ class Usecase:
|
||||
self.update_colour_rgb(element.SurfaceColour, self.settings["surface_colour"])
|
||||
else:
|
||||
element.SurfaceColour = self.create_colour_rgb(self.settings["surface_colour"])
|
||||
element.Transparency = (self.settings["transparency"] - 1) * -1
|
||||
element.Transparency = self.settings["transparency"]
|
||||
if element.is_a("IfcSurfaceStyleRendering"):
|
||||
if element.DiffuseColour:
|
||||
self.update_colour_rgb(element.DiffuseColour, self.settings["diffuse_colour"])
|
||||
@@ -44,7 +44,7 @@ class Usecase:
|
||||
def create_surface_style_rendering(self):
|
||||
return self.file.create_entity("IfcSurfaceStyleRendering", **{
|
||||
"SurfaceColour": self.create_colour_rgb(self.settings["surface_colour"]),
|
||||
"Transparency": (self.settings["transparency"] - 1) * -1,
|
||||
"Transparency": self.settings["transparency"],
|
||||
"ReflectanceMethod": "NOTDEFINED",
|
||||
"DiffuseColour": self.create_colour_rgb(self.settings["diffuse_colour"])
|
||||
})
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcSystem", **{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"Name": "Unnamed"
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"system": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if not self.settings["system"].IsGroupedBy:
|
||||
return self.file.create_entity("IfcRelAssignsToGroup", **{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatedObjects": [self.settings["product"]],
|
||||
"RelatingGroup": self.settings["system"]
|
||||
})
|
||||
rel = self.settings["system"].IsGroupedBy[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.add(self.settings["product"])
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
@@ -0,0 +1,24 @@
|
||||
class Data:
|
||||
is_loaded = False
|
||||
products = {}
|
||||
systems = {}
|
||||
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
cls.is_loaded = False
|
||||
cls.products = {}
|
||||
cls.systems = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, file):
|
||||
cls.products = {}
|
||||
cls.systems = {}
|
||||
for system in file.by_type("IfcSystem", include_subtypes=False):
|
||||
if system.IsGroupedBy:
|
||||
for rel in system.IsGroupedBy:
|
||||
for product in rel.RelatedObjects:
|
||||
cls.products.setdefault(product.id(), []).append(system.id())
|
||||
data = system.get_info()
|
||||
del data["OwnerHistory"]
|
||||
cls.systems[system.id()] = data
|
||||
cls.is_loaded=True
|
||||
@@ -0,0 +1,13 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"system": None,
|
||||
"attributes": {}
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["system"], name, value)
|
||||
@@ -0,0 +1,11 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"system": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["system"].IsGroupedBy or []:
|
||||
self.file.remove(rel)
|
||||
self.file.remove(self.settings["system"])
|
||||
@@ -0,0 +1,25 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"system": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if not self.settings["system"].IsGroupedBy:
|
||||
return
|
||||
rel = self.settings["system"].IsGroupedBy[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.remove(self.settings["product"])
|
||||
if len(related_objects):
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
self.file.remove(rel)
|
||||
@@ -1,18 +1,40 @@
|
||||
class Data:
|
||||
products = {}
|
||||
types = {}
|
||||
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
cls.products = {}
|
||||
cls.types = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, file, product_id):
|
||||
if not file:
|
||||
return
|
||||
cls.file = file
|
||||
product = file.by_id(product_id)
|
||||
if file.schema == "IFC2X3" and not hasattr(product, "IsDefinedBy"):
|
||||
if product.is_a("IfcTypeObject"):
|
||||
cls.load_type(product_id)
|
||||
else:
|
||||
cls.load_product(product_id)
|
||||
|
||||
@classmethod
|
||||
def load_type(cls, product_id):
|
||||
product = cls.file.by_id(product_id)
|
||||
cls.types[product_id] = None
|
||||
if cls.file.schema == "IFC2X3":
|
||||
if getattr(product, "ObjectTypeOf", None):
|
||||
cls.types[product_id] = [o.id() for o in product.ObjectTypeOf[0].RelatedObjects]
|
||||
else:
|
||||
if getattr(product, "Types", None):
|
||||
cls.types[product_id] = [o.id() for o in product.Types[0].RelatedObjects]
|
||||
|
||||
@classmethod
|
||||
def load_product(cls, product_id):
|
||||
product = cls.file.by_id(product_id)
|
||||
if cls.file.schema == "IFC2X3" and not hasattr(product, "IsDefinedBy"):
|
||||
cls.products[product_id] = None
|
||||
elif file.schema != "IFC2X3" and not hasattr(product, "IsTypedBy"):
|
||||
elif cls.file.schema != "IFC2X3" and not hasattr(product, "IsTypedBy"):
|
||||
cls.products[product_id] = None
|
||||
elif hasattr(product, "IsTypedBy") and product.IsTypedBy:
|
||||
type = product.IsTypedBy[0].RelatingType
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"currency": "DOLLARYDOO"}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity("IfcMonetaryUnit", self.settings["currency"])
|
||||
@@ -1,44 +1,55 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
class Usecase():
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"length": {
|
||||
"is_metric": True,
|
||||
"raw": "MILLIMETERS"
|
||||
},
|
||||
"area": {
|
||||
"is_metric": True,
|
||||
"raw": "METERS"
|
||||
},
|
||||
"volume": {
|
||||
"is_metric": True,
|
||||
"raw": "METERS"
|
||||
},
|
||||
"units": None,
|
||||
"length": {"is_metric": True, "raw": "MILLIMETERS"},
|
||||
"area": {"is_metric": True, "raw": "METERS"},
|
||||
"volume": {"is_metric": True, "raw": "METERS"},
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for unit_type, data in self.settings.items():
|
||||
if data["is_metric"]:
|
||||
data["ifc"] = self.create_metric_unit(unit_type, data)
|
||||
else:
|
||||
data["ifc"] = self.create_imperial_unit(unit_type, data)
|
||||
# We're going to refactor this to split unit creation and assignment
|
||||
if self.settings["units"]:
|
||||
units = self.settings["units"]
|
||||
else:
|
||||
del self.settings["units"] # TODO refactor
|
||||
units = []
|
||||
for unit_type, data in self.settings.items():
|
||||
if data["is_metric"]:
|
||||
units.append(self.create_metric_unit(unit_type, data))
|
||||
else:
|
||||
units.append(self.create_imperial_unit(unit_type, data))
|
||||
|
||||
unit_assignment = self.get_unit_assignment()
|
||||
self.assign_units(unit_assignment, units)
|
||||
return unit_assignment
|
||||
|
||||
def get_unit_assignment(self):
|
||||
unit_assignment = self.file.by_type("IfcUnitAssignment")
|
||||
if unit_assignment:
|
||||
unit_assignment = unit_assignment[0]
|
||||
# TODO: handle unit rewriting, which is complicated
|
||||
else:
|
||||
unit_assignment = self.file.createIfcUnitAssignment([u["ifc"] for u in self.settings.values()])
|
||||
unit_assignment = self.file.createIfcUnitAssignment()
|
||||
if self.file.schema == "IFC2X3":
|
||||
self.file.by_type("IfcProject")[0].UnitsInContext = unit_assignment
|
||||
else:
|
||||
self.file.by_type("IfcContext")[0].UnitsInContext = unit_assignment
|
||||
return unit_assignment
|
||||
|
||||
def assign_units(self, unit_assignment, new_units):
|
||||
units = set(unit_assignment.Units or [])
|
||||
for unit in new_units:
|
||||
units.add(unit)
|
||||
unit_assignment.Units = list(units)
|
||||
|
||||
def create_metric_unit(self, unit_type, data):
|
||||
type_prefix = ""
|
||||
if unit_type == "area":
|
||||
@@ -72,7 +83,9 @@ class Usecase():
|
||||
name = "{}inch".format(name_prefix + " " if name_prefix else "")
|
||||
elif data["raw"] == "FEET":
|
||||
name = "{}foot".format(name_prefix + " " if name_prefix else "")
|
||||
value_component = self.file.create_entity("IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]})
|
||||
value_component = self.file.create_entity(
|
||||
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]}
|
||||
)
|
||||
conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit)
|
||||
return self.file.createIfcConversionBasedUnit(
|
||||
dimensional_exponents, "{}UNIT".format(unit_type.upper()), name, conversion_factor
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
units = {}
|
||||
@@ -5,16 +9,43 @@ class Data:
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
cls.is_loaded = False
|
||||
cls.unit_assignment = []
|
||||
cls.units = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, file):
|
||||
if not file:
|
||||
return
|
||||
unit_assignment = file.by_type("IfcUnitAssignment")
|
||||
cls.file = file
|
||||
cls.unit_assignment = []
|
||||
cls.units = {}
|
||||
unit_assignment = cls.file.by_type("IfcUnitAssignment")
|
||||
if not unit_assignment:
|
||||
return
|
||||
for unit in unit_assignment[0].Units:
|
||||
pass
|
||||
# TODO: implement along with UI
|
||||
cls.unit_assignment.append(unit.id())
|
||||
cls.load_unit(unit)
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def load_unit(cls, unit):
|
||||
if unit.is_a("IfcDerivedUnit"):
|
||||
data = unit.get_info()
|
||||
data["Elements"] = [{"Unit": e.Unit.id(), "Exponent": e.Exponent} for e in unit.Elements]
|
||||
for element in unit.Elements:
|
||||
cls.load_unit(element.Unit)
|
||||
cls.units[unit.id()] = data
|
||||
elif unit.is_a("IfcNamedUnit"):
|
||||
data = unit.get_info()
|
||||
if unit.is_a("IfcSIUnit"):
|
||||
data["Dimensions"] = ifcopenshell.util.unit.get_si_dimensions(unit.Name)
|
||||
else:
|
||||
data["Dimensions"] = unit.Dimensions.get_info()
|
||||
if unit.is_a("IfcConversionBasedUnit"):
|
||||
conversion_factor = unit.ConversionFactor.get_info()
|
||||
cls.load_unit(unit.ConversionFactor.UnitComponent)
|
||||
conversion_factor["UnitComponent"] = unit.ConversionFactor.UnitComponent.id()
|
||||
data["ConversionFactor"] = conversion_factor
|
||||
cls.units[unit.id()] = data
|
||||
elif unit.is_a("IfcMonetaryUnit"):
|
||||
cls.units[unit.id()] = unit.get_info()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"unit": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["unit"], name, value)
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"unit": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["unit"], name, value)
|
||||
@@ -0,0 +1,10 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"unit": None, "attributes": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["unit"], name, value)
|
||||
@@ -0,0 +1,18 @@
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Usecase():
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"unit": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
unit_assignment = self.file.by_type("IfcUnitAssignment")[0]
|
||||
units = list(unit_assignment.Units)
|
||||
units.remove(self.settings["unit"])
|
||||
if not units:
|
||||
return
|
||||
unit_assignment.Units = units
|
||||
ifcopenshell.util.element.remove_deep(self.file, self.settings["unit"])
|
||||
@@ -176,12 +176,14 @@ const IfcParse::enumeration_type& %(schema_name)s::%(name)s::Class() { return *%
|
||||
}
|
||||
|
||||
%(schema_name)s::%(name)s::%(name)s(Value v) {
|
||||
data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(name)s_type);
|
||||
IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();
|
||||
attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,ToString(v)));
|
||||
data_->setArgument(0,attr);
|
||||
}
|
||||
|
||||
%(schema_name)s::%(name)s::%(name)s(const std::string& v) {
|
||||
data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(name)s_type);
|
||||
IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();
|
||||
attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(FromString(v),ToString(FromString(v))));
|
||||
data_->setArgument(0,attr);
|
||||
|
||||
@@ -264,19 +264,44 @@ class file(object):
|
||||
eid = kwargs.pop("id", -1)
|
||||
except:
|
||||
pass
|
||||
|
||||
e = entity_instance((self.schema, type), self)
|
||||
self.wrapped_data.add(e.wrapped_data, eid)
|
||||
e.wrapped_data.this.disown()
|
||||
|
||||
# Create pairs of {attribute index, attribute value}.
|
||||
# Keyword arguments are mapped to their corresponding
|
||||
# numeric index with get_argument_index().
|
||||
|
||||
# @todo we should probably check that values for
|
||||
# attributes are not passed as duplicates using
|
||||
# both regular arguments and keyword arguments.
|
||||
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
|
||||
# Don't store these attributes as transactions
|
||||
# as the creation it self is already stored with
|
||||
# it's arguments
|
||||
if attrs:
|
||||
transaction = self.transaction
|
||||
self.transaction = None
|
||||
|
||||
for idx, arg in attrs:
|
||||
e[idx] = arg
|
||||
|
||||
# Restore transaction status
|
||||
if attrs:
|
||||
self.transaction = transaction
|
||||
|
||||
# Once the values are populated add the instance
|
||||
# to the file.
|
||||
self.wrapped_data.add(e.wrapped_data, eid)
|
||||
|
||||
# The file container now handles the lifetime of
|
||||
# this instance. Tell SWIG that it is no longer
|
||||
# the owner.
|
||||
e.wrapped_data.this.disown()
|
||||
|
||||
if self.transaction:
|
||||
self.transaction.store_create(e)
|
||||
|
||||
return e
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
||||
@@ -184,6 +184,9 @@ def create_shape(settings, inst, repr=None):
|
||||
or
|
||||
Return an OpenCASCADE BRep if settings.USE_PYTHON_OPENCASCADE == True
|
||||
|
||||
Note that in Python, you must store a reference to the element returned by this function to prevent garbage
|
||||
collection when you access its children. See #1124.
|
||||
|
||||
example:
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
@@ -195,9 +198,12 @@ def create_shape(settings, inst, repr=None):
|
||||
for i, product in enumerate(products):
|
||||
if product.Representation is not None:
|
||||
try:
|
||||
shape = geom.create_shape(settings, inst=product).geometry
|
||||
created_shape = geom.create_shape(settings, inst=product)
|
||||
shape = created_shape.geometry # see #1124
|
||||
shape_gpXYZ = shape.Location().Transformation().TranslationPart() # These are methods of the TopoDS_Shape class from pythonOCC
|
||||
print(shape_gpXYZ.X(), shape_gpXYZ.Y(), shape_gpXYZ.Z()) # These are methods of the gpXYZ class from pythonOCC
|
||||
except:
|
||||
print("Shape creation failed")
|
||||
"""
|
||||
return wrap_shape_creation(
|
||||
settings,
|
||||
|
||||
Submodule src/ifcopenshell-python/ifcopenshell/mvd updated: 2f0ce53d56...667894b629
@@ -42,7 +42,7 @@ def ifc2datetime(element):
|
||||
element.DateComponent.DayComponent,
|
||||
element.TimeComponent.HourComponent,
|
||||
element.TimeComponent.MinuteComponent,
|
||||
element.TimeComponent.SecondComponent,
|
||||
int(element.TimeComponent.SecondComponent),
|
||||
# TODO: implement TimeComponent timezone
|
||||
)
|
||||
elif element.is_a("IfcCalendarDate"):
|
||||
|
||||
@@ -73,10 +73,8 @@ def get_material(element, should_skip_usage=False):
|
||||
return relationship.RelatingMaterial.ForProfileSet
|
||||
return relationship.RelatingMaterial
|
||||
relating_type = get_type(element)
|
||||
if hasattr(relating_type, "HasAssociations") and relating_type.HasAssociations:
|
||||
for relationship in relating_type.HasAssociations:
|
||||
if relationship.is_a("IfcRelAssociatesMaterial"):
|
||||
return relationship.RelatingMaterial
|
||||
if relating_type != element and hasattr(relating_type, "HasAssociations") and relating_type.HasAssociations:
|
||||
return get_material(relating_type, should_skip_usage)
|
||||
|
||||
|
||||
def get_container(element):
|
||||
@@ -119,7 +117,10 @@ def copy(ifc_file, element):
|
||||
for i, attribute in enumerate(element):
|
||||
if attribute is None:
|
||||
continue
|
||||
new[i] = attribute
|
||||
if new.attribute_name(i) == "GlobalId":
|
||||
new[i] = ifcopenshell.guid.new()
|
||||
else:
|
||||
new[i] = attribute
|
||||
return new
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
cobie_type_classes = [
|
||||
"IfcDoorStyle",
|
||||
"IfcBuildingElementProxyType",
|
||||
"IfcChimneyType",
|
||||
"IfcCoveringType",
|
||||
"IfcDoorType",
|
||||
"IfcFootingType",
|
||||
"IfcPileType",
|
||||
"IfcRoofType",
|
||||
"IfcShadingDeviceType",
|
||||
"IfcWindowType",
|
||||
"IfcDistributionControlElementType",
|
||||
"IfcDistributionChamberElementType",
|
||||
"IfcEnergyConversionDeviceType",
|
||||
"IfcFlowControllerType",
|
||||
"IfcFlowMovingDeviceType",
|
||||
"IfcFlowStorageDeviceType",
|
||||
"IfcFlowTerminalType",
|
||||
"IfcFlowTreatmentDeviceType",
|
||||
"IfcElementAssemblyType",
|
||||
"IfcBuildingElementPartType",
|
||||
"IfcDiscreteAccessoryType",
|
||||
"IfcMechanicalFastenerType",
|
||||
"IfcReinforcingElementType",
|
||||
"IfcVibrationIsolatorType",
|
||||
"IfcFurnishingElementType",
|
||||
"IfcGeographicElementType",
|
||||
"IfcTransportElementType",
|
||||
"IfcSpatialZoneType",
|
||||
"IfcWindowStyle",
|
||||
]
|
||||
|
||||
cobie_component_classes = [
|
||||
"IfcBuildingElementProxy",
|
||||
"IfcChimney",
|
||||
"IfcCovering",
|
||||
"IfcDoor",
|
||||
"IfcShadingDevice",
|
||||
"IfcWindow",
|
||||
"IfcDistributionControlElement",
|
||||
"IfcDistributionChamberElement",
|
||||
"IfcEnergyConversionDevice",
|
||||
"IfcFlowController",
|
||||
"IfcFlowMovingDevice",
|
||||
"IfcFlowStorageDevice",
|
||||
"IfcFlowTerminal",
|
||||
"IfcFlowTreatmentDevice",
|
||||
"IfcDiscreteAccessory",
|
||||
"IfcTendon",
|
||||
"IfcTendonAnchor",
|
||||
"IfcVibrationIsolator",
|
||||
"IfcFurnishingElement",
|
||||
"IfcGeographicElement",
|
||||
"IfcTransportElement",
|
||||
]
|
||||
|
||||
fmhem_classes = [
|
||||
"IfcDoorStyle",
|
||||
"IfcWindowStyle",
|
||||
"IfcDoorType",
|
||||
"IfcWindowType",
|
||||
"IfcRoofType",
|
||||
"IfcShadingDeviceType",
|
||||
"IfcDistributionControlElementType",
|
||||
"IfcEnergyConversionDeviceType",
|
||||
"IfcFlowControllerType",
|
||||
"IfcJunctionBoxType",
|
||||
"IfcFlowMovingDeviceType",
|
||||
"IfcFlowStorageDeviceType",
|
||||
"IfcFlowTerminalType",
|
||||
"IfcFlowTreatmentDeviceType",
|
||||
"IfcFurnishingElementType",
|
||||
"IfcTransportElementType",
|
||||
]
|
||||
|
||||
|
||||
def get_cobie_types(ifc_file):
|
||||
elements = []
|
||||
for ifc_class in cobie_type_classes:
|
||||
try:
|
||||
elements += ifc_file.by_type(ifc_class)
|
||||
except:
|
||||
pass
|
||||
return elements
|
||||
|
||||
|
||||
def get_cobie_components(ifc_file):
|
||||
elements = []
|
||||
for ifc_class in cobie_component_classes:
|
||||
try:
|
||||
elements += ifc_file.by_type(ifc_class)
|
||||
except:
|
||||
pass
|
||||
return elements
|
||||
|
||||
|
||||
def get_fmhem_types(ifc_file):
|
||||
elements = []
|
||||
for ifc_class in fmhem_classes:
|
||||
try:
|
||||
if ifc_class == "IfcEnergyConversionDeviceType":
|
||||
elements += [e for e in ifc_file.by_type(ifc_class) if not e.is_a("IfcCooledBeamType")]
|
||||
else:
|
||||
elements += ifc_file.by_type(ifc_class)
|
||||
except:
|
||||
pass
|
||||
return elements
|
||||
@@ -18,6 +18,17 @@ def is_a(entity, ifc_class):
|
||||
return False
|
||||
|
||||
|
||||
def get_subtypes(entity):
|
||||
def get_classes(declaration):
|
||||
results = []
|
||||
if not declaration.is_abstract():
|
||||
results.append(declaration)
|
||||
for subtype in declaration.subtypes():
|
||||
results.extend(get_classes(subtype))
|
||||
return results
|
||||
return get_classes(entity)
|
||||
|
||||
|
||||
def reassign_class(ifc_file, element, new_class):
|
||||
try:
|
||||
new_element = ifc_file.create_entity(new_class)
|
||||
|
||||
@@ -1,62 +1,8 @@
|
||||
import ifcopenshell.util
|
||||
import ifcopenshell.util.fm
|
||||
import ifcopenshell.util.element
|
||||
import lark
|
||||
|
||||
cobie_type_assets = [
|
||||
"IfcDoorStyle",
|
||||
"IfcBuildingElementProxyType",
|
||||
"IfcChimneyType",
|
||||
"IfcCoveringType",
|
||||
"IfcDoorType",
|
||||
"IfcFootingType",
|
||||
"IfcPileType",
|
||||
"IfcRoofType",
|
||||
"IfcShadingDeviceType",
|
||||
"IfcWindowType",
|
||||
"IfcDistributionControlElementType",
|
||||
"IfcDistributionChamberElementType",
|
||||
"IfcEnergyConversionDeviceType",
|
||||
"IfcFlowControllerType",
|
||||
"IfcFlowMovingDeviceType",
|
||||
"IfcFlowStorageDeviceType",
|
||||
"IfcFlowTerminalType",
|
||||
"IfcFlowTreatmentDeviceType",
|
||||
"IfcElementAssemblyType",
|
||||
"IfcBuildingElementPartType",
|
||||
"IfcDiscreteAccessoryType",
|
||||
"IfcMechanicalFastenerType",
|
||||
"IfcReinforcingElementType",
|
||||
"IfcVibrationIsolatorType",
|
||||
"IfcFurnishingElementType",
|
||||
"IfcGeographicElementType",
|
||||
"IfcTransportElementType",
|
||||
"IfcSpatialZoneType",
|
||||
"IfcWindowStyle",
|
||||
]
|
||||
cobie_component_assets = [
|
||||
"IfcBuildingElementProxy",
|
||||
"IfcChimney",
|
||||
"IfcCovering",
|
||||
"IfcDoor",
|
||||
"IfcShadingDevice",
|
||||
"IfcWindow",
|
||||
"IfcDistributionControlElement",
|
||||
"IfcDistributionChamberElement",
|
||||
"IfcEnergyConversionDevice",
|
||||
"IfcFlowController",
|
||||
"IfcFlowMovingDevice",
|
||||
"IfcFlowStorageDevice",
|
||||
"IfcFlowTerminal",
|
||||
"IfcFlowTreatmentDevice",
|
||||
"IfcDiscreteAccessory",
|
||||
"IfcTendon",
|
||||
"IfcTendonAnchor",
|
||||
"IfcVibrationIsolator",
|
||||
"IfcFurnishingElement",
|
||||
"IfcGeographicElement",
|
||||
"IfcTransportElement",
|
||||
]
|
||||
|
||||
|
||||
class Selector:
|
||||
def parse(self, ifc_file, query):
|
||||
@@ -74,9 +20,10 @@ class Selector:
|
||||
filter_value: ESCAPED_STRING
|
||||
pset_or_qto: /[A-Za-z0-9_]+/ "." /[A-Za-z0-9_]+/
|
||||
lfunction: and | or
|
||||
inverse_relationship: types | contains_elements
|
||||
inverse_relationship: types | contains_elements | boundedby
|
||||
types: "*"
|
||||
contains_elements: "@"
|
||||
boundedby: "@@"
|
||||
and: "&"
|
||||
or: "|"
|
||||
comparison: contains | morethanequalto | lessthanequalto | equal | morethan | lessthan
|
||||
@@ -170,23 +117,18 @@ class Selector:
|
||||
elif inverse_relationship == "contains_elements" and hasattr(element, "ContainsElements"):
|
||||
for relationship in element.ContainsElements:
|
||||
results.extend(relationship.RelatedElements)
|
||||
elif inverse_relationship == "boundedby" and hasattr(element, "BoundedBy"):
|
||||
for relationship in element.BoundedBy:
|
||||
results.append(relationship.RelatedBuildingElement)
|
||||
return results
|
||||
|
||||
def get_class_selector(self, class_selector):
|
||||
if class_selector.children[0] == "COBie":
|
||||
elements = []
|
||||
for ifc_class in cobie_component_assets:
|
||||
try:
|
||||
elements += self.file.by_type(ifc_class)
|
||||
except:
|
||||
pass
|
||||
ifcopenshell.util.fm.get_cobie_components(self.file)
|
||||
elif class_selector.children[0] == "COBieType":
|
||||
elements = []
|
||||
for ifc_class in cobie_type_assets:
|
||||
try:
|
||||
elements += self.file.by_type(ifc_class)
|
||||
except:
|
||||
pass
|
||||
ifcopenshell.util.fm.get_cobie_types(self.file)
|
||||
elif class_selector.children[0] == "FMHEM":
|
||||
ifcopenshell.util.fm.get_fmhem_types(self.file)
|
||||
else:
|
||||
elements = self.file.by_type(class_selector.children[0])
|
||||
if len(class_selector.children) > 1 and class_selector.children[1].data == "filter":
|
||||
|
||||
@@ -12,13 +12,14 @@ with open(os.path.join(cwd, "entity_to_type_map_2x3.json")) as f:
|
||||
with open(os.path.join(cwd, "entity_to_type_map_4.json")) as f:
|
||||
entity_to_type_map["IFC4"] = json.load(f)
|
||||
|
||||
type_to_entity_map["IFC2X3"] = {
|
||||
value: [key] for key in entity_to_type_map["IFC2X3"] for value in entity_to_type_map["IFC2X3"][key]
|
||||
}
|
||||
|
||||
type_to_entity_map["IFC4"] = {
|
||||
value: [key] for key in entity_to_type_map["IFC4"] for value in entity_to_type_map["IFC4"][key]
|
||||
}
|
||||
for schema in ["IFC2X3", "IFC4"]:
|
||||
type_to_entity_map[schema] = {}
|
||||
for element, element_types in entity_to_type_map[schema].items():
|
||||
for element_type in element_types:
|
||||
type_to_entity_map[schema].setdefault(element_type, []).append(element)
|
||||
# TODO: this method just fails for IFC2X3 because 2X3 type mapping is just so broken.
|
||||
# Uncomment the following line to see how bad it is.
|
||||
# print(type_to_entity_map[schema])
|
||||
|
||||
|
||||
def get_applicable_types(ifc_class, schema="IFC4"):
|
||||
|
||||
@@ -52,6 +52,41 @@ unit_names = [
|
||||
"WEBER",
|
||||
]
|
||||
|
||||
|
||||
si_dimensions = {
|
||||
"METRE": (1, 0, 0, 0, 0, 0, 0),
|
||||
"SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0),
|
||||
"CUBIC_METRE": (3, 0, 0, 0, 0, 0, 0),
|
||||
"GRAM": (0, 1, 0, 0, 0, 0, 0),
|
||||
"SECOND": (0, 0, 1, 0, 0, 0, 0),
|
||||
"AMPERE": (0, 0, 0, 1, 0, 0, 0),
|
||||
"KELVIN": (0, 0, 0, 0, 1, 0, 0),
|
||||
"MOLE": (0, 0, 0, 0, 0, 1, 0),
|
||||
"CANDELA": (0, 0, 0, 0, 0, 0, 1),
|
||||
"RADIAN": (0, 0, 0, 0, 0, 0, 0),
|
||||
"STERADIAN": (0, 0, 0, 0, 0, 0, 0),
|
||||
"HERTZ": (0, 0, -1, 0, 0, 0, 0),
|
||||
"NEWTON": (1, 1, -2, 0, 0, 0, 0),
|
||||
"PASCAL": (-1, 1, -2, 0, 0, 0, 0),
|
||||
"JOULE": (2, 1, -2, 0, 0, 0, 0),
|
||||
"WATT": (2, 1, -3, 0, 0, 0, 0),
|
||||
"COULOMB": (0, 0, 1, 1, 0, 0, 0),
|
||||
"VOLT": (2, 1, -3, -1, 0, 0, 0),
|
||||
"FARAD": (-2, -1, 4, 2, 0, 0, 0),
|
||||
"OHM": (2, 1, -3, -2, 0, 0, 0),
|
||||
"SIEMENS": (-2, -1, 3, 2, 0, 0, 0),
|
||||
"WEBER": (2, 1, -2, -1, 0, 0, 0),
|
||||
"TESLA": (0, 1, -2, -1, 0, 0, 0),
|
||||
"HENRY": (2, 1, -2, -2, 0, 0, 0),
|
||||
"DEGREE_CELSIUS": (0, 0, 0, 0, 1, 0, 0),
|
||||
"LUMEN": (0, 0, 0, 0, 0, 0, 1),
|
||||
"LUX": (-2, 0, 0, 0, 0, 0, 1),
|
||||
"BECQUEREL": (0, 0, -1, 0, 0, 0, 0),
|
||||
"GRAY": (2, 0, -2, 0, 0, 0, 0),
|
||||
"SIEVERT": (2, 0, -2, 0, 0, 0, 0),
|
||||
"OTHERWISE": (0, 0, 0, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
si_conversions = {
|
||||
"inch": 0.0254,
|
||||
"foot": 0.3048,
|
||||
@@ -87,6 +122,33 @@ si_conversions = {
|
||||
"btu": 1055.056,
|
||||
}
|
||||
|
||||
prefix_symbols = {
|
||||
"EXA": "E",
|
||||
"PETA": "P",
|
||||
"TERA": "T",
|
||||
"GIGA": "G",
|
||||
"MEGA": "M",
|
||||
"KILO": "k",
|
||||
"HECTO": "h",
|
||||
"DECA": "da",
|
||||
"DECI": "d",
|
||||
"CENTI": "c",
|
||||
"MILLI": "m",
|
||||
"MICRO": "μ",
|
||||
"NANO": "n",
|
||||
"PICO": "p",
|
||||
"FEMTO": "f",
|
||||
"ATTO": "a",
|
||||
}
|
||||
|
||||
unit_symbols = {
|
||||
"CUBIC_METRE": "m3",
|
||||
"GRAM": "g",
|
||||
"SECOND": "s",
|
||||
"SQUARE_METRE": "m2",
|
||||
"METRE": "m",
|
||||
}
|
||||
|
||||
|
||||
def get_prefix(text):
|
||||
if text:
|
||||
@@ -105,11 +167,63 @@ def get_prefix_multiplier(text):
|
||||
|
||||
|
||||
def get_unit_name(text):
|
||||
text = text.upper().replace("METER", "METRE")
|
||||
for name in unit_names:
|
||||
if name in text.upper().replace("METER", "METRE"):
|
||||
if name in text:
|
||||
return name
|
||||
|
||||
|
||||
def get_si_dimensions(name):
|
||||
return si_dimensions.get(name, si_dimensions["OTHERWISE"])
|
||||
|
||||
|
||||
def get_property_unit(prop, ifc_file):
|
||||
unit = getattr(prop, "Unit", None)
|
||||
if unit:
|
||||
return unit
|
||||
unit_assignment = ifc_file.by_type("IfcUnitAssignment")
|
||||
if not unit_assignment:
|
||||
return
|
||||
entity = prop.wrapped_data.declaration().as_entity()
|
||||
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||
measure_type = entity.attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||
elif prop.is_a("IfcPropertySingleValue") and prop.NominalValue:
|
||||
measure_type = prop.NominalValue.is_a()
|
||||
for text in ("Ifc", "Measure", "Non", "Positive", "Negative"):
|
||||
measure_type = measure_type.replace(text, "")
|
||||
measure_type = measure_type.upper() + "UNIT"
|
||||
units = [u for u in unit_assignment[0].Units if getattr(u, "UnitType", None) == measure_type]
|
||||
if units:
|
||||
return units[0]
|
||||
|
||||
|
||||
def get_symbol_quantity_class(symbol):
|
||||
# Dumb, but everybody gets it, unlike regex golf
|
||||
if not symbol:
|
||||
return "IfcQuantityCount"
|
||||
symbol = symbol.lower()
|
||||
if symbol in ["kg", "g", "mt", "kt", "t"]:
|
||||
return "IfcQuantityWeight"
|
||||
elif symbol in ["day", "d", "hour", "hr", "h", "minute", "min", "m", "second", "sec", "s"]:
|
||||
return "IfcQuantityTime"
|
||||
elif symbol in ["km3", "m3", "cm3", "mm3", "cy", "cft", "cin"]:
|
||||
return "IfcQuantityVolume"
|
||||
elif symbol in ["km2", "m2", "cm2", "mm2", "sqy", "sqft", "sqin"]:
|
||||
return "IfcQuantityArea"
|
||||
elif symbol in ["km", "m", "cm", "mm", "ly", "lf", "lin", "yd", "ft", "in"]:
|
||||
return "IfcQuantityLength"
|
||||
return "IfcQuantityCount"
|
||||
|
||||
|
||||
def get_unit_symbol(unit):
|
||||
if unit.is_a("IfcSIUnit"):
|
||||
symbol = ""
|
||||
symbol += prefix_symbols.get(unit.Prefix, "")
|
||||
symbol += unit_symbols.get(unit.Name.replace("METER", "METRE"), "?")
|
||||
return symbol
|
||||
return "?"
|
||||
|
||||
|
||||
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
|
||||
"""Converts between length, area, and volume units
|
||||
|
||||
@@ -155,6 +269,8 @@ Example::
|
||||
:returns: The scale factor
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
|
||||
def calculate_unit_scale(file):
|
||||
units = file.by_type("IfcUnitAssignment")[0]
|
||||
unit_scale = 1
|
||||
|
||||
@@ -34,7 +34,7 @@ class json_logger:
|
||||
self.instance = instance
|
||||
|
||||
def log(self, level, message, *args, **kwargs):
|
||||
self.statements.append(log_entry_type(level, message % args, kwargs.get('instance'))._asdict())
|
||||
self.statements.append(log_entry_type(level, message % args, kwargs.get("instance"))._asdict())
|
||||
|
||||
def __getattr__(self, level):
|
||||
return functools.partial(self.log, level, instance=self.instance)
|
||||
@@ -75,7 +75,7 @@ def assert_valid(attr, val, schema):
|
||||
|
||||
while isinstance(attr_type, type_wrappers):
|
||||
attr_type = attr_type.declared_type()
|
||||
|
||||
|
||||
invalid = False
|
||||
|
||||
if isinstance(attr_type, simple_type):
|
||||
@@ -120,10 +120,10 @@ def validate(f, logger):
|
||||
numeric identifiers or invalidate entity names are not caught by this function. Some of these might have been
|
||||
logged and can be retrieved by calling `ifcopenshell.get_log()`. A verification of the type, entity and global
|
||||
WHERE rules is also not implemented.
|
||||
|
||||
|
||||
For every entity instance in the model, it is checked that the entity is not abstract that every attribute value
|
||||
is of the correct type and that the inverse attributes are of the correct cardinality.
|
||||
|
||||
|
||||
Express simple types are checked for their valuation type. For select types it is asserted that the value conforms
|
||||
to one of the leaves. For enumerations it is checked that the value is indeed on of the items. For aggregations it
|
||||
is checked that the elements and the cardinality conforms. Type declarations (IfcInteger which is an integer) are
|
||||
|
||||
Reference in New Issue
Block a user