diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py index 38f766d4e8..1c4656f625 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py @@ -17,6 +17,8 @@ # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.api.pset +import ifcopenshell.util.element def add_georeferencing(file: ifcopenshell.file) -> None: @@ -42,6 +44,25 @@ def add_georeferencing(file: ifcopenshell.file) -> None: ifcopenshell.api.run("georeference.add_georeferencing", model) """ + if file.schema == "IFC2X3": + if not (project := file.by_type("IfcProject")): + return + project = project[0] + if ifcopenshell.util.element.get_pset(project, "ePSet_ProjectedCRS"): + return + conversion = ifcopenshell.api.pset.add_pset(file, project, "ePSet_MapConversion") + crs = ifcopenshell.api.pset.add_pset(file, project, "ePSet_ProjectedCRS") + ifcopenshell.api.pset.edit_pset(file, crs, properties={"Name": ""}) + ifcopenshell.api.pset.edit_pset( + file, + conversion, + properties={ + "Eastings": file.createIfcLengthMeasure(0), + "Northings": file.createIfcLengthMeasure(0), + "OrthogonalHeight": file.createIfcLengthMeasure(0), + }, + ) + return if file.by_type("IfcProjectedCRS"): return source_crs = None diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py index 12ac54693a..ec01435505 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py @@ -100,13 +100,36 @@ def edit_georeferencing( class Usecase: def execute(self): + self.set_true_north() + if self.file.schema == "IFC2X3": + if not (project := self.file.by_type("IfcProject")): + return + project = project[0] + if (crs := ifcopenshell.util.element.get_pset(project, "ePSet_ProjectedCRS")): + crs = self.file.by_id(crs["id"]) + for k, v in self.settings["projected_crs"].items(): + if k == "Description": + v = self.file.createIfcText(v) + elif k == "Name": + v = self.file.createIfcLabel(v) + else: + v = self.file.createIfcIdentifier(v) + ifcopenshell.api.pset.edit_pset(self.file, crs, properties=self.settings["projected_crs"]) + if (conversion := ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion")): + conversion = self.file.by_id(conversion["id"]) + for k, v in self.settings["map_conversion"].items(): + if k in ("XAxisAbscissa", "XAxisOrdinate", "Scale"): + v = self.file.createIfcReal(v) + else: + v = self.file.createIfcLengthMeasure(v) + ifcopenshell.api.pset.edit_pset(self.file, conversion, properties=self.settings["map_conversion"]) + return map_conversion = self.file.by_type("IfcMapConversion")[0] projected_crs = self.file.by_type("IfcProjectedCRS")[0] for name, value in self.settings["map_conversion"].items(): setattr(map_conversion, name, value) for name, value in self.settings["projected_crs"].items(): setattr(projected_crs, name, value) - self.set_true_north() def set_true_north(self): if not self.settings["true_north"]: diff --git a/src/ifcopenshell-python/test/api/georeference/__init__.py b/src/ifcopenshell-python/test/api/georeference/__init__.py new file mode 100644 index 0000000000..8bed51a56f --- /dev/null +++ b/src/ifcopenshell-python/test/api/georeference/__init__.py @@ -0,0 +1,17 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell 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. +# +# IfcOpenShell 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 IfcOpenShell. If not, see . diff --git a/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py b/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py new file mode 100644 index 0000000000..df2c6de583 --- /dev/null +++ b/src/ifcopenshell-python/test/api/georeference/test_add_georeferencing.py @@ -0,0 +1,69 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell 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. +# +# IfcOpenShell 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 IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api.root +import ifcopenshell.api.context +import ifcopenshell.api.georeference +import ifcopenshell.util.element + + +class TestAddGeoreferencing(test.bootstrap.IFC4): + def test_adding_georeferencing(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.context.add_context(self.file, "Model") + ifcopenshell.api.georeference.add_georeferencing(self.file) + assert len(self.file.by_type("IfcMapConversion")) == 1 + assert len(self.file.by_type("IfcProjectedCRS")) == 1 + assert (conversion := self.file.by_type("IfcMapConversion")[0]) + assert (crs := self.file.by_type("IfcProjectedCRS")[0]) + assert conversion.Eastings == conversion.Northings == conversion.OrthogonalHeight == 0 + assert crs.Name == "" + + def test_not_doing_anything_if_no_model_context(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.context.add_context(self.file, "Plan") + ifcopenshell.api.georeference.add_georeferencing(self.file) + assert len(self.file.by_type("IfcMapConversion")) == 0 + assert len(self.file.by_type("IfcProjectedCRS")) == 0 + + def test_not_adding_georeferencing_twice(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.context.add_context(self.file, "Model") + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.georeference.add_georeferencing(self.file) + assert len(self.file.by_type("IfcMapConversion")) == 1 + assert len(self.file.by_type("IfcProjectedCRS")) == 1 + + +class TestAddGeoreferencingIFC2X3(test.bootstrap.IFC2X3): + def test_adding_georeferencing(self): + project = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.georeference.add_georeferencing(self.file) + conversion = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion", verbose=True) + crs = ifcopenshell.util.element.get_pset(project, "ePSet_ProjectedCRS", verbose=True) + assert len(self.file.by_type("IfcPropertySet")) == 2 + assert conversion + assert crs + assert crs["Name"]["value"] == "" + assert self.file.by_id(crs["Name"]["id"]).NominalValue.is_a("IfcLabel") + assert conversion["Eastings"]["value"] == 0 + assert self.file.by_id(conversion["Eastings"]["id"]).NominalValue.is_a("IfcLengthMeasure") + assert conversion["Northings"]["value"] == 0 + assert conversion["OrthogonalHeight"]["value"] == 0 diff --git a/src/ifcopenshell-python/test/api/georeference/test_edit_georeferencing.py b/src/ifcopenshell-python/test/api/georeference/test_edit_georeferencing.py new file mode 100644 index 0000000000..55fd79a6a9 --- /dev/null +++ b/src/ifcopenshell-python/test/api/georeference/test_edit_georeferencing.py @@ -0,0 +1,74 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell 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. +# +# IfcOpenShell 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 IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api.root +import ifcopenshell.api.context +import ifcopenshell.api.georeference +import ifcopenshell.util.element + + +class TestEditGeoreferencing(test.bootstrap.IFC4): + def test_editing_georeferencing(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.context.add_context(self.file, "Model") + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.georeference.edit_georeferencing( + self.file, + projected_crs={"Name": "EPSG:7856"}, + map_conversion={ + "Eastings": 123.45, + "Northings": 234.56, + }, + ) + crs = self.file.by_type("IfcProjectedCRS")[0] + assert crs.Name == "EPSG:7856" + conversion = self.file.by_type("IfcMapConversion")[0] + assert conversion.Eastings == 123.45 + assert conversion.Northings == 234.56 + + def test_editing_true_north(self): + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + model = ifcopenshell.api.context.add_context(self.file, "Model") + plan = ifcopenshell.api.context.add_context(self.file, "Plan") + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.georeference.edit_georeferencing(self.file, true_north=[0., 1.]) + assert model.TrueNorth[0] == (0., 1., 0.) + assert plan.TrueNorth[0] == (0., 1.) + + +class TestEditGeoreferencingIFC2X3(test.bootstrap.IFC2X3): + def test_editing_georeferencing(self): + project = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + ifcopenshell.api.georeference.add_georeferencing(self.file) + ifcopenshell.api.georeference.edit_georeferencing( + self.file, + projected_crs={"Name": "EPSG:7856"}, + map_conversion={ + "Eastings": 123.45, + "Northings": 234.56, + }, + ) + conversion = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion", verbose=True) + crs = ifcopenshell.util.element.get_pset(project, "ePSet_ProjectedCRS", verbose=True) + assert crs["Name"]["value"] == "EPSG:7856" + assert self.file.by_id(crs["Name"]["id"]).NominalValue.is_a("IfcLabel") + assert conversion["Eastings"]["value"] == 123.45 + assert self.file.by_id(conversion["Eastings"]["id"]).NominalValue.is_a("IfcLengthMeasure") + assert conversion["Northings"]["value"] == 234.56 + assert conversion["OrthogonalHeight"]["value"] == 0