From fcf2c3743235b2bc712e65b71318d900d72799c3 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 19 Jun 2024 23:41:46 +1000 Subject: [PATCH] New SetFalseOrigin patch recipe to make it easy to "Just Fix It!" This combines the best practices of a few other patch recipes to make it easy to say "this point here is this world coordinate". --- .../api/georeference/add_georeferencing.py | 14 +-- .../ifcpatch/recipes/SetFalseOrigin.py | 97 +++++++++++++++++++ 2 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py index 8b6ed48337..38f766d4e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py @@ -42,7 +42,8 @@ def add_georeferencing(file: ifcopenshell.file) -> None: ifcopenshell.api.run("georeference.add_georeferencing", model) """ - + if file.by_type("IfcProjectedCRS"): + return source_crs = None for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): if context.ContextType == "Model": @@ -50,14 +51,7 @@ def add_georeferencing(file: ifcopenshell.file) -> None: break if not source_crs: return - projected_crs = file.create_entity("IfcProjectedCRS", **{"Name": ""}) + projected_crs = file.create_entity("IfcProjectedCRS", Name="") file.create_entity( - "IfcMapConversion", - **{ - "SourceCRS": source_crs, - "TargetCRS": projected_crs, - "Eastings": 0, - "Northings": 0, - "OrthogonalHeight": 0, - } + "IfcMapConversion", SourceCRS=source_crs, TargetCRS=projected_crs, Eastings=0, Northings=0, OrthogonalHeight=0 ) diff --git a/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py b/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py new file mode 100644 index 0000000000..9d4d40ebcd --- /dev/null +++ b/src/ifcpatch/ifcpatch/recipes/SetFalseOrigin.py @@ -0,0 +1,97 @@ +# IfcPatch - IFC patching utiliy +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of IfcPatch. +# +# IfcPatch is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcPatch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcPatch. If not, see . + +import ifcopenshell +import ifcopenshell.api.georeference +from ifcpatch.recipes import OffsetObjectPlacements, SetWorldCoordinateSystem + + +class Patcher: + def __init__( + self, + src, + file, + logger, + name: str = "EPSG:1234", + x: float = 0, + y: float = 0, + z: float = 0, + e: float = 0, + n: float = 0, + h: float = 0, + gn_angle: float = 0, + rotate_angle: float = 0, + ): + """Sets a false origin with a map conversion in a model + + On IFC2X3 models, a EPset_MapConversion is used. + + :param x: The local X coordinate which will become the new false origin. + :param y: The local Y coordinate which will become the new false origin. + :param z: The local Z coordinate which will become the new false origin. + :param e: The easting which the false origin correlates to. + :param n: The northing which the false origin correlates to. + :param h: The height which the false origin correlates to. + :param gn_angle: The anticlockwise angle to grid north. + :param rotate_angle: An anticlockwise angle to rotate the model by if + necessary (pivoted by the false origin). + + Example: + + .. code:: python + + # Set the current origin 0,0,0 to correlate to map coordinates 1000,1000,0 and a grid north of 15. + ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "SetFalseOrigin", "arguments": [0, 0, 0, 1000, 1000, 0, 15, 0]}) + """ + self.src = src + self.file = file + self.logger = logger + self.name = name + self.x = x + self.y = y + self.z = z + self.e = e + self.n = n + self.h = h + self.gn_angle = gn_angle + self.rotate_angle = rotate_angle + + def patch(self): + SetWorldCoordinateSystem.Patcher(self.src, self.file, self.logger, x=0, y=0, z=0, ax=0, ay=0, az=0).patch() + map_conversion = { + "Eastings": self.e, + "Northings": self.n, + "OrthogonalHeight": self.h, + } + if self.gn_angle: + a, o = ifcopenshell.util.geolocation.angle2xaxis(self.gn_angle) + map_conversion.update({"XAxisAbscissa": a, "XAxisOrdinate": o}) + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.georeference.edit_georeferencing( + self.file, projected_crs={"Name": self.name}, map_conversion=map_conversion + ) + OffsetObjectPlacements.Patcher( + self.src, + self.file, + self.logger, + x=-self.x, + y=-self.y, + z=self.z, + should_rotate_first=False, + ax=self.rotate_angle or None, + ).patch()