mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 22:50:21 +00:00
typing
This commit is contained in:
+24
-7
@@ -29,6 +29,7 @@ import ifcopenshell.util.selector
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
from statistics import mean
|
from statistics import mean
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from odf.namespaces import OFFICENS
|
from odf.namespaces import OFFICENS
|
||||||
@@ -269,7 +270,7 @@ class IfcCsv:
|
|||||||
elif not include_global_id:
|
elif not include_global_id:
|
||||||
self.results = sorted(self.results, key=lambda x: x[0])
|
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:
|
with open(output, "w", newline="", encoding="utf-8") as f:
|
||||||
writer = csv.writer(f, delimiter=delimiter)
|
writer = csv.writer(f, delimiter=delimiter)
|
||||||
writer.writerow(self.headers)
|
writer.writerow(self.headers)
|
||||||
@@ -387,8 +388,16 @@ class IfcCsv:
|
|||||||
self.import_xlsx(ifc_file, table, attributes, null, empty, bool_true, bool_false)
|
self.import_xlsx(ifc_file, table, attributes, null, empty, bool_true, bool_false)
|
||||||
|
|
||||||
def import_csv(
|
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:
|
with open(table, newline="", encoding="utf-8") as f:
|
||||||
reader = csv.reader(f, delimiter=delimiter)
|
reader = csv.reader(f, delimiter=delimiter)
|
||||||
headers = []
|
headers = []
|
||||||
@@ -421,7 +430,17 @@ class IfcCsv:
|
|||||||
for _, row in df.iterrows():
|
for _, row in df.iterrows():
|
||||||
self.process_row(ifc_file, row.tolist(), headers, attributes, null, empty, bool_true, bool_false)
|
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:
|
try:
|
||||||
element = ifc_file.by_guid(row[0])
|
element = ifc_file.by_guid(row[0])
|
||||||
except:
|
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("-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("-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("-d", "--delimiter", type=str, default=",", help="The delimiter in CSV. Defaults to a comma.")
|
||||||
parser.add_argument(
|
parser.add_argument("-n", "--null", type=str, default="N/A", help="How to represent null values. Defaults to N/A.")
|
||||||
"-n", "--null", type=str, default="N/A", help="How to represent null values. Defaults to N/A."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e", "--empty", type=str, default="-", help="How to represent empty strings. Defaults to a hyphen."
|
"-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/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell.util.element
|
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()
|
results = set()
|
||||||
if not element.is_a("IfcRoot"):
|
if not element.is_a("IfcRoot"):
|
||||||
if hasattr(element, "HasExternalReferences"):
|
if hasattr(element, "HasExternalReferences"):
|
||||||
return {r.RelatingReference for r in element.HasExternalReferences or []}
|
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 []}
|
return {r.RelatingReference for r in element.HasExternalReference or []}
|
||||||
if should_inherit:
|
if should_inherit:
|
||||||
element_type = ifcopenshell.util.element.get_type(element)
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
@@ -50,13 +51,13 @@ def get_references(element, should_inherit=True):
|
|||||||
return occurrence_results
|
return occurrence_results
|
||||||
|
|
||||||
|
|
||||||
def get_classification(reference):
|
def get_classification(reference: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||||
if reference.is_a("IfcClassification"):
|
if reference.is_a("IfcClassification"):
|
||||||
return reference
|
return reference
|
||||||
return get_classification(reference.ReferencedSource)
|
return get_classification(reference.ReferencedSource)
|
||||||
|
|
||||||
|
|
||||||
def get_inherited_references(reference):
|
def get_inherited_references(reference: Optional[ifcopenshell.entity_instance]) -> list[ifcopenshell.entity_instance]:
|
||||||
results = []
|
results = []
|
||||||
while True:
|
while True:
|
||||||
if not reference or reference.is_a("IfcClassification"):
|
if not reference or reference.is_a("IfcClassification"):
|
||||||
@@ -64,4 +65,3 @@ def get_inherited_references(reference):
|
|||||||
results.append(reference)
|
results.append(reference)
|
||||||
reference = reference.ReferencedSource
|
reference = reference.ReferencedSource
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,10 @@
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ifcopenshell
|
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
|
"""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
|
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)
|
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
|
"""Parse a local placement into a 4x4 transformation matrix
|
||||||
|
|
||||||
This is typically used to find the location and rotation of an element. The
|
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))
|
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
|
"""Parses an IfcCartesianTransformationOperator into a 4x4 transformation matrix
|
||||||
|
|
||||||
Note that in general you will not need to call this directly. See
|
Note that in general you will not need to call this directly. See
|
||||||
@@ -152,7 +153,7 @@ def get_cartesiantransformationoperator3d(inst):
|
|||||||
return m4
|
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
|
"""Parse an IfcMappedItem into a 4x4 transformation matrix
|
||||||
|
|
||||||
Mapped items take a representation with an origin and transform them with a
|
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
|
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
|
"""Get the Z elevation in project units of a buildling storey
|
||||||
|
|
||||||
Building storeys store elevation in two possible locations: the Z value of
|
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
|
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
|
"""Create a 4x4 numpy matrix representing an euler rotation
|
||||||
|
|
||||||
:param angle: The angle of rotation
|
:param angle: The angle of rotation
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
|
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
|
||||||
|
|
||||||
# This is highly experimental and incomplete, however, it may work for simple datasets.
|
# This is highly experimental and incomplete, however, it may work for simple datasets.
|
||||||
# In this simple implementation, we only support 2X3<->4 right now
|
# 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__))
|
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,
|
"""fallback to the schema version we do have docs and mapping for,
|
||||||
needed to support IFC versions like 4X3_RC1, 4X1 etc"""
|
needed to support IFC versions like 4X3_RC1, 4X1 etc"""
|
||||||
if version.startswith("IFC4X3"):
|
if version.startswith("IFC4X3"):
|
||||||
@@ -38,7 +39,7 @@ def get_fallback_schema(version):
|
|||||||
return 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()
|
ifc_class = ifc_class.upper()
|
||||||
if entity.name_uc() == ifc_class:
|
if entity.name_uc() == ifc_class:
|
||||||
return True
|
return True
|
||||||
@@ -59,7 +60,9 @@ def get_subtypes(entity):
|
|||||||
return get_classes(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
|
Attempts to change the class (entity name) of `element` to `new_class` by
|
||||||
removing element and recreating a similar instance of type `new_class`
|
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.
|
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:
|
try:
|
||||||
declaration = schema.declaration_by_name(new_class)
|
declaration = schema.declaration_by_name(new_class)
|
||||||
except:
|
except:
|
||||||
@@ -116,11 +119,11 @@ def reassign_class(ifc_file, element, new_class):
|
|||||||
|
|
||||||
|
|
||||||
class BatchReassignClass:
|
class BatchReassignClass:
|
||||||
def __init__(self, file):
|
def __init__(self, file: ifcopenshell.file):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.purge()
|
self.purge()
|
||||||
|
|
||||||
def reassign(self, element, new_class):
|
def reassign(self, element: ifcopenshell.entity_instance, new_class: str) -> ifcopenshell.entity_instance:
|
||||||
try:
|
try:
|
||||||
new_element = self.file.create_entity(new_class)
|
new_element = self.file.create_entity(new_class)
|
||||||
except:
|
except:
|
||||||
@@ -150,9 +153,12 @@ class BatchReassignClass:
|
|||||||
self.file.remove(element)
|
self.file.remove(element)
|
||||||
self.purge()
|
self.purge()
|
||||||
|
|
||||||
def purge(self):
|
def purge(self) -> None:
|
||||||
self.replacements = {}
|
# mapping {inverse: {attribute_index: {old_element: new_element} } }
|
||||||
self.to_delete = set()
|
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:
|
class Migrator:
|
||||||
@@ -217,7 +223,7 @@ class Migrator:
|
|||||||
"User": None,
|
"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:
|
if element.id() == 0:
|
||||||
return new_file.create_entity(element.is_a(), element.wrappedValue)
|
return new_file.create_entity(element.is_a(), element.wrappedValue)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
import ifcopenshell.util.geolocation
|
import ifcopenshell.util.geolocation
|
||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
|
import ifcopenshell.util.schema
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Optional
|
from typing import Optional, Any, Union
|
||||||
|
|
||||||
|
|
||||||
filter_elements_grammar = lark.Lark(
|
filter_elements_grammar = lark.Lark(
|
||||||
@@ -258,8 +259,8 @@ def format(query):
|
|||||||
return FormatTransformer().transform(format_grammar.parse(query))
|
return FormatTransformer().transform(format_grammar.parse(query))
|
||||||
|
|
||||||
|
|
||||||
def get_element_value(element, query):
|
def get_element_value(element: ifcopenshell.entity_instance, query: str) -> Any:
|
||||||
keys = GetElementTransformer().transform(get_element_grammar.parse(query))
|
keys: list[str] = GetElementTransformer().transform(get_element_grammar.parse(query))
|
||||||
return Selector.get_element_value(element, keys)
|
return Selector.get_element_value(element, keys)
|
||||||
|
|
||||||
|
|
||||||
@@ -309,7 +310,12 @@ def filter_elements(
|
|||||||
return transformer.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)):
|
if isinstance(query, (list, tuple)):
|
||||||
keys = query
|
keys = query
|
||||||
else:
|
else:
|
||||||
@@ -885,7 +891,7 @@ class Selector:
|
|||||||
return {"keys": keys, "is_regex": is_regex}
|
return {"keys": keys, "is_regex": is_regex}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_element_value(cls, element, keys):
|
def get_element_value(cls, element: ifcopenshell.entity_instance, keys: list[str]) -> Any:
|
||||||
value = element
|
value = element
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user