First Commit
This commit is contained in:
@@ -0,0 +1,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.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))
|
||||
Reference in New Issue
Block a user