import numpy as np import ifcopenshell.util.geolocation from behave import step from utils import IfcFile from utils import IfcFile, assert_number, assert_type def a2p(o, z, x): y = np.cross(z, x) r = np.eye(4) r[:-1,:-1] = x,y,z r[-1,:-1] = o return r.T def get_axis2placement(plc): z = np.array(plc.Axis.DirectionRatios if plc.Axis else (0,0,1)) x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1,0,0)) o = plc.Location.Coordinates return a2p(o,z,x) def get_local_placement(plc): if plc is None: return np.eye(4) if plc.PlacementRelTo is None: parent = np.eye(4) else: parent = get_local_placement(plc.PlacementRelTo) return np.dot(get_axis2placement(plc.RelativePlacement), parent) def get_decimal_points(value): try: return len(value.split('.')[1]) except: return 0 def get_containing_spatial_elements(element): results = [] if element.is_a('IfcSpatialElement'): results.append(element) for rel in element.Decomposes: if rel.is_a('IfcRelAggregates'): results.append(get_containing_spatial_elements(rel.RelatingObject)) elif element.is_a('IfcElement'): for rel in element.ContainedInStructure: if rel.is_a('ifcRelContainedInSpatialStructure'): results.append(get_containing_spatial_elements(rel.RelatingStructure)) return results @step('There is a datum element {guid} as an {ifc_class}') def step_impl(context, guid, ifc_class): element = IfcFile.by_guid(guid) assert_type(element, ifc_class) @step('The element {guid} has a global easting, northing, and elevation of {easting}, {northing}, and {elevation} respectively') def step_impl(context, guid, easting, northing, elevation): if IfcFile.get().schema == 'IFC2X3': if element.is_a('IfcSite'): site = element else: potential_sites = [s for s in get_containing_spatial_elements(element) if s.is_a('IfcSite')] if potential_sites: site = potential_sites[0] else: assert False, 'The datum element does not belong to a geolocated site' map_conversion = assert_pset(site, 'EPset_MapConversion') else: map_conversion = IfcFile.get().by_type('IfcMapConversion') if map_conversion: map_conversion = map_conversion[0].get_info() else: assert False, 'No map conversion was found in the file' element = IfcFile.by_guid(guid) if not element.ObjectPlacement: assert False, 'The element does not have an object placement: {}'.format(element) m = get_local_placement(element.ObjectPlacement) e, n, h = ifcopenshell.util.geolocation.xyz2enh( m[0][3], m[1][3], m[2][3], float(map_conversion['Eastings']), float(map_conversion['Northings']), float(map_conversion['OrthogonalHeight']), float(map_conversion['XAxisAbscissa']), float(map_conversion['XAxisOrdinate']), float(map_conversion['Scale']), ) element_x = round(e, get_decimal_points(easting)) element_y = round(n, get_decimal_points(northing)) element_z = round(h, get_decimal_points(elevation)) expected_placement = (assert_number(easting), assert_number(northing), assert_number(elevation)) if (element_x, element_y, element_z) != expected_placement: assert False, 'The element {} is meant to have a location of {} but instead we found {}'.format( element, expected_placement, (element_x, element_y, element_z)) @step('The element {guid} has a local X, Y, and Z coordinate of {x}, {y}, and {z} respectively') def step_impl(context, guid, x, y, z): element = IfcFile.by_guid(guid) if not element.ObjectPlacement: assert False, 'The element does not have an object placement: {}'.format(element) m = get_local_placement(element.ObjectPlacement) element_x = round(m[0][3], get_decimal_points(x)) element_y = round(m[1][3], get_decimal_points(y)) element_z = round(m[2][3], get_decimal_points(z)) expected_placement = (assert_number(x), assert_number(y), assert_number(z)) if (element_x, element_y, element_z) != expected_placement: assert False, 'The element {} is meant to have a location of {} but instead we found {}'.format( element, expected_placement, (element_x, element_y, element_z))