View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.io.Serializable;
4   
5   import javax.media.j3d.Bounds;
6   
7   import nl.tudelft.simulation.dsol.animation.LocatableInterface;
8   import nl.tudelft.simulation.language.d3.DirectedPoint;
9   
10  import org.opentrafficsim.core.OTS_SCALAR;
11  import org.opentrafficsim.core.geometry.OTSBuffering;
12  import org.opentrafficsim.core.geometry.OTSGeometryException;
13  import org.opentrafficsim.core.geometry.OTSLine3D;
14  import org.opentrafficsim.core.geometry.OTSPoint3D;
15  import org.opentrafficsim.core.network.LateralDirectionality;
16  import org.opentrafficsim.core.network.NetworkException;
17  
18  /**
19   * <p>
20   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
21   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
22   * <p>
23   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
24   * initial version Aug 19, 2014 <br>
25   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
26   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
27   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
28   */
29  public abstract class CrossSectionElement implements LocatableInterface, Serializable, OTS_SCALAR
30  {
31      /** */
32      private static final long serialVersionUID = 20150826L;
33  
34      /** the id. Should be unique within the parentLink. */
35      private final String id;
36  
37      /** Cross Section Link to which the element belongs. */
38      @SuppressWarnings("checkstyle:visibilitymodifier")
39      protected final CrossSectionLink parentLink;
40  
41      /** The lateral offset from the design line of the parentLink at the start of the parentLink. */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      protected final Length.Rel designLineOffsetAtBegin;
44  
45      /** The lateral offset from the design line of the parentLink at the end of the parentLink. */
46      @SuppressWarnings("checkstyle:visibilitymodifier")
47      protected final Length.Rel designLineOffsetAtEnd;
48  
49      /** Start width, positioned <i>symmetrically around</i> the lateral start position. */
50      @SuppressWarnings("checkstyle:visibilitymodifier")
51      protected final Length.Rel beginWidth;
52  
53      /** End width, positioned <i>symmetrically around</i> the lateral end position. */
54      @SuppressWarnings("checkstyle:visibilitymodifier")
55      protected final Length.Rel endWidth;
56  
57      /** The length of the line. Calculated once at the creation. */
58      @SuppressWarnings("checkstyle:visibilitymodifier")
59      protected final Length.Rel length;
60  
61      /** The center line of the element. Calculated once at the creation. */
62      private final OTSLine3D centerLine;
63  
64      /** The contour of the element. Calculated once at the creation. */
65      private final OTSLine3D contour;
66  
67      /**
68       * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
69       * the StartNode towards the EndNode as the longitudinal direction.
70       * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
71       * @param parentLink CrossSectionLink; Link to which the element belongs.
72       * @param lateralOffsetAtBegin DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
73       *            CrossSectionLink with respect to the design line of the parent Link at the start of the parent Link
74       * @param lateralOffsetAtEnd DoubleScalar.Rel&lt;LengthUnit&gt;; the lateral offset of the design line of the new
75       *            CrossSectionLink with respect to the design line of the parent Link at the end of the parent Link
76       * @param beginWidth DoubleScalar.Rel&lt;LengthUnit&gt;; width at start, positioned <i>symmetrically around</i> the design
77       *            line
78       * @param endWidth DoubleScalar.Rel&lt;LengthUnit&gt;; width at end, positioned <i>symmetrically around</i> the design line
79       * @throws OTSGeometryException when creation of the geometry fails
80       * @throws NetworkException when id equal to null or not unique
81       */
82      public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length.Rel lateralOffsetAtBegin,
83          final Length.Rel lateralOffsetAtEnd, final Length.Rel beginWidth, final Length.Rel endWidth)
84          throws OTSGeometryException, NetworkException
85      {
86          super();
87          if (id == null)
88          {
89              throw new NetworkException("Constructor of CrossSectionElement -- id cannot be null");
90          }
91          for (CrossSectionElement cse : parentLink.getCrossSectionElementList())
92          {
93              if (cse.getId().equals(id))
94              {
95                  throw new NetworkException("Constructor of CrossSectionElement -- id " + id + " not unique within the Link");
96              }
97          }
98          this.id = id;
99          this.parentLink = parentLink;
100         this.designLineOffsetAtBegin = lateralOffsetAtBegin;
101         this.designLineOffsetAtEnd = lateralOffsetAtEnd;
102         this.beginWidth = beginWidth;
103         this.endWidth = endWidth;
104 
105         this.centerLine =
106             OTSBuffering.offsetLine(this.getParentLink().getDesignLine(), this.designLineOffsetAtBegin.getSI(),
107                 this.designLineOffsetAtEnd.getSI());
108         this.length = this.centerLine.getLength();
109         this.contour = constructContour(this);
110 
111         this.parentLink.addCrossSectionElement(this);
112     }
113 
114     /**
115      * @return parentLink.
116      */
117     public final CrossSectionLink getParentLink()
118     {
119         return this.parentLink;
120     }
121 
122     /**
123      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
124      * @param fractionalPosition double; fractional longitudinal position on this Lane
125      * @return DoubleScalar.Rel&lt;LengthUnit&gt; the lateralCenterPosition at the specified longitudinal position
126      */
127     public final Length.Rel getLateralCenterPosition(final double fractionalPosition)
128     {
129         return Length.Rel.interpolate(this.designLineOffsetAtBegin, this.designLineOffsetAtEnd, fractionalPosition);
130     }
131 
132     /**
133      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
134      * @param longitudinalPosition DoubleScalar.Rel&lt;LengthUnit&gt;; the longitudinal position on this Lane
135      * @return DoubleScalar.Rel&lt;LengthUnit&gt; the lateralCenterPosition at the specified longitudinal position
136      */
137     public final Length.Rel getLateralCenterPosition(final Length.Rel longitudinalPosition)
138     {
139         return getLateralCenterPosition(longitudinalPosition.getSI() / getLength().getSI());
140     }
141 
142     /**
143      * Return the width of this CrossSectionElement at a specified longitudinal position.
144      * @param longitudinalPosition DoubleScalar&lt;LengthUnit&gt;; the longitudinal position
145      * @return DoubleScalar.Rel&lt;LengthUnit&gt;; the width of this CrossSectionElement at the specified longitudinal position.
146      */
147     public final Length.Rel getWidth(final Length.Rel longitudinalPosition)
148     {
149         return getWidth(longitudinalPosition.getSI() / getLength().getSI());
150     }
151 
152     /**
153      * Return the width of this CrossSectionElement at a specified fractional longitudinal position.
154      * @param fractionalPosition double; the fractional longitudinal position
155      * @return DoubleScalar.Rel&lt;LengthUnit&gt;; the width of this CrossSectionElement at the specified fractional
156      *         longitudinal position.
157      */
158     public final Length.Rel getWidth(final double fractionalPosition)
159     {
160         return Length.Rel.interpolate(this.beginWidth, this.endWidth, fractionalPosition);
161     }
162 
163     /**
164      * Return the length of this CrossSectionElement as measured along the design line (which equals the center line).
165      * @return DoubleScalar.Rel&lt;LengthUnit&gt;; the length of this CrossSectionElement
166      */
167     public final Length.Rel getLength()
168     {
169         return this.length;
170     }
171 
172     /**
173      * @return designLineOffsetAtBegin.
174      */
175     public final Length.Rel getDesignLineOffsetAtBegin()
176     {
177         return this.designLineOffsetAtBegin;
178     }
179 
180     /**
181      * @return designLineOffsetAtEnd.
182      */
183     public final Length.Rel getDesignLineOffsetAtEnd()
184     {
185         return this.designLineOffsetAtEnd;
186     }
187 
188     /**
189      * @return beginWidth.
190      */
191     public final Length.Rel getBeginWidth()
192     {
193         return this.beginWidth;
194     }
195 
196     /**
197      * @return endWidth.
198      */
199     public final Length.Rel getEndWidth()
200     {
201         return this.endWidth;
202     }
203 
204     /**
205      * @return the z-offset for drawing (what's on top, what's underneath).
206      */
207     protected abstract double getZ();
208 
209     /**
210      * @return centerLine.
211      */
212     public final OTSLine3D getCenterLine()
213     {
214         return this.centerLine;
215     }
216 
217     /**
218      * @return contour.
219      */
220     public final OTSLine3D getContour()
221     {
222         return this.contour;
223     }
224 
225     /**
226      * @return id
227      */
228     public final String getId()
229     {
230         return this.id;
231     }
232 
233     /**
234      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
235      * CrossSectionElement at the specified fractional longitudinal position.
236      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
237      * @param fractionalLongitudinalPosition double; ranges from 0.0 (begin of parentLink) to 1.0 (end of parentLink)
238      * @return DoubleScalar.Rel&lt;LengthUnit&gt;
239      */
240     public final Length.Rel getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
241         final double fractionalLongitudinalPosition)
242     {
243         Length.Rel designLineOffset =
244             Length.Rel.interpolate(this.designLineOffsetAtBegin, this.designLineOffsetAtEnd, fractionalLongitudinalPosition);
245         Length.Rel halfWidth =
246             Length.Rel.interpolate(this.beginWidth, this.endWidth, fractionalLongitudinalPosition).multiplyBy(0.5);
247         switch (lateralDirection)
248         {
249             case LEFT:
250                 return designLineOffset.minus(halfWidth);
251             case RIGHT:
252                 return designLineOffset.plus(halfWidth);
253             default:
254                 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
255         }
256     }
257 
258     /**
259      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
260      * CrossSectionElement at the specified longitudinal position.
261      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
262      * @param longitudinalPosition DoubleScalar.Rel&lt;LengthUnit&gt;; the position along the length of this CrossSectionElement
263      * @return DoubleScalar.Rel&lt;LengthUnit&gt;
264      */
265     public final Length.Rel getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
266         final Length.Rel longitudinalPosition)
267     {
268         return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
269     }
270 
271 
272     /**
273      * Construct a buffer geometry by offsetting the linear geometry line with a distance and constructing a so-called "buffer"
274      * around it.
275      * @param cse the CrossSectionElement to construct the contour for
276      * @return the geometry belonging to this CrossSectionElement.
277      * @throws OTSGeometryException when construction of the geometry fails
278      */
279     public static OTSLine3D constructContour(final CrossSectionElement cse) throws OTSGeometryException
280     {
281         OTSLine3D crossSectionDesignLine =
282             OTSBuffering.offsetLine(cse.getParentLink().getDesignLine(), cse.getDesignLineOffsetAtBegin().getSI(), cse
283                 .getDesignLineOffsetAtEnd().getSI());
284         OTSLine3D rightBoundary =
285                 OTSBuffering.offsetLine(crossSectionDesignLine, -cse.getBeginWidth().getSI() / 2, -cse.getEndWidth().getSI() / 2);
286         OTSLine3D leftBoundary =
287                 OTSBuffering.offsetLine(crossSectionDesignLine, cse.getBeginWidth().getSI() / 2, cse.getEndWidth().getSI() / 2);
288         OTSPoint3D[] result = new OTSPoint3D[rightBoundary.size() + leftBoundary.size() + 1];
289         int resultIndex = 0;
290         for (int index = 0; index < rightBoundary.size(); index++)
291         {
292             result[resultIndex++] = rightBoundary.get(index);
293         }
294         for (int index = leftBoundary.size(); --index >= 0;)
295         {
296             result[resultIndex++] = leftBoundary.get(index);
297         }
298         result[resultIndex] = rightBoundary.get(0); // close the contour
299         return new OTSLine3D(result);
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     @SuppressWarnings("checkstyle:designforextension")
305     public DirectedPoint getLocation() 
306     {
307         DirectedPoint centroid = this.contour.getLocation();
308         return new DirectedPoint(centroid.x, centroid.y, getZ());
309     }
310 
311     /** {@inheritDoc} */
312     @Override
313     @SuppressWarnings("checkstyle:designforextension")
314     public Bounds getBounds() 
315     {
316         return this.contour.getBounds();
317     }
318 
319     /** {@inheritDoc} */
320     @Override
321     @SuppressWarnings("checkstyle:designforextension")
322     public String toString()
323     {
324         return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", this.designLineOffsetAtBegin.getSI(),
325             this.designLineOffsetAtEnd.getSI(), this.beginWidth.getSI(), this.endWidth.getSI());
326     }
327 
328     /** {@inheritDoc} */
329     @SuppressWarnings("checkstyle:designforextension")
330     @Override
331     public int hashCode()
332     {
333         final int prime = 31;
334         int result = 1;
335         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
336         result = prime * result + ((this.parentLink == null) ? 0 : this.parentLink.hashCode());
337         return result;
338     }
339 
340     /** {@inheritDoc} */
341     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
342     @Override
343     public boolean equals(final Object obj)
344     {
345         if (this == obj)
346             return true;
347         if (obj == null)
348             return false;
349         if (getClass() != obj.getClass())
350             return false;
351         CrossSectionElement other = (CrossSectionElement) obj;
352         if (this.id == null)
353         {
354             if (other.id != null)
355                 return false;
356         }
357         else if (!this.id.equals(other.id))
358             return false;
359         if (this.parentLink == null)
360         {
361             if (other.parentLink != null)
362                 return false;
363         }
364         else if (!this.parentLink.equals(other.parentLink))
365             return false;
366         return true;
367     }
368 
369 }