forked from IfcOpenShell/step-file-parser
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfile.py
More file actions
108 lines (88 loc) · 3.34 KB
/
file.py
File metadata and controls
108 lines (88 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import types
import re
import numbers
import itertools
from .parse import parse, ParseResult
from .grammar import HEADER_FIELDS
from .transformer import entity_instance
try:
from .mvd_info import MvdInfo, LARK_AVAILABLE
except ImportError: # in case of running module locally (e.g. test_parser.py)
from mvd_info import MvdInfo, LARK_AVAILABLE
class file:
"""
A somewhat compatible interface (but very limited) to ifcopenshell.file
"""
def __init__(self, result:ParseResult):
self.header_ = result.header
self.data_ = result.entities
@property
def schema_identifier(self) -> str:
return self.header_["FILE_SCHEMA"][0][0]
@property
def schema(self) -> str:
"""General IFC schema version: IFC2X3, IFC4, IFC4X3."""
prefixes = ("IFC", "X", "_ADD", "_TC")
reg = "".join(f"(?P<{s}>{s}\\d+)?" for s in prefixes)
match = re.match(reg, self.schema_identifier)
version_tuple = tuple(
map(
lambda pp: int(pp[1][len(pp[0]) :]) if pp[1] else None,
((p, match.group(p)) for p in prefixes),
)
)
return "".join(
"".join(map(str, t)) if t[1] else ""
for t in zip(prefixes, version_tuple[0:2])
)
@property
def schema_version(self) -> tuple[int, int, int, int]:
"""Numeric representation of the full IFC schema version.
E.g. IFC4X3_ADD2 is represented as (4, 3, 2, 0).
"""
schema = self.wrapped_data.schema
version = []
for prefix in ("IFC", "X", "_ADD", "_TC"):
number = re.search(prefix + r"(\d)", schema)
version.append(int(number.group(1)) if number else 0)
return tuple(version)
@property
def header(self):
header = {}
for field_name, namedtuple_class in HEADER_FIELDS.items():
field_data = self.header_.get(field_name.upper(), [])
header[field_name.lower()] = namedtuple_class(*field_data)
return types.SimpleNamespace(**header)
@property
def mvd(self):
if not LARK_AVAILABLE or MvdInfo is None:
return None
return MvdInfo(self.header)
def __getitem__(self, key: numbers.Integral) -> entity_instance:
return self.by_id(key)
def by_id(self, id: int) -> entity_instance:
"""Return an IFC entity instance filtered by IFC ID.
:param id: STEP numerical identifier
:type id: int
:raises RuntimeError: If `id` is not found or multiple definitions exist for `id`.
:rtype: entity_instance
"""
ns = self.data_.get(id, [])
if len(ns) == 0:
raise RuntimeError(f"Instance with id {id} not found")
elif len(ns) > 1:
raise RuntimeError(f"Duplicate definition for id {id}")
return ns[0]
def by_type(self, type: str) -> list[entity_instance]:
"""Return IFC objects filtered by IFC Type and wrapped with the entity_instance class.
:rtype: list[entity_instance]
"""
type_lc = type.lower()
return list(
filter(
lambda ent: ent.type.lower() == type_lc,
itertools.chain.from_iterable(self.data_.values()),
)
)
def open(fn, only_header= False) -> file:
return file(parse(filename=fn, only_header=only_header))