From 252479189c548e7816cb4261813735f927ec803c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 28 Jul 2020 16:41:13 +1000 Subject: [PATCH] Refactor geolocation MicroMVD definitions --- .../features/steps/geolocation.py | 117 +++++++++++------- src/ifcbimtester/features/steps/utils.py | 34 +++++ 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/src/ifcbimtester/features/steps/geolocation.py b/src/ifcbimtester/features/steps/geolocation.py index 417cc6eddb..b15e902d6d 100644 --- a/src/ifcbimtester/features/steps/geolocation.py +++ b/src/ifcbimtester/features/steps/geolocation.py @@ -1,5 +1,5 @@ from behave import step -from utils import IfcFile +from utils import IfcFile, assert_number, assert_pset, assert_attribute import math import ifcopenshell.util import ifcopenshell.util.element @@ -9,18 +9,6 @@ def step_impl(context, ifc_class): assert len(IfcFile.get().by_type(ifc_class)) >= 1, 'An element of {} could not be found'.format(ifc_class) -def check_ifc2x3_geolocation(pset_name, prop_name=None, value=None): - for site in IfcFile.get().by_type('IfcSite'): - psets = ifcopenshell.util.element.get_psets(site) - if pset_name not in psets: - assert False, 'The site {} does not have a property set named {}'.format(site, pset_name) - if not prop_name: - continue - if prop_name not in psets[pset_name]: - assert False, 'The site {} does not have a property named "{}" in {}'.format(site, prop_name, pset_name) - actual_value = psets[pset_name][prop_name] - assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) - def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_assert=True): if entity_name not in IfcFile.bookmarks: has_entity = False @@ -53,56 +41,72 @@ def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_asser @step(u'The project must have coordinate reference system data') def step_impl(context): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS') + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS') + return check_ifc4_geolocation('IfcProjectedCRS') @step(u'The name of the CRS must be {coordinate_reference_name}') def step_impl(context, coordinate_reference_name): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'Name', coordinate_reference_name) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'Name', coordinate_reference_name) + return check_ifc4_geolocation('IfcProjectedCRS', 'Name', coordinate_reference_name) @step(u'The description of the CRS must be {value}') def step_impl(context, value): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'Description', value) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'Description', value) + return check_ifc4_geolocation('IfcProjectedCRS', 'Description', value) @step(u'The geodetic datum must be {coordinate_reference_name}') def step_impl(context, coordinate_reference_name): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'GeodeticDatum', coordinate_reference_name) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'GeodeticDatum', coordinate_reference_name) + return check_ifc4_geolocation('IfcProjectedCRS', 'GeodeticDatum', coordinate_reference_name) @step(u'The vertical datum must be {coordinate_reference_name}') def step_impl(context, coordinate_reference_name): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'VerticalDatum', coordinate_reference_name) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'VerticalDatum', coordinate_reference_name) + return check_ifc4_geolocation('IfcProjectedCRS', 'VerticalDatum', coordinate_reference_name) @step(u'The map projection must be {coordinate_reference_name}') def step_impl(context, coordinate_reference_name): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'MapProjection', coordinate_reference_name) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'MapProjection', coordinate_reference_name) + return check_ifc4_geolocation('IfcProjectedCRS', 'MapProjection', coordinate_reference_name) @step(u'The map zone must be {coordinate_reference_name}') def step_impl(context, coordinate_reference_name): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'MapZone', coordinate_reference_name) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'MapZone', coordinate_reference_name) + return check_ifc4_geolocation('IfcProjectedCRS', 'MapZone', coordinate_reference_name) @step(u'The map unit must be {unit}') def step_impl(context, unit): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_ProjectedCRS', 'MapUnit', unit) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_ProjectedCRS', 'MapUnit', unit) + return actual_value = check_ifc4_geolocation('IfcProjectedCRS', 'MapUnit', should_assert=False) if actual_value.is_a('IfcSIUnit'): prefix = actual_value.Prefix if actual_value.Prefix else '' @@ -115,49 +119,44 @@ def step_impl(context, unit): @step(u'The project must have coordinate transformations to convert from local to global coordinates') def step_impl(context): if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion') + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_MapConversion') check_ifc4_geolocation('IfcMapConversion') @step(u'The eastings of the model must be offset by {number} to derive its global coordinates') def step_impl(context, number): - try: - number = float(number) - except: - assert False, 'A number should be specified, not {}'.format(number) + number = assert_number(number) if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion', 'Eastings', number) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_MapConversion', 'Eastings', number) + return check_ifc4_geolocation('IfcMapConversion', 'Eastings', number) @step(u'The northings of the model must be offset by {number} to derive its global coordinates') def step_impl(context, number): - try: - number = float(number) - except: - assert False, 'A number should be specified, not {}'.format(number) + number = assert_number(number) if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion', 'Northings', number) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_MapConversion', 'Northings', number) + return check_ifc4_geolocation('IfcMapConversion', 'Northings', number) @step(u'The height of the model must be offset by {number} to derive its global coordinates') def step_impl(context, number): - try: - number = float(number) - except: - assert False, 'A number should be specified, not {}'.format(number) + number = assert_number(number) if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion', 'OrthogonalHeight', number) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_MapConversion', 'OrthogonalHeight', number) + return check_ifc4_geolocation('IfcMapConversion', 'OrthogonalHeight', number) @step(u'The model must be rotated clockwise by {number} to derive its global coordinates') def step_impl(context, number): - try: - number = float(number) - except: - assert False, 'A number should be specified, not {}'.format(number) + number = assert_number(number) if IfcFile.get().schema == 'IFC2X3': return check_ifc2x3_geolocation('EPset_MapConversion', 'Height', number) abscissa = check_ifc4_geolocation('IfcMapConversion', 'XAxisAbscissa', should_assert=False) @@ -170,10 +169,36 @@ def step_impl(context, number): @step(u'The model must be scaled along the horizontal axis by {number} to derive its global coordinates') def step_impl(context, number): - try: - number = float(number) - except: - assert False, 'A number should be specified, not {}'.format(number) + number = assert_number(number) if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion', 'Scale', number) + for site in IfcFile.get().by_type('IfcSite'): + assert_pset(site, 'EPset_MapConversion', 'Scale', number) + return check_ifc4_geolocation('IfcMapConversion', 'Scale', number) + + +@step(u'The site {guid} has a longitude of {number}') +def step_impl(context, guid, number): + number = assert_number(number) + site = IfcFile.by_guid(guid) + if not site.is_a('IfcSite'): + assert False, 'The element {} is not an IfcSite'.format(site) + assert_attribute(site, 'RefLongitude', number) + + +@step(u'The site {guid} has a latitude of {number}') +def step_impl(context, guid, number): + number = assert_number(number) + site = IfcFile.by_guid(guid) + if not site.is_a('IfcSite'): + assert False, 'The element {} is not an IfcSite'.format(site) + assert_attribute(site, 'RefLatitude', number) + + +@step(u'The site {guid} has an elevation of {number}') +def step_impl(context, guid, number): + number = assert_number(number) + site = IfcFile.by_guid(guid) + if not site.is_a('IfcSite'): + assert False, 'The element {} is not an IfcSite'.format(site) + assert_attribute(site, 'RefElevation', number) diff --git a/src/ifcbimtester/features/steps/utils.py b/src/ifcbimtester/features/steps/utils.py index 5483da60e7..8bd89e11fb 100644 --- a/src/ifcbimtester/features/steps/utils.py +++ b/src/ifcbimtester/features/steps/utils.py @@ -1,4 +1,6 @@ import ifcopenshell +import ifcopenshell.util +import ifcopenshell.util.element class IfcFile(object): file = None @@ -13,3 +15,35 @@ class IfcFile(object): if not cls.file: assert False, 'No file was loaded, so this requirement cannot be checked' return cls.file + + @classmethod + def by_guid(cls, guid): + try: + return cls.get().by_guid(guid) + except: + assert False, 'An element with the ID {} could not be found.'.format(guid) + +def assert_number(number): + try: + return float(number) + except ValueError: + assert False, 'A number should be specified, not {}'.format(number) + +def assert_attribute(element, name, value): + if not hasattr(element, name): + assert False, 'The element {} does not have the attribute {}'.format(element, name) + actual_value = getattr(element, name) + assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) + +def assert_pset(element, pset_name, prop_name=None, value=None): + psets = ifcopenshell.util.element.get_psets(site) + if pset_name not in psets: + assert False, 'The element {} does not have a property set named {}'.format(element, pset_name) + if prop_name is None: + return psets[pset_name] + if prop_name not in psets[pset_name]: + assert False, 'The element {} does not have a property named "{}" in the pset "{}"'.format(element, prop_name, pset_name) + if value is None: + return psets[pset_name][prop_name] + actual_value = psets[pset_name][prop_name] + assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value)