From 09a43f13395ff23b237b0429a21e28fa6b190fda Mon Sep 17 00:00:00 2001 From: Richard Brice <37087370+RickBrice@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:12:24 -0700 Subject: [PATCH] get distance along alignment from station New alignment api function to get the horizontal distance along an alignment from a station value --- .../ifcopenshell/api/alignment/__init__.py | 1 + .../alignment/distance_along_from_station.py | 56 +++++++++++++++++++ .../test_distance_along_from_station.py | 55 ++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/alignment/distance_along_from_station.py create mode 100644 src/ifcopenshell-python/test/api/alignment/test_distance_along_from_station.py diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py index 1098c23fe6..e32853f601 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/__init__.py @@ -51,6 +51,7 @@ from .create_alignment_from_csv import create_alignment_from_csv from .create_horizontal_alignment_by_pi_method import create_horizontal_alignment_by_pi_method from .create_geometric_representation import create_geometric_representation from .create_vertical_alignment_by_pi_method import create_vertical_alignment_by_pi_method +from .distance_along_from_station import distance_along_from_station from .get_alignment_layouts import get_alignment_layouts from .get_axis_subcontext import get_axis_subcontext from .get_basis_curve import get_basis_curve diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/distance_along_from_station.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/distance_along_from_station.py new file mode 100644 index 0000000000..8bfbb8f03c --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/distance_along_from_station.py @@ -0,0 +1,56 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2025 Thomas Krijnen +# +# 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 ifcopenshell +import ifcopenshell.api.alignment +import ifcopenshell.api.nest +import ifcopenshell.guid +from ifcopenshell import entity_instance + + +def distance_along_from_station(file: ifcopenshell.file, alignment: entity_instance, station: float) -> float: + """ + Given a station, returns the distance along the horizontal alignment. + + If the alignment does not have stationing defined with an IfcReferent, the start of the alignment is assumed + to be at station 0.0. That is, the station is the distance along. + + .. note:: The current implementation does not account for station equations and assumes stationing is increasing along the alignment. + + :param alignment: the alignment + :param station: station value + :return: distance along the horizontal alignment + + Example: + + .. code:: python + + alignment = model.by_type("IfcAlignment")[0] # alignment with start station 1+00.00 + dist_along = ifcopenshell.api.alignment.distance_along_from_station(model,alignment=alignment,station=200.0) + print(dist_along) # 100.00 + """ + + start_station = 0.0 + components = ifcopenshell.util.element.get_components(alignment) + for c in components: + if c.is_a("IfcReferent") and ifcopenshell.util.element.get_predefined_type(c) == "STATION": + start_station = ifcopenshell.util.element.get_pset(c, name="Pset_Stationing", prop="Station") + break + + dist_along = station - start_station + return dist_along diff --git a/src/ifcopenshell-python/test/api/alignment/test_distance_along_from_station.py b/src/ifcopenshell-python/test/api/alignment/test_distance_along_from_station.py new file mode 100644 index 0000000000..fa42765c56 --- /dev/null +++ b/src/ifcopenshell-python/test/api/alignment/test_distance_along_from_station.py @@ -0,0 +1,55 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2025 Thomas Krijnen +# +# 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 pytest +import ifcopenshell.api.alignment +import ifcopenshell.api.context + + +def test_add_stationing_to_alignment(): + file = ifcopenshell.file(schema="IFC4X3_ADD2") + project = file.createIfcProject(Name="Test") + geometric_representation_context = ifcopenshell.api.context.add_context(file, context_type="Model") + axis_model_representation_subcontext = ifcopenshell.api.context.add_context( + file, + context_type="Model", + context_identifier="Axis", + target_view="MODEL_VIEW", + parent=geometric_representation_context, + ) + + coordinates = [(500.0, 2500.0), (3340.0, 660.0), (4340.0, 5000.0), (7600.0, 4560.0), (8480.0, 2010.0)] + radii = [(1000.0), (1250.0), (950.0)] + vpoints = [(0.0, 100.0), (2000.0, 135.0), (5000.0, 105.0), (7400.0, 153.0), (9800.0, 105.0), (12800.0, 90.0)] + lengths = [(1600.0), (1200.0), (2000.0), (800.0)] + + alignment = ifcopenshell.api.alignment.create_alignment_by_pi_method( + file, "TestAlignment", coordinates, radii, vpoints, lengths + ) + + # test alignment without stationing referent + assert ifcopenshell.api.alignment.distance_along_from_station(file, alignment, 500.0) == pytest.approx(500.0) + + # add stationing referent + ifcopenshell.api.alignment.add_stationing_to_alignment(file, alignment, 10000.0) + + # Station 138+83.96 + assert ifcopenshell.api.alignment.distance_along_from_station(file, alignment, 13883.96) == pytest.approx(3883.96) + + # Station 175+25.36 + assert ifcopenshell.api.alignment.distance_along_from_station(file, alignment, 17525.36) == pytest.approx(7525.36)