This commit is contained in:
Andrej730
2024-03-18 16:09:35 +05:00
parent e2a06e10b6
commit b67b5e8d9f
5 changed files with 63 additions and 33 deletions
+24 -7
View File
@@ -29,6 +29,7 @@ import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.schema
from statistics import mean
from typing import Optional, Union
try:
from odf.namespaces import OFFICENS
@@ -269,7 +270,7 @@ class IfcCsv:
elif not include_global_id:
self.results = sorted(self.results, key=lambda x: x[0])
def export_csv(self, output, delimiter=None):
def export_csv(self, output: str, delimiter: Optional[str] = None) -> None:
with open(output, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f, delimiter=delimiter)
writer.writerow(self.headers)
@@ -387,8 +388,16 @@ class IfcCsv:
self.import_xlsx(ifc_file, table, attributes, null, empty, bool_true, bool_false)
def import_csv(
self, ifc_file, table, attributes=None, delimiter=",", null="-", empty="", bool_true="YES", bool_false="NO"
):
self,
ifc_file: ifcopenshell.file,
table: str,
attributes: Optional[list[Union[str, None]]] = None,
delimiter: str = ",",
null: str = "-",
empty: str = "",
bool_true: str = "YES",
bool_false: str = "NO",
) -> None:
with open(table, newline="", encoding="utf-8") as f:
reader = csv.reader(f, delimiter=delimiter)
headers = []
@@ -421,7 +430,17 @@ class IfcCsv:
for _, row in df.iterrows():
self.process_row(ifc_file, row.tolist(), headers, attributes, null, empty, bool_true, bool_false)
def process_row(self, ifc_file, row, headers, attributes, null, empty, bool_true, bool_false):
def process_row(
self,
ifc_file: ifcopenshell.file,
row: list[str],
headers: list[str],
attributes: list[Union[str, None]],
null: str,
empty: str,
bool_true: str,
bool_false: str,
) -> None:
try:
element = ifc_file.by_guid(row[0])
except:
@@ -448,9 +467,7 @@ if __name__ == "__main__":
parser.add_argument("-s", "--spreadsheet", type=str, default="data.csv", help="The spreadsheet file")
parser.add_argument("-f", "--format", type=str, default="csv", help="The format, chosen from csv, ods, or xlsx")
parser.add_argument("-d", "--delimiter", type=str, default=",", help="The delimiter in CSV. Defaults to a comma.")
parser.add_argument(
"-n", "--null", type=str, default="N/A", help="How to represent null values. Defaults to N/A."
)
parser.add_argument("-n", "--null", type=str, default="N/A", help="How to represent null values. Defaults to N/A.")
parser.add_argument(
"-e", "--empty", type=str, default="-", help="How to represent empty strings. Defaults to a hyphen."
)
@@ -17,14 +17,15 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.element
from typing import Optional
def get_references(element, should_inherit=True):
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?
elif hasattr(element, "HasExternalReference"): # Seriously, IFC?
return {r.RelatingReference for r in element.HasExternalReference or []}
if should_inherit:
element_type = ifcopenshell.util.element.get_type(element)
@@ -50,13 +51,13 @@ def get_references(element, should_inherit=True):
return occurrence_results
def get_classification(reference):
def get_classification(reference: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
if reference.is_a("IfcClassification"):
return reference
return get_classification(reference.ReferencedSource)
def get_inherited_references(reference):
def get_inherited_references(reference: Optional[ifcopenshell.entity_instance]) -> list[ifcopenshell.entity_instance]:
results = []
while True:
if not reference or reference.is_a("IfcClassification"):
@@ -64,4 +65,3 @@ def get_inherited_references(reference):
results.append(reference)
reference = reference.ReferencedSource
return results
@@ -18,9 +18,10 @@
import numpy as np
import ifcopenshell
from typing import Literal, Iterable
def a2p(o, z, x):
def a2p(o: Iterable[float], z: Iterable[float], x: Iterable[float]) -> np.array:
"""Converts a location, X, and Z axis vector to a 4x4 transformation matrix
IFC uses a right-handed coordinate system, so it is not necessary to
@@ -73,7 +74,7 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> np.array:
return a2p(o, z, x)
def get_local_placement(placement):
def get_local_placement(placement: ifcopenshell.entity_instance) -> np.array:
"""Parse a local placement into a 4x4 transformation matrix
This is typically used to find the location and rotation of an element. The
@@ -107,7 +108,7 @@ def get_local_placement(placement):
return np.dot(parent, get_axis2placement(placement.RelativePlacement))
def get_cartesiantransformationoperator3d(inst):
def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) -> np.array:
"""Parses an IfcCartesianTransformationOperator into a 4x4 transformation matrix
Note that in general you will not need to call this directly. See
@@ -152,7 +153,7 @@ def get_cartesiantransformationoperator3d(inst):
return m4
def get_mappeditem_transformation(item):
def get_mappeditem_transformation(item: ifcopenshell.entity_instance) -> np.array:
"""Parse an IfcMappedItem into a 4x4 transformation matrix
Mapped items take a representation with an origin and transform them with a
@@ -170,7 +171,7 @@ def get_mappeditem_transformation(item):
return get_cartesiantransformationoperator3d(item.MappingTarget) @ m4
def get_storey_elevation(storey):
def get_storey_elevation(storey: ifcopenshell.entity_instance) -> float:
"""Get the Z elevation in project units of a buildling storey
Building storeys store elevation in two possible locations: the Z value of
@@ -187,7 +188,7 @@ def get_storey_elevation(storey):
return getattr(storey, "Elevation", 0.0) or 0.0
def rotation(angle, axis, is_degrees=True):
def rotation(angle: float, axis: Literal["X", "Y", "Z"], is_degrees=True) -> np.array:
"""Create a 4x4 numpy matrix representing an euler rotation
:param angle: The angle of rotation
@@ -21,6 +21,7 @@ import json
import time
import ifcopenshell
import ifcopenshell.util.attribute
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
# This is highly experimental and incomplete, however, it may work for simple datasets.
# In this simple implementation, we only support 2X3<->4 right now
@@ -28,7 +29,7 @@ import ifcopenshell.util.attribute
cwd = os.path.dirname(os.path.realpath(__file__))
def get_fallback_schema(version):
def get_fallback_schema(version: str) -> str:
"""fallback to the schema version we do have docs and mapping for,
needed to support IFC versions like 4X3_RC1, 4X1 etc"""
if version.startswith("IFC4X3"):
@@ -38,7 +39,7 @@ def get_fallback_schema(version):
return version
def is_a(entity, ifc_class):
def is_a(entity: ifcopenshell.entity_instance, ifc_class: str) -> bool:
ifc_class = ifc_class.upper()
if entity.name_uc() == ifc_class:
return True
@@ -59,7 +60,9 @@ def get_subtypes(entity):
return get_classes(entity)
def reassign_class(ifc_file, element, new_class):
def reassign_class(
ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance, new_class: str
) -> ifcopenshell.entity_instance:
"""
Attempts to change the class (entity name) of `element` to `new_class` by
removing element and recreating a similar instance of type `new_class`
@@ -74,7 +77,7 @@ def reassign_class(ifc_file, element, new_class):
It's unlikely that this affects real-world usage of this function.
"""
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
schema: ifcopenshell_wrapper.schema_definition = ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
try:
declaration = schema.declaration_by_name(new_class)
except:
@@ -116,11 +119,11 @@ def reassign_class(ifc_file, element, new_class):
class BatchReassignClass:
def __init__(self, file):
def __init__(self, file: ifcopenshell.file):
self.file = file
self.purge()
def reassign(self, element, new_class):
def reassign(self, element: ifcopenshell.entity_instance, new_class: str) -> ifcopenshell.entity_instance:
try:
new_element = self.file.create_entity(new_class)
except:
@@ -150,9 +153,12 @@ class BatchReassignClass:
self.file.remove(element)
self.purge()
def purge(self):
self.replacements = {}
self.to_delete = set()
def purge(self) -> None:
# mapping {inverse: {attribute_index: {old_element: new_element} } }
self.replacements: dict[
ifcopenshell.entity_instance, dict[int, dict[ifcopenshell.entity_instance, ifcopenshell.entity_instance]]
] = {}
self.to_delete: set[ifcopenshell.entity_instance] = set()
class Migrator:
@@ -217,7 +223,7 @@ class Migrator:
"User": None,
}
def migrate(self, element, new_file):
def migrate(self, element: ifcopenshell.entity_instance, new_file: ifcopenshell.file) -> ifcopenshell.entity_instance:
if element.id() == 0:
return new_file.create_entity(element.is_a(), element.wrappedValue)
try:
@@ -25,8 +25,9 @@ import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.geolocation
import ifcopenshell.util.classification
import ifcopenshell.util.schema
from decimal import Decimal
from typing import Optional
from typing import Optional, Any, Union
filter_elements_grammar = lark.Lark(
@@ -258,8 +259,8 @@ def format(query):
return FormatTransformer().transform(format_grammar.parse(query))
def get_element_value(element, query):
keys = GetElementTransformer().transform(get_element_grammar.parse(query))
def get_element_value(element: ifcopenshell.entity_instance, query: str) -> Any:
keys: list[str] = GetElementTransformer().transform(get_element_grammar.parse(query))
return Selector.get_element_value(element, keys)
@@ -309,7 +310,12 @@ def filter_elements(
return transformer.elements
def set_element_value(ifc_file, element, query, value):
def set_element_value(
ifc_file: ifcopenshell.file,
element: ifcopenshell.entity_instance,
query: Union[str, list[str]],
value: Optional[str],
) -> Union[ifcopenshell.entity_instance, None]:
if isinstance(query, (list, tuple)):
keys = query
else:
@@ -885,7 +891,7 @@ class Selector:
return {"keys": keys, "is_regex": is_regex}
@classmethod
def get_element_value(cls, element, keys):
def get_element_value(cls, element: ifcopenshell.entity_instance, keys: list[str]) -> Any:
value = element
for key in keys:
if value is None: