black ifcopenshell-python

This commit is contained in:
htlcnn
2020-11-01 20:08:27 +07:00
committed by Dion Moult
parent 2c9d6a47f4
commit 286c77e3b0
27 changed files with 1502 additions and 979 deletions
@@ -1,18 +1,19 @@
def get_psets(element):
psets = {}
try:
if element.is_a('IfcTypeObject'):
if element.is_a("IfcTypeObject"):
if element.HasPropertySets:
for definition in element.HasPropertySets:
psets[definition.Name] = get_property_definition(definition)
else:
for relationship in element.IsDefinedBy:
if relationship.is_a('IfcRelDefinesByProperties'):
if relationship.is_a("IfcRelDefinesByProperties"):
definition = relationship.RelatingPropertyDefinition
psets[definition.Name] = get_property_definition(definition)
except Exception as e:
import traceback
print('failed to load properties: {}'.format(e))
print("failed to load properties: {}".format(e))
traceback.print_exc()
return psets
@@ -20,9 +21,9 @@ def get_psets(element):
def get_property_definition(definition):
if definition is not None:
props = {}
if definition.is_a('IfcElementQuantity'):
if definition.is_a("IfcElementQuantity"):
props.update(get_quantities(definition.Quantities))
elif definition.is_a('IfcPropertySet'):
elif definition.is_a("IfcPropertySet"):
props.update(get_properties(definition.HasProperties))
else:
# Entity introduced in IFC4
@@ -35,7 +36,7 @@ def get_property_definition(definition):
def get_quantities(quantities):
results = {}
for quantity in quantities:
if quantity.is_a('IfcPhysicalSimpleQuantity'):
if quantity.is_a("IfcPhysicalSimpleQuantity"):
results[quantity.Name] = quantity[3]
return results
@@ -43,22 +44,22 @@ def get_quantities(quantities):
def get_properties(properties):
results = {}
for prop in properties:
if prop.is_a('IfcPropertySingleValue'):
if prop.is_a("IfcPropertySingleValue"):
results[prop.Name] = prop.NominalValue.wrappedValue
elif prop.is_a('IfcComplexProperty'):
elif prop.is_a("IfcComplexProperty"):
data = prop.get_info()
data['properties'] = get_properties(prop.HasProperties)
del(data['HasProperties'])
data["properties"] = get_properties(prop.HasProperties)
del data["HasProperties"]
results[prop.Name] = data
return results
def get_type(element):
if hasattr(element, 'IsTypedBy') and element.IsTypedBy:
if hasattr(element, "IsTypedBy") and element.IsTypedBy:
return element.IsTypedBy[0].RelatingType
elif hasattr(element, 'IsDefinedBy') and element.IsDefinedBy: # IFC2X3
elif hasattr(element, "IsDefinedBy") and element.IsDefinedBy: # IFC2X3
for relationship in element.IsDefinedBy:
if relationship.is_a('IfcRelDefinesByType'):
if relationship.is_a("IfcRelDefinesByType"):
return relationship.RelatingType
@@ -1,16 +1,18 @@
import math
def dms2dd(degrees, minutes, seconds, ms=0):
dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(ms/3600000000.0)
dd = float(degrees) + float(minutes) / 60.0 + float(seconds) / (3600.0) + float(ms / 3600000000.0)
return dd
def dd2dms(dd, use_ms=False):
dd = float(dd)
sign = 1 if dd >= 0 else -1
dd = abs(dd)
if use_ms:
seconds, ms = divmod(dd*60*60*1000000, 1000000)
minutes, seconds = divmod(dd*60*60, 60)
seconds, ms = divmod(dd * 60 * 60 * 1000000, 1000000)
minutes, seconds = divmod(dd * 60 * 60, 60)
degrees, minutes = divmod(minutes, 60)
if dd < 0:
degrees = -degrees
@@ -18,9 +20,10 @@ def dd2dms(dd, use_ms=False):
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign, int(ms) * sign)
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign)
def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_axis_ordinate, scale=None):
if scale is None:
scale = 1.
scale = 1.0
rotation = math.atan2(x_axis_ordinate, x_axis_abscissa)
a = scale * math.cos(rotation)
b = scale * math.sin(rotation)
@@ -29,6 +32,7 @@ def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_
height = z + orthogonal_height
return (eastings, northings, height)
# Used for converting the X and Y vectors of the X Axis in IFC geolocation
def xy2angle(x, y):
return math.degrees(math.atan2(y, x))
@@ -3,65 +3,67 @@ 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',
"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',
"IfcBuildingElementProxy",
"IfcChimney",
"IfcCovering",
"IfcDoor",
"IfcShadingDevice",
"IfcWindow",
"IfcDistributionControlElement",
"IfcDistributionChamberElement",
"IfcEnergyConversionDevice",
"IfcFlowController",
"IfcFlowMovingDevice",
"IfcFlowStorageDevice",
"IfcFlowTerminal",
"IfcFlowTreatmentDevice",
"IfcDiscreteAccessory",
"IfcTendon",
"IfcTendonAnchor",
"IfcVibrationIsolator",
"IfcFurnishingElement",
"IfcGeographicElement",
"IfcTransportElement",
]
class Selector():
class Selector:
def parse(self, ifc_file, query):
self.file = ifc_file
l = lark.Lark('''start: query (lfunction query)*
l = lark.Lark(
"""start: query (lfunction query)*
query: selector | group
group: "(" query (lfunction query)* ")"
selector: (inverse_relationship)? guid_selector | (inverse_relationship)? class_selector
@@ -111,7 +113,8 @@ class Selector():
NEWLINE: (CR? LF)+
%ignore WS // Disregard spaces in text
''')
"""
)
start = l.parse(query)
return self.get_group(start)
@@ -119,24 +122,24 @@ class Selector():
def get_group(self, group):
lfunction = None
for child in group.children:
if child.data == 'query':
if child.data == "query":
new_results = self.get_query(child)
if not lfunction:
results = new_results
elif lfunction == 'or':
elif lfunction == "or":
results.extend(new_results)
elif lfunction == 'and':
elif lfunction == "and":
results = list(set(results).intersection(new_results))
results = list(set(results))
elif child.data == 'lfunction':
elif child.data == "lfunction":
lfunction = child.children[0].data
return results
def get_query(self, query):
for child in query.children:
if child.data == 'selector':
if child.data == "selector":
return self.get_selector(child)
elif child.data == 'group':
elif child.data == "group":
return self.get_group(child)
def get_selector(self, selector):
@@ -147,9 +150,9 @@ class Selector():
inverse_relationship = selector.children[0]
class_or_guid_selector = selector.children[1]
if class_or_guid_selector.data == 'class_selector':
if class_or_guid_selector.data == "class_selector":
results = self.get_class_selector(class_or_guid_selector)
elif class_or_guid_selector.data == 'guid_selector':
elif class_or_guid_selector.data == "guid_selector":
results = self.get_guid_selector(class_or_guid_selector)
if not inverse_relationship:
@@ -159,26 +162,25 @@ class Selector():
def parse_inverse_relationship(self, elements, inverse_relationship):
results = []
for element in elements:
if inverse_relationship == 'types':
if hasattr(element, 'Types') and element.Types:
if inverse_relationship == "types":
if hasattr(element, "Types") and element.Types:
results.extend(element.Types[0].RelatedObjects)
elif hasattr(element, 'ObjectTypeOf') and element.ObjectTypeOf:
elif hasattr(element, "ObjectTypeOf") and element.ObjectTypeOf:
results.extend(element.ObjectTypeOf[0].RelatedObjects)
elif inverse_relationship == 'contains_elements' \
and hasattr(element, 'ContainsElements'):
elif inverse_relationship == "contains_elements" and hasattr(element, "ContainsElements"):
for relationship in element.ContainsElements:
results.extend(relationship.RelatedElements)
return results
def get_class_selector(self, class_selector):
if class_selector.children[0] == 'COBie':
if class_selector.children[0] == "COBie":
elements = []
for ifc_class in cobie_component_assets:
try:
elements += self.file.by_type(ifc_class)
except:
pass
elif class_selector.children[0] == 'COBieType':
elif class_selector.children[0] == "COBieType":
elements = []
for ifc_class in cobie_type_assets:
try:
@@ -187,8 +189,7 @@ class Selector():
pass
else:
elements = self.file.by_type(class_selector.children[0])
if len(class_selector.children) > 1 \
and class_selector.children[1].data == 'filter':
if len(class_selector.children) > 1 and class_selector.children[1].data == "filter":
return self.filter_elements(elements, class_selector.children[1])
return elements
@@ -196,7 +197,7 @@ class Selector():
results = []
key = filter_rule.children[0].children[0]
if not isinstance(key, str):
key = key.children[0] + '.' + key.children[1]
key = key.children[0] + "." + key.children[1]
comparison = value = None
if len(filter_rule.children) > 1:
comparison = filter_rule.children[1].children[0].data
@@ -205,42 +206,40 @@ class Selector():
element_value = self.get_element_value(element, key)
if not element_value:
continue
if not comparison \
or self.filter_element(element, element_value, comparison, value):
if not comparison or self.filter_element(element, element_value, comparison, value):
results.append(element)
return results
def get_element_value(self, element, key):
if '.' in key \
and key.split('.')[0] == 'type':
if "." in key and key.split(".")[0] == "type":
try:
element = ifcopenshell.util.element.get_type(element)
if not element:
return None
except:
return
key = '.'.join(key.split('.')[1:])
key = ".".join(key.split(".")[1:])
info = element.get_info()
if key in info:
return info[key]
elif '.' in key:
pset_name, prop = key.split('.')
elif "." in key:
pset_name, prop = key.split(".")
psets = ifcopenshell.util.element.get_psets(element)
if pset_name in psets and prop in psets[pset_name]:
return psets[pset_name][prop]
def filter_element(self, element, element_value, comparison, value):
if comparison == 'equal':
if comparison == "equal":
return str(element_value) == value
elif comparison == 'contains':
elif comparison == "contains":
return value in str(element_value)
elif comparison == 'morethan':
elif comparison == "morethan":
return element_value > float(value)
elif comparison == 'lessthan':
elif comparison == "lessthan":
return element_value < float(value)
elif comparison == 'morethanequalto':
elif comparison == "morethanequalto":
return element_value >= float(value)
elif comparison == 'lessthanequalto':
elif comparison == "lessthanequalto":
return element_value <= float(value)
return False
@@ -1,55 +1,99 @@
from math import pi
prefixes = {'EXA': 1e18, 'PETA': 1e15, 'TERA': 1e12, 'GIGA': 1e9, 'MEGA':
1e6, 'KILO': 1e3, 'HECTO': 1e2, 'DECA': 1e1, 'DECI': 1e-1, 'CENTI':
1e-2, 'MILLI': 1e-3, 'MICRO': 1e-6, 'NANO': 1e-9, 'PICO': 1e-12,
'FEMTO': 1e-15, 'ATTO': 1e-18}
prefixes = {
"EXA": 1e18,
"PETA": 1e15,
"TERA": 1e12,
"GIGA": 1e9,
"MEGA": 1e6,
"KILO": 1e3,
"HECTO": 1e2,
"DECA": 1e1,
"DECI": 1e-1,
"CENTI": 1e-2,
"MILLI": 1e-3,
"MICRO": 1e-6,
"NANO": 1e-9,
"PICO": 1e-12,
"FEMTO": 1e-15,
"ATTO": 1e-18,
}
unit_names = ['AMPERE', 'BECQUEREL', 'CANDELA', 'COULOMB',
'CUBIC_METRE', 'DEGREE CELSIUS', 'FARAD', 'GRAM', 'GRAY', 'HENRY',
'HERTZ', 'JOULE', 'KELVIN', 'LUMEN', 'LUX', 'MOLE', 'NEWTON', 'OHM',
'PASCAL', 'RADIAN', 'SECOND', 'SIEMENS', 'SIEVERT', 'SQUARE METRE',
'METRE', 'STERADIAN', 'TESLA', 'VOLT', 'WATT', 'WEBER']
unit_names = [
"AMPERE",
"BECQUEREL",
"CANDELA",
"COULOMB",
"CUBIC_METRE",
"DEGREE CELSIUS",
"FARAD",
"GRAM",
"GRAY",
"HENRY",
"HERTZ",
"JOULE",
"KELVIN",
"LUMEN",
"LUX",
"MOLE",
"NEWTON",
"OHM",
"PASCAL",
"RADIAN",
"SECOND",
"SIEMENS",
"SIEVERT",
"SQUARE METRE",
"METRE",
"STERADIAN",
"TESLA",
"VOLT",
"WATT",
"WEBER",
]
si_conversions = {
'inch': 0.0254,
'foot': 0.3048,
'yard': 0.914,
'mile': 1609,
'square inch': 0.0006452,
'square foot': 0.09290304,
'square yard': 0.83612736,
'acre': 4046.86,
'square mile': 2588881,
'cubic inch': 0.00001639,
'cubic foot': 0.02831684671168849,
'cubic yard': 0.7636,
'litre': 0.001,
'fluid ounce UK': 0.0000284130625,
'fluid ounce US': 0.00002957353,
'pint UK': 0.000568,
'pint US': 0.000473,
'gallon UK': 0.004546,
'gallon US': 0.003785,
'degree': pi/180,
'ounce': 0.02835,
'pound': 0.454,
'ton UK': 1016.0469088,
'ton US': 907.18474,
'lbf': 4.4482216153,
'kip': 4448.2216153,
'psi': 6894.7572932,
'ksi': 6894757.2932,
'minute': 60,
'hour': 3600,
'day': 86400,
'btu': 1055.056}
"inch": 0.0254,
"foot": 0.3048,
"yard": 0.914,
"mile": 1609,
"square inch": 0.0006452,
"square foot": 0.09290304,
"square yard": 0.83612736,
"acre": 4046.86,
"square mile": 2588881,
"cubic inch": 0.00001639,
"cubic foot": 0.02831684671168849,
"cubic yard": 0.7636,
"litre": 0.001,
"fluid ounce UK": 0.0000284130625,
"fluid ounce US": 0.00002957353,
"pint UK": 0.000568,
"pint US": 0.000473,
"gallon UK": 0.004546,
"gallon US": 0.003785,
"degree": pi / 180,
"ounce": 0.02835,
"pound": 0.454,
"ton UK": 1016.0469088,
"ton US": 907.18474,
"lbf": 4.4482216153,
"kip": 4448.2216153,
"psi": 6894.7572932,
"ksi": 6894757.2932,
"minute": 60,
"hour": 3600,
"day": 86400,
"btu": 1055.056,
}
def get_prefix(text):
for prefix in prefixes.keys():
if prefix in text.upper():
return prefix
def get_prefix_multiplier(text):
if not text:
return 1
@@ -58,11 +102,13 @@ def get_prefix_multiplier(text):
return prefixes[prefix]
return 1
def get_unit_name(text):
for name in unit_names:
if name in text.upper().replace('METER', 'METRE'):
if name in text.upper().replace("METER", "METRE"):
return name
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
"""Converts between length, area, and volume units
@@ -81,18 +127,18 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit):
value *= si_conversions[from_unit]
elif from_prefix:
value *= get_prefix_multiplier(from_prefix)
if 'SQUARE' in from_unit:
if "SQUARE" in from_unit:
value *= get_prefix_multiplier(from_prefix)
elif 'CUBIC' in from_unit:
elif "CUBIC" in from_unit:
value *= get_prefix_multiplier(from_prefix)
value *= get_prefix_multiplier(from_prefix)
if to_unit in si_conversions:
return value * (1 / si_conversions[to_unit])
elif to_prefix:
value *= (1 / get_prefix_multiplier(to_prefix))
if 'SQUARE' in from_unit:
value *= (1 / get_prefix_multiplier(to_prefix))
elif 'CUBIC' in from_unit:
value *= (1 / get_prefix_multiplier(to_prefix))
value *= (1 / get_prefix_multiplier(to_prefix))
value *= 1 / get_prefix_multiplier(to_prefix)
if "SQUARE" in from_unit:
value *= 1 / get_prefix_multiplier(to_prefix)
elif "CUBIC" in from_unit:
value *= 1 / get_prefix_multiplier(to_prefix)
value *= 1 / get_prefix_multiplier(to_prefix)
return value