View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.List;
7   
8   import javax.media.j3d.Bounds;
9   
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.opentrafficsim.base.Identifiable;
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.geometry.OTSShape;
16  import org.opentrafficsim.core.network.LateralDirectionality;
17  import org.opentrafficsim.core.network.NetworkException;
18  
19  import nl.tudelft.simulation.dsol.animation.Locatable;
20  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
21  import nl.tudelft.simulation.event.EventProducer;
22  import nl.tudelft.simulation.language.Throw;
23  import nl.tudelft.simulation.language.d3.DirectedPoint;
24  
25  /**
26   * Cross section elements are used to compose a CrossSectionLink.
27   * <p>
28   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
29   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
30   * <p>
31   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
32   * initial version Aug 19, 2014 <br>
33   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
34   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
35   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
36   */
37  public abstract class CrossSectionElement extends EventProducer implements Locatable, Serializable, Identifiable
38  {
39      /** */
40      private static final long serialVersionUID = 20150826L;
41  
42      /** The id. Should be unique within the parentLink. */
43      private final String id;
44  
45      /** Cross Section Link to which the element belongs. */
46      @SuppressWarnings("checkstyle:visibilitymodifier")
47      protected final CrossSectionLink parentLink;
48  
49      /** The offsets and widths at positions along the line, relative to the design line of the parent link. */
50      @SuppressWarnings("checkstyle:visibilitymodifier")
51      protected final List<CrossSectionSlice> crossSectionSlices;
52  
53      /** The length of the line. Calculated once at the creation. */
54      @SuppressWarnings("checkstyle:visibilitymodifier")
55      protected final Length length;
56  
57      /** The center line of the element. Calculated once at the creation. */
58      private final OTSLine3D centerLine;
59  
60      /** The contour of the element. Calculated once at the creation. */
61      private final OTSShape contour;
62  
63      /**
64       * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
65       * the StartNode towards the EndNode as the longitudinal direction.
66       * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
67       * @param parentLink CrossSectionLink; Link to which the element belongs.
68       * @param crossSectionSlices List&lt;CrossSectionSlice&gt;; the offsets and widths at positions along the line, relative to
69       *            the design line of the parent link. If there is just one with and offset, there should just be one element in
70       *            the list with Length = 0. If there are more slices, the last one should be at the length of the design line.
71       *            If not, a NetworkException is thrown.
72       * @throws OTSGeometryException when creation of the geometry fails
73       * @throws NetworkException when id equal to null or not unique, or there are multiple slices and the last slice does not
74       *             end at the length of the design line.
75       */
76      public CrossSectionElement(final CrossSectionLink parentLink, final String id,
77              final List<CrossSectionSlice> crossSectionSlices) throws OTSGeometryException, NetworkException
78      {
79          Throw.when(parentLink == null, NetworkException.class,
80                  "Constructor of CrossSectionElement for id %s, parentLink cannot be null", id);
81          Throw.when(id == null, NetworkException.class, "Constructor of CrossSectionElement -- id cannot be null");
82          for (CrossSectionElement cse : parentLink.getCrossSectionElementList())
83          {
84              Throw.when(cse.getId().equals(id), NetworkException.class,
85                      "Constructor of CrossSectionElement -- id %s not unique within the Link", id);
86          }
87          this.id = id;
88          this.parentLink = parentLink;
89  
90          this.crossSectionSlices = new ArrayList<>(crossSectionSlices); // copy of list with immutable slices
91          Throw.when(this.crossSectionSlices.size() == 0, NetworkException.class,
92                  "CrossSectionElement %s is created with zero slices for %s", id, parentLink);
93          Throw.when(this.crossSectionSlices.get(0).getRelativeLength().si != 0.0, NetworkException.class,
94                  "CrossSectionElement %s for %s has a first slice with relativeLength is not equal to 0.0", id, parentLink);
95          Throw.when(
96                  this.crossSectionSlices.size() > 1
97                          && this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getRelativeLength()
98                                  .ne(this.parentLink.getLength()), NetworkException.class,
99                  "CrossSectionElement %s for %s has a last slice with relativeLength is not equal "
100                         + "to the length of the parent link", id, parentLink);
101 
102         if (this.crossSectionSlices.size() <= 2)
103         {
104             this.centerLine =
105                     this.getParentLink().getDesignLine()
106                             .offsetLine(getDesignLineOffsetAtBegin().getSI(), getDesignLineOffsetAtEnd().getSI());
107         }
108         else
109         {
110             double[] relativeFractions = new double[this.crossSectionSlices.size()];
111             double[] offsets = new double[this.crossSectionSlices.size()];
112             for (int i = 0; i < this.crossSectionSlices.size(); i++)
113             {
114                 relativeFractions[i] =
115                         this.crossSectionSlices.get(i).getRelativeLength().si / this.parentLink.getLength().si;
116                 offsets[i] = this.crossSectionSlices.get(i).getDesignLineOffset().si;
117             }
118             this.centerLine = this.getParentLink().getDesignLine().offsetLine(relativeFractions, offsets);
119         }
120 
121         this.length = this.centerLine.getLength();
122         this.contour = constructContour(this);
123 
124         this.parentLink.addCrossSectionElement(this);
125     }
126 
127     /**
128      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
129      * the StartNode towards the EndNode as the longitudinal direction.
130      * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
131      * @param parentLink CrossSectionLink; Link to which the element belongs.
132      * @param lateralOffsetAtBegin Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
133      *            design line of the parent Link at the start of the parent Link
134      * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
135      *            design line of the parent Link at the end of the parent Link
136      * @param beginWidth Length; width at start, positioned <i>symmetrically around</i> the design line
137      * @param endWidth Length; width at end, positioned <i>symmetrically around</i> the design line
138      * @throws OTSGeometryException when creation of the geometry fails
139      * @throws NetworkException when id equal to null or not unique
140      */
141     public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffsetAtBegin,
142             final Length lateralOffsetAtEnd, final Length beginWidth, final Length endWidth) throws OTSGeometryException,
143             NetworkException
144     {
145         this(parentLink, id, fixGradualLateraloffset(parentLink, lateralOffsetAtBegin, lateralOffsetAtEnd, beginWidth,
146                 endWidth));
147     }
148 
149     /**
150      * Construct a list of cross section slices. using sinusoidal interpolation for changin lateral offset.
151      * @param parentLink CrossSectionLink; Link to which the element belongs.
152      * @param lateralOffsetAtBegin Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
153      *            design line of the parent Link at the start of the parent Link
154      * @param lateralOffsetAtEnd Length; the lateral offset of the design line of the new CrossSectionLink with respect to the
155      *            design line of the parent Link at the end of the parent Link
156      * @param beginWidth Length; width at start, positioned <i>symmetrically around</i> the design line
157      * @param endWidth Length; width at end, positioned <i>symmetrically around</i> the design line
158      * @return List&ltCrossSectionSlice&gt;; the cross section slices
159      */
160     private static List<CrossSectionSlice> fixGradualLateraloffset(final CrossSectionLink parentLink,
161             final Length lateralOffsetAtBegin, final Length lateralOffsetAtEnd, final Length beginWidth,
162             final Length endWidth)
163     {
164         if ("1021_J23".equals(parentLink.getId()))
165         {
166             System.out.println("Adding cross section to link " + parentLink.getId());
167         }
168         List<CrossSectionSlice> result = new ArrayList<>();
169         int numPoints = lateralOffsetAtBegin.equals(lateralOffsetAtEnd) ? 2 : 8;
170         Length parentLength = parentLink.getLength();
171         for (int index = 0; index < numPoints; index++)
172         {
173             double fraction = index * 1.0 / (numPoints - 1);
174             Length lengthAtCrossSection = parentLength.multiplyBy(fraction);
175             double relativeOffsetAtFraction = (1 + Math.sin((fraction - 0.5) * Math.PI)) / 2;
176             Length offsetAtFraction = Length.interpolate(lateralOffsetAtBegin, lateralOffsetAtEnd, relativeOffsetAtFraction);
177             result.add(new CrossSectionSlice(lengthAtCrossSection, offsetAtFraction, Length.interpolate(beginWidth,
178                     endWidth, fraction)));
179             System.out.println("fraction " + fraction + ", " + result.get(result.size() - 1));
180         }
181 
182         // Arrays.asList(new CrossSectionSlice[] { new CrossSectionSlice(Length.ZERO, lateralOffsetAtBegin, beginWidth),
183         // new CrossSectionSlice(parentLink.getLength(), lateralOffsetAtEnd, endWidth) })
184         return result;
185     }
186 
187     /**
188      * Clone a CrossSectionElement for a new network.
189      * @param newCrossSectionLink the new link to which the clone belongs
190      * @param newSimulator the new simulator for this network
191      * @param animation whether to (re)create animation or not
192      * @param cse the element to clone from
193      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
194      *             or the end node of the link are not registered in the network.
195      */
196     protected CrossSectionElement(final CrossSectionLink newCrossSectionLink, final SimulatorInterface.TimeDoubleUnit newSimulator,
197             final boolean animation, final CrossSectionElement cse) throws NetworkException
198     {
199         this.id = cse.id;
200         this.parentLink = newCrossSectionLink;
201         this.centerLine = cse.centerLine;
202         this.length = this.centerLine.getLength();
203         this.contour = cse.contour;
204         this.crossSectionSlices = new ArrayList<>(cse.crossSectionSlices);
205         newCrossSectionLink.addCrossSectionElement(this);
206     }
207 
208     /**
209      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction, with the direction from
210      * the StartNode towards the EndNode as the longitudinal direction.
211      * @param id String; The id of the CrosssSectionElement. Should be unique within the parentLink.
212      * @param parentLink CrossSectionLink; Link to which the element belongs.
213      * @param lateralOffset Length; the lateral offset of the design line of the new CrossSectionLink with respect to the design
214      *            line of the parent Link
215      * @param width Length; width, positioned <i>symmetrically around</i> the design line
216      * @throws OTSGeometryException when creation of the geometry fails
217      * @throws NetworkException when id equal to null or not unique
218      */
219     public CrossSectionElement(final CrossSectionLink parentLink, final String id, final Length lateralOffset,
220             final Length width) throws OTSGeometryException, NetworkException
221     {
222         this(parentLink, id, Arrays
223                 .asList(new CrossSectionSlice[] { new CrossSectionSlice(Length.ZERO, lateralOffset, width) }));
224     }
225 
226     /**
227      * @return parentLink.
228      */
229     public final CrossSectionLink getParentLink()
230     {
231         return this.parentLink;
232     }
233 
234     /**
235      * Calculate the slice the fractional position is in.
236      * @param fractionalPosition the fractional position between 0 and 1 compared to the design line
237      * @return int; the lower slice number between 0 and number of slices - 1.
238      */
239     private int calculateSliceNumber(final double fractionalPosition)
240     {
241         double linkLength = this.parentLink.getLength().si;
242         for (int i = 0; i < this.crossSectionSlices.size() - 1; i++)
243         {
244             if (fractionalPosition >= this.crossSectionSlices.get(i).getRelativeLength().si / linkLength
245                     && fractionalPosition <= this.crossSectionSlices.get(i + 1).getRelativeLength().si / linkLength)
246             {
247                 return i;
248             }
249         }
250         return this.crossSectionSlices.size() - 2;
251     }
252 
253     /**
254      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
255      * @param fractionalPosition double; fractional longitudinal position on this Lane
256      * @return Length; the lateralCenterPosition at the specified longitudinal position
257      */
258     public final Length getLateralCenterPosition(final double fractionalPosition)
259     {
260         if (this.crossSectionSlices.size() == 1)
261         {
262             return this.getDesignLineOffsetAtBegin();
263         }
264         if (this.crossSectionSlices.size() == 2)
265         {
266             return Length
267                     .interpolate(this.getDesignLineOffsetAtBegin(), this.getDesignLineOffsetAtEnd(), fractionalPosition);
268         }
269         int sliceNr = calculateSliceNumber(fractionalPosition);
270         return Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(),
271                 this.crossSectionSlices.get(sliceNr + 1).getDesignLineOffset(), fractionalPosition
272                         - this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si);
273     }
274 
275     /**
276      * Retrieve the lateral offset from the Link design line at the specified longitudinal position.
277      * @param longitudinalPosition Length; the longitudinal position on this Lane
278      * @return Length; the lateralCenterPosition at the specified longitudinal position
279      */
280     public final Length getLateralCenterPosition(final Length longitudinalPosition)
281     {
282         return getLateralCenterPosition(longitudinalPosition.getSI() / getLength().getSI());
283     }
284 
285     /**
286      * Return the width of this CrossSectionElement at a specified longitudinal position.
287      * @param longitudinalPosition DoubleScalar&lt;LengthUnit&gt;; the longitudinal position
288      * @return Length; the width of this CrossSectionElement at the specified longitudinal position.
289      */
290     public final Length getWidth(final Length longitudinalPosition)
291     {
292         return getWidth(longitudinalPosition.getSI() / getLength().getSI());
293     }
294 
295     /**
296      * Return the width of this CrossSectionElement at a specified fractional longitudinal position.
297      * @param fractionalPosition double; the fractional longitudinal position
298      * @return Length; the width of this CrossSectionElement at the specified fractional longitudinal position.
299      */
300     public final Length getWidth(final double fractionalPosition)
301     {
302         if (this.crossSectionSlices.size() == 1)
303         {
304             return this.getBeginWidth();
305         }
306         if (this.crossSectionSlices.size() == 2)
307         {
308             return Length.interpolate(this.getBeginWidth(), this.getEndWidth(), fractionalPosition);
309         }
310         int sliceNr = calculateSliceNumber(fractionalPosition);
311         return Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(), this.crossSectionSlices.get(sliceNr + 1)
312                 .getWidth(), fractionalPosition - this.crossSectionSlices.get(sliceNr).getRelativeLength().si
313                 / this.parentLink.getLength().si);
314     }
315 
316     /**
317      * Return the length of this CrossSectionElement as measured along the design line (which equals the center line).
318      * @return Length; the length of this CrossSectionElement
319      */
320     public final Length getLength()
321     {
322         return this.length;
323     }
324 
325     /**
326      * Retrieve the offset from the design line at the begin of the parent link.
327      * @return Length; the offset of this CrossSectionElement at the begin of the parent link
328      */
329     public final Length getDesignLineOffsetAtBegin()
330     {
331         return this.crossSectionSlices.get(0).getDesignLineOffset();
332     }
333 
334     /**
335      * Retrieve the offset from the design line at the end of the parent link.
336      * @return Length; the offset of this CrossSectionElement at the end of the parent link
337      */
338     public final Length getDesignLineOffsetAtEnd()
339     {
340         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getDesignLineOffset();
341     }
342 
343     /**
344      * Retrieve the width at the begin of the parent link.
345      * @return Length; the width of this CrossSectionElement at the begin of the parent link
346      */
347     public final Length getBeginWidth()
348     {
349         return this.crossSectionSlices.get(0).getWidth();
350     }
351 
352     /**
353      * Retrieve the width at the end of the parent link.
354      * @return Length; the width of this CrossSectionElement at the end of the parent link
355      */
356     public final Length getEndWidth()
357     {
358         return this.crossSectionSlices.get(this.crossSectionSlices.size() - 1).getWidth();
359     }
360 
361     /**
362      * Retrieve the Z offset (used to determine what covers what when drawing).
363      * @return double; the Z-offset for drawing (what's on top, what's underneath).
364      */
365     protected abstract double getZ();
366 
367     /**
368      * Retrieve the center line of this CrossSectionElement.
369      * @return OTSLine3D; the center line of this CrossSectionElement
370      */
371     public final OTSLine3D getCenterLine()
372     {
373         return this.centerLine;
374     }
375 
376     /**
377      * Retrieve the contour of this CrossSectionElement.
378      * @return OTSShape; the contour of this CrossSectionElement
379      */
380     public final OTSShape getContour()
381     {
382         return this.contour;
383     }
384 
385     /**
386      * Retrieve the id of this CrossSectionElement.
387      * @return String; the id of this CrossSectionElement
388      */
389     @Override
390     public final String getId()
391     {
392         return this.id;
393     }
394 
395     /**
396      * Retrieve the id of this CrossSectionElement.
397      * @return String; the id of this CrossSectionElement
398      */
399     public final String getFullId()
400     {
401         return getParentLink().getId() + "." + this.id;
402     }
403 
404     /**
405      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
406      * CrossSectionElement at the specified fractional longitudinal position.
407      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
408      * @param fractionalLongitudinalPosition double; ranges from 0.0 (begin of parentLink) to 1.0 (end of parentLink)
409      * @return Length
410      */
411     public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
412             final double fractionalLongitudinalPosition)
413     {
414         Length designLineOffset;
415         Length halfWidth;
416         if (this.crossSectionSlices.size() <= 2)
417         {
418             designLineOffset =
419                     Length.interpolate(getDesignLineOffsetAtBegin(), getDesignLineOffsetAtEnd(),
420                             fractionalLongitudinalPosition);
421             halfWidth = Length.interpolate(getBeginWidth(), getEndWidth(), fractionalLongitudinalPosition).multiplyBy(0.5);
422         }
423         else
424         {
425             int sliceNr = calculateSliceNumber(fractionalLongitudinalPosition);
426             double startFractionalPosition =
427                     this.crossSectionSlices.get(sliceNr).getRelativeLength().si / this.parentLink.getLength().si;
428             designLineOffset =
429                     Length.interpolate(this.crossSectionSlices.get(sliceNr).getDesignLineOffset(), this.crossSectionSlices
430                             .get(sliceNr + 1).getDesignLineOffset(), fractionalLongitudinalPosition
431                             - startFractionalPosition);
432             halfWidth =
433                     Length.interpolate(this.crossSectionSlices.get(sliceNr).getWidth(),
434                             this.crossSectionSlices.get(sliceNr + 1).getWidth(),
435                             fractionalLongitudinalPosition - startFractionalPosition).multiplyBy(0.5);
436         }
437 
438         switch (lateralDirection)
439         {
440             case LEFT:
441                 return designLineOffset.minus(halfWidth);
442             case RIGHT:
443                 return designLineOffset.plus(halfWidth);
444             default:
445                 throw new Error("Bad switch on LateralDirectionality " + lateralDirection);
446         }
447     }
448 
449     /**
450      * Return the lateral offset from the design line of the parent Link of the Left or Right boundary of this
451      * CrossSectionElement at the specified longitudinal position.
452      * @param lateralDirection LateralDirectionality; LEFT, or RIGHT
453      * @param longitudinalPosition Length; the position along the length of this CrossSectionElement
454      * @return Length
455      */
456     public final Length getLateralBoundaryPosition(final LateralDirectionality lateralDirection,
457             final Length longitudinalPosition)
458     {
459         return getLateralBoundaryPosition(lateralDirection, longitudinalPosition.getSI() / getLength().getSI());
460     }
461 
462     /**
463      * Construct a buffer geometry by offsetting the linear geometry line with a distance and constructing a so-called "buffer"
464      * around it.
465      * @param cse CrossSectionElement; the cross section element to construct the contour for
466      * @return OTSShape; the geometry belonging to this CrossSectionElement.
467      * @throws OTSGeometryException when construction of the geometry fails
468      * @throws NetworkException when the resulting contour is degenerate (cannot happen; we hope)
469      */
470     public static OTSShape constructContour(final CrossSectionElement cse) throws OTSGeometryException, NetworkException
471     {
472         OTSPoint3D[] result = null;
473 
474         if (cse.crossSectionSlices.size() <= 2)
475         {
476             OTSLine3D crossSectionDesignLine =
477                     cse.getParentLink().getDesignLine()
478                             .offsetLine(cse.getDesignLineOffsetAtBegin().getSI(), cse.getDesignLineOffsetAtEnd().getSI());
479             OTSLine3D rightBoundary =
480                     crossSectionDesignLine.offsetLine(-cse.getBeginWidth().getSI() / 2, -cse.getEndWidth().getSI() / 2);
481             OTSLine3D leftBoundary =
482                     crossSectionDesignLine.offsetLine(cse.getBeginWidth().getSI() / 2, cse.getEndWidth().getSI() / 2);
483             result = new OTSPoint3D[rightBoundary.size() + leftBoundary.size() + 1];
484             int resultIndex = 0;
485             for (int index = 0; index < rightBoundary.size(); index++)
486             {
487                 result[resultIndex++] = rightBoundary.get(index);
488             }
489             for (int index = leftBoundary.size(); --index >= 0;)
490             {
491                 result[resultIndex++] = leftBoundary.get(index);
492             }
493             result[resultIndex] = rightBoundary.get(0); // close the contour
494         }
495         else
496         {
497             List<OTSPoint3D> resultList = new ArrayList<>();
498             List<OTSPoint3D> rightBoundary = new ArrayList<>();
499             for (int i = 0; i < cse.crossSectionSlices.size() - 1; i++)
500             {
501                 double plLength = cse.getParentLink().getLength().si;
502                 double so = cse.crossSectionSlices.get(i).getDesignLineOffset().si;
503                 double eo = cse.crossSectionSlices.get(i + 1).getDesignLineOffset().si;
504                 double sw2 = cse.crossSectionSlices.get(i).getWidth().si / 2.0;
505                 double ew2 = cse.crossSectionSlices.get(i + 1).getWidth().si / 2.0;
506                 double sf = cse.crossSectionSlices.get(i).getRelativeLength().si / plLength;
507                 double ef = cse.crossSectionSlices.get(i + 1).getRelativeLength().si / plLength;
508                 OTSLine3D crossSectionDesignLine =
509                         cse.getParentLink().getDesignLine().extractFractional(sf, ef).offsetLine(so, eo);
510                 resultList.addAll(Arrays.asList(crossSectionDesignLine.offsetLine(-sw2, -ew2).getPoints()));
511                 rightBoundary.addAll(Arrays.asList(crossSectionDesignLine.offsetLine(sw2, ew2).getPoints()));
512             }
513             for (int index = rightBoundary.size(); --index >= 0;)
514             {
515                 resultList.add(rightBoundary.get(index));
516             }
517             // close the contour (might not be needed)
518             resultList.add(resultList.get(0));
519             result = resultList.toArray(new OTSPoint3D[] {});
520         }
521         return OTSShape.createAndCleanOTSShape(result);
522     }
523 
524     /** {@inheritDoc} */
525     @Override
526     @SuppressWarnings("checkstyle:designforextension")
527     public DirectedPoint getLocation()
528     {
529         DirectedPoint centroid = this.contour.getLocation();
530         return new DirectedPoint(centroid.x, centroid.y, getZ());
531     }
532 
533     /** {@inheritDoc} */
534     @Override
535     @SuppressWarnings("checkstyle:designforextension")
536     public Bounds getBounds()
537     {
538         return this.contour.getBounds();
539     }
540 
541     /** {@inheritDoc} */
542     @Override
543     @SuppressWarnings("checkstyle:designforextension")
544     public String toString()
545     {
546         return String.format("CSE offset %.2fm..%.2fm, width %.2fm..%.2fm", getDesignLineOffsetAtBegin().getSI(),
547                 getDesignLineOffsetAtEnd().getSI(), getBeginWidth().getSI(), getEndWidth().getSI());
548     }
549 
550     /** {@inheritDoc} */
551     @SuppressWarnings("checkstyle:designforextension")
552     @Override
553     public int hashCode()
554     {
555         final int prime = 31;
556         int result = 1;
557         result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
558         result = prime * result + ((this.parentLink == null) ? 0 : this.parentLink.hashCode());
559         return result;
560     }
561 
562     /** {@inheritDoc} */
563     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:needbraces" })
564     @Override
565     public boolean equals(final Object obj)
566     {
567         if (this == obj)
568             return true;
569         if (obj == null)
570             return false;
571         if (getClass() != obj.getClass())
572             return false;
573         CrossSectionElement other = (CrossSectionElement) obj;
574         if (this.id == null)
575         {
576             if (other.id != null)
577                 return false;
578         }
579         else if (!this.id.equals(other.id))
580             return false;
581         if (this.parentLink == null)
582         {
583             if (other.parentLink != null)
584                 return false;
585         }
586         else if (!this.parentLink.equals(other.parentLink))
587             return false;
588         return true;
589     }
590 
591     /**
592      * Clone the CrossSectionElement for e.g., copying a network.
593      * @param newParentLink the new link to which the clone belongs
594      * @param newSimulator the new simulator for this network
595      * @param animation whether to (re)create animation or not
596      * @return a clone of this object
597      * @throws NetworkException in case the cloning fails
598      */
599     @SuppressWarnings("checkstyle:designforextension")
600     public abstract CrossSectionElement clone(final CrossSectionLink newParentLink,
601             final SimulatorInterface.TimeDoubleUnit newSimulator, final boolean animation) throws NetworkException;
602 }