2025-07-08 10:09:14 -07:00
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2025 Thomas Krijnen <thomas@aecgeeks.com>
#
# 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 <http://www.gnu.org/licenses/>.
2026-07-09 14:10:33 -07:00
from typing import Optional
2025-12-19 18:53:04 +05:00
2025-07-08 10:09:14 -07:00
import ifcopenshell
import ifcopenshell . api . alignment
2026-05-25 10:31:57 -07:00
from ifcopenshell . api . alignment . update_fallback_position import update_fallback_position
2025-07-08 10:09:14 -07:00
import ifcopenshell . api . pset
import ifcopenshell . guid
2025-09-15 09:52:30 +05:00
import ifcopenshell . util . element
2026-07-09 14:10:33 -07:00
from ifcopenshell import entity_instance
2025-07-08 10:09:14 -07:00
def add_stationing_referent (
file : ifcopenshell . file ,
2026-07-09 14:10:33 -07:00
name : str ,
2025-08-29 12:48:03 -07:00
alignment : entity_instance ,
2025-07-08 10:09:14 -07:00
distance_along : float ,
station : float ,
2026-07-09 14:10:33 -07:00
incoming_station : Optional [ float ] = None ,
2026-07-10 09:46:11 -07:00
on_basis_curve : Optional [ bool ] = None ,
2025-07-08 10:09:14 -07:00
) - > entity_instance :
"""
2026-07-09 14:10:33 -07:00
Adds an IfcReferent to the alignment that defines the stationing system.
2025-07-08 10:09:14 -07:00
2026-07-09 14:10:33 -07:00
:param name: name to assign to IfcReferent.Name, typically a stringized version of the station value
2025-08-29 12:48:03 -07:00
:param alignment: the alignment to receive the referent
:param distance_along: distance along the alignment basis curve
2025-07-08 10:09:14 -07:00
:param station: station value
2026-07-09 14:10:33 -07:00
:param incoming_station: station value of the incoming segment, only set to specify a station equation
2026-07-10 09:46:11 -07:00
:param on_basis_curve: whether the referent is positioned on the basis curve or the alignment curve, if None the function will default to the basis curve
2025-07-08 10:09:14 -07:00
:return: referent
Example:
.. code:: python
alignment = model.by_type( " IfcAlignment " )[0]
2026-07-09 14:10:33 -07:00
ifcopenshell.api.alignment.add_stationing_referent(model,name= " 1+00.0 " ,alignment=alignment,distance_along=0.0,station=100.0)
2025-07-08 10:09:14 -07:00
"""
2026-07-10 09:46:11 -07:00
if on_basis_curve is None :
on_basis_curve = True
curve = ifcopenshell . api . alignment . get_basis_curve ( alignment ) if on_basis_curve else ifcopenshell . api . alignment . get_curve ( alignment )
2025-07-08 10:09:14 -07:00
2025-07-13 16:34:52 -07:00
object_placement = None
representation = None
2026-07-10 09:46:11 -07:00
if curve and curve . is_a ( " IfcCompositeCurve " ) and 0 < len ( curve . Segments ) :
2025-07-08 10:09:14 -07:00
object_placement = file . createIfcLinearPlacement (
RelativePlacement = file . createIfcAxis2PlacementLinear (
Location = file . createIfcPointByDistanceExpression (
DistanceAlong = file . createIfcLengthMeasure ( distance_along ) ,
OffsetLateral = None ,
OffsetVertical = None ,
OffsetLongitudinal = None ,
2026-07-10 09:46:11 -07:00
BasisCurve = curve ,
2025-07-08 10:09:14 -07:00
)
) ,
)
2025-07-13 16:34:52 -07:00
2026-05-25 10:31:57 -07:00
update_fallback_position ( file , object_placement )
else :
object_placement = file . createIfcLocalPlacement (
PlacementRelTo = None ,
RelativePlacement = file . createIfcAxis2Placement2D (
Location = file . createIfcCartesianPoint ( alignment . ObjectPlacement . RelativePlacement . Location . Coordinates )
) ,
2025-08-29 12:48:03 -07:00
)
2025-08-29 14:37:17 -07:00
2025-07-08 10:09:14 -07:00
# this commented out code is what you would do to add a geometric representation of the referent
# the example is a circle. a better way would be to pass a representation into the function
# representation = file.create_entity(
# name="IfcCircle",
# position=file.createIfcAxis2Placement2D(Location=file.createIfcCartesianPoint(Coordinates=(0.0, 0.0)),
# radius=1.0)
# )
# create referent for the station
referent = file . createIfcReferent (
GlobalId = ifcopenshell . guid . new ( ) ,
OwnerHistory = None ,
Name = name ,
Description = None ,
ObjectType = None ,
ObjectPlacement = object_placement ,
Representation = representation ,
2025-08-29 12:48:03 -07:00
PredefinedType = " STATION " ,
2025-07-08 10:09:14 -07:00
)
2026-07-09 14:10:33 -07:00
properties = { " Station " : station }
if incoming_station is not None :
properties [ " IncomingStation " ] = incoming_station
2025-07-08 10:09:14 -07:00
pset_stationing = ifcopenshell . api . pset . add_pset ( file , product = referent , name = " Pset_Stationing " )
2026-07-09 14:10:33 -07:00
ifcopenshell . api . pset . edit_pset ( file , pset = pset_stationing , properties = properties )
2025-08-25 19:58:55 -07:00
2025-08-29 12:48:03 -07:00
nest = ifcopenshell . api . alignment . get_referent_nest ( file , alignment )
2026-05-25 10:31:57 -07:00
if nest is None :
nest = file . createIfcRelNests (
GlobalId = ifcopenshell . guid . new ( ) , RelatingObject = alignment , RelatedObjects = ( referent , )
)
else :
nest . RelatedObjects + = ( referent , )
2025-07-08 10:09:14 -07:00
2025-08-29 14:37:17 -07:00
nest . RelatedObjects = sorted (
nest . RelatedObjects , key = lambda x : ifcopenshell . util . element . get_pset ( x , name = " Pset_Stationing " , prop = " Station " )
)
2025-08-29 12:48:03 -07:00
2025-07-08 10:09:14 -07:00
return referent