diff --git a/src/ifcbimtester/features/steps/geocoding.py b/src/ifcbimtester/features/steps/geocoding.py index daf690a62e..40d62b0345 100644 --- a/src/ifcbimtester/features/steps/geocoding.py +++ b/src/ifcbimtester/features/steps/geocoding.py @@ -1,14 +1,24 @@ from behave import step from utils import IfcFile, assert_attribute, assert_type -def check_geocode_attribute(guid, ifc_class, name, value): + +def get_ifc_class_from_spatial_type(spatial_type): + if spatial_type == 'site': + return 'IfcSite' + elif spatial_type == 'building': + return 'IfcBuilding' + return 'IfcFacility' + + +def check_geocode_attribute(guid, spatial_type, name, value): element = IfcFile.by_guid(guid) - assert_type(element, ifc_class) + assert_type(element, get_ifc_class_from_spatial_type(spatial_type)) assert_attribute(element, name, value) -def check_geocode_address(guid, ifc_class, name, value): +def check_geocode_address(guid, spatial_type, name, value): element = IfcFile.by_guid(guid) + ifc_class = get_ifc_class_from_spatial_type(spatial_type) assert_type(element, ifc_class) if ifc_class == 'IfcSite': address_name = 'SiteAddress' @@ -20,50 +30,48 @@ def check_geocode_address(guid, ifc_class, name, value): use_step_matcher('re') @step('The (site|building|facility) (?P.*) has a name of (?P.*)') -def step_impl(context, _unused, guid, name): - check_geocode_attribute(guid, 'IfcSite', 'Name', name) +def step_impl(context, spatial_type, guid, name): + check_geocode_attribute(guid, spatial_type, 'Name', name) -@step('The (site|building|facility) (?P.*) has a description of (?P.*)') -def step_impl(context, _unused, guid, description): - check_geocode_attribute(guid, 'IfcSite', 'Description', description) - - -@step('The (site|building) (?P.*) has a land title number of (?P.*)') -def step_impl(context, _unused, guid, land_title_number): - check_geocode_attribute(guid, 'IfcSite', 'LandTitleNumber', land_title_number) +@step('The (site|building|facility) (?P.*) has a description of "(?P.*)"') +def step_impl(context, spatial_type, guid, description): + check_geocode_attribute(guid, spatial_type, 'Description', description) +@step('The site (?P.*) has a land title number of (?P.*)') +def step_impl(context, guid, land_title_number): + check_geocode_attribute(guid, 'site', 'LandTitleNumber', land_title_number) @step('The (site|building) (?P.*) has the address "(?P.*)"') -def step_impl(context, _unused, guid, address_lines): - check_geocode_address(guid, 'IfcSite', 'AddressLines', address_lines.split('\\n')) +def step_impl(context, spatial_type, guid, address_lines): + check_geocode_address(guid, spatial_type, 'AddressLines', address_lines.split('\\n')) @step('The (site|building) (?P.*) has a postal box of (?P.*)') -def step_impl(context, _unused, guid, postal_box): - check_geocode_address(guid, 'IfcSite', 'PostalBox', postal_box) +def step_impl(context, spatial_type, guid, postal_box): + check_geocode_address(guid, spatial_type, 'PostalBox', postal_box) @step('The (site|building) (?P.*) is in the town (?P.*)') -def step_impl(context, _unused, guid, town): - check_geocode_address(guid, 'IfcSite', 'Town', town) +def step_impl(context, spatial_type, guid, town): + check_geocode_address(guid, spatial_type, 'Town', town) @step('The (site|building) (?P.*) is in the region (?P.*)') -def step_impl(context, _unused, guid, region): - check_geocode_address(guid, 'IfcSite', 'Region', region) +def step_impl(context, spatial_type, guid, region): + check_geocode_address(guid, spatial_type, 'Region', region) @step('The (site|building) (?P.*) has a post code of (?P.*)') -def step_impl(context, _unused, guid, post_code): - check_geocode_address(guid, 'IfcSite', 'PostalCode', post_code) +def step_impl(context, spatial_type, guid, post_code): + check_geocode_address(guid, spatial_type, 'PostalCode', post_code) @step('The (site|building) (?P.*) is in the country (?P.*)') -def step_impl(context, _unused, guid, country): - check_geocode_address(guid, 'IfcSite', 'Country', country) +def step_impl(context, spatial_type, guid, country): + check_geocode_address(guid, spatial_type, 'Country', country) -@step('The (site|building) (?P.*) has an address description of (?P.*)') -def step_impl(context, _unused, guid, description): - check_geocode_address(guid, 'IfcSite', 'Description', description) +@step('The (site|building) (?P.*) has an address description of "(?P.*)"') +def step_impl(context, spatial_type, guid, description): + check_geocode_address(guid, spatial_type, 'Description', description) diff --git a/src/ifcbimtester/features/steps/geolocation.py b/src/ifcbimtester/features/steps/geolocation.py index 0c243749c0..2a0fe18dae 100644 --- a/src/ifcbimtester/features/steps/geolocation.py +++ b/src/ifcbimtester/features/steps/geolocation.py @@ -109,6 +109,8 @@ def step_impl(context, unit): assert_pset(site, 'EPset_ProjectedCRS', 'MapUnit', unit) return actual_value = check_ifc4_geolocation('IfcProjectedCRS', 'MapUnit', should_assert=False) + if not actual_value: + assert False, 'A unit was not provided in the projected CRS' if actual_value.is_a('IfcSIUnit'): prefix = actual_value.Prefix if actual_value.Prefix else '' actual_value = prefix + actual_value.Name @@ -162,7 +164,7 @@ def step_impl(context, number): return check_ifc2x3_geolocation('EPset_MapConversion', 'Height', number) abscissa = check_ifc4_geolocation('IfcMapConversion', 'XAxisAbscissa', should_assert=False) ordinate = check_ifc4_geolocation('IfcMapConversion', 'XAxisOrdinate', should_assert=False) - actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate) * -1, 3) + actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3) value = round(number, 3) assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) @@ -183,7 +185,8 @@ def step_impl(context, guid, number): site = IfcFile.by_guid(guid) if not site.is_a('IfcSite'): assert False, 'The element {} is not an IfcSite'.format(site) - number = ifcopenshell.util.geolocation.dd2dms(number) + ref = assert_attribute(site, 'RefLongitude') + number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) assert_attribute(site, 'RefLongitude', number) @@ -193,7 +196,8 @@ def step_impl(context, guid, number): site = IfcFile.by_guid(guid) if not site.is_a('IfcSite'): assert False, 'The element {} is not an IfcSite'.format(site) - number = ifcopenshell.util.geolocation.dd2dms(number) + ref = assert_attribute(site, 'RefLatitude') + number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) assert_attribute(site, 'RefLatitude', number) diff --git a/src/ifcbimtester/features/steps/utils.py b/src/ifcbimtester/features/steps/utils.py index dc305033c5..787d21de88 100644 --- a/src/ifcbimtester/features/steps/utils.py +++ b/src/ifcbimtester/features/steps/utils.py @@ -41,7 +41,7 @@ def assert_attribute(element, name, value=None): if not value: if getattr(element, name) is None: assert False, 'The element {} does not have a value for the attribute {}'.format(element, name) - return + return getattr(element, name) if value == 'NULL': value = None actual_value = getattr(element, name) diff --git a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py index a7c5d7fd7d..5455f7e3ec 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py @@ -1,17 +1,21 @@ import math -def dms2dd(degrees, minutes, seconds, milliseconds=0): - dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(milliseconds/3600000.0) +def dms2dd(degrees, minutes, seconds, ms=0): + dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(ms/3600000000.0) return dd -def dd2dms(dd): +def dd2dms(dd, use_ms=False): dd = float(dd) sign = 1 if dd >= 0 else -1 dd = abs(dd) - minutes, seconds = divmod(dd*3600, 60) + if use_ms: + 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 + if use_ms: + 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):