View Javadoc
1   package org.opentrafficsim.road.network.lane;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import org.djutils.event.EventType;
8   import org.djutils.exceptions.Throw;
9   import org.djutils.exceptions.Try;
10  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
11  import org.opentrafficsim.core.geometry.OTSLine3D;
12  import org.opentrafficsim.core.geometry.OTSPoint3D;
13  import org.opentrafficsim.core.network.LinkType;
14  import org.opentrafficsim.core.network.Network;
15  import org.opentrafficsim.core.network.NetworkException;
16  import org.opentrafficsim.core.network.OTSLink;
17  import org.opentrafficsim.road.network.RoadNetwork;
18  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
19  
20  import nl.tudelft.simulation.language.d3.DirectedPoint;
21  
22  /**
23   * A CrossSectionLink is a link with lanes where GTUs can possibly switch between lanes.
24   * <p>
25   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * <p>
28   * $LastChangedDate: 2015-09-16 19:20:07 +0200 (Wed, 16 Sep 2015) $, @version $Revision: 1405 $, by $Author: averbraeck $,
29   * initial version Aug 19, 2014 <br>
30   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
33   */
34  public class CrossSectionLink extends OTSLink implements Serializable
35  {
36      /** */
37      private static final long serialVersionUID = 20141015L;
38  
39      /** List of cross-section elements. */
40      private final List<CrossSectionElement> crossSectionElementList = new ArrayList<>();
41  
42      /** List of lanes. */
43      private final List<Lane> lanes = new ArrayList<>();
44  
45      /** The policy to generally keep left, keep right, or keep lane. */
46      private final LaneKeepingPolicy laneKeepingPolicy;
47  
48      /** Priority. */
49      // TODO per GTUDirectionality / LongitudinalDirectionality?
50      private Priority priority = Priority.NONE;
51  
52      /** Fraction in range 0...1 to divide origin or destination flow over connectors. */
53      private Double demandWeight = null;
54  
55      /** Line over which GTUs enter or leave the link at the start node. */
56      private OTSLine3D startLine;
57  
58      /** Line over which GTUs enter or leave the link at the end node. */
59      private OTSLine3D endLine;
60  
61      /**
62       * The (regular, not timed) event type for pub/sub indicating the addition of a Lane to a CrossSectionLink. <br>
63       * Payload: Object[] { String networkId, String linkId, String LaneId, Lane lane, int laneNumber } <br>
64       * TODO work in a different way with lane numbers to align to standard lane numbering.
65       */
66      public static final EventType LANE_ADD_EVENT = new EventType("LINK.LANE.ADD");
67  
68      /**
69       * The (regular, not timed) event type for pub/sub indicating the removal of a Lane from a CrossSectionLink. <br>
70       * Payload: Object[] { String networkId, String linkId, String LaneId } <br>
71       * TODO allow for the removal of a Lane; currently this is not possible.
72       */
73      public static final EventType LANE_REMOVE_EVENT = new EventType("LINK.LANE.REMOVE");
74  
75      /**
76       * Construction of a cross section link.
77       * @param network RoadNetwork; the network
78       * @param id String; the link id.
79       * @param startNode OTSRoadNode; the start node (directional).
80       * @param endNode OTSRoadNode; the end node (directional).
81       * @param linkType LinkType; the link type
82       * @param designLine OTSLine3D; the design line of the Link
83       * @param simulator OTSSimulatorInterface; the simulator on which events can be scheduled
84       * @param laneKeepingPolicy LaneKeepingPolicy; the policy to generally keep left, keep right, or keep lane
85       * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
86       *             or the end node of the link are not registered in the network.
87       */
88      @SuppressWarnings("checkstyle:parameternumber")
89      public CrossSectionLink(final RoadNetwork network, final String id, final OTSRoadNodene/OTSRoadNode.html#OTSRoadNode">OTSRoadNode startNode, final OTSRoadNode endNode,
90              final LinkType linkType, final OTSLine3D designLine, final OTSSimulatorInterface simulator,
91              final LaneKeepingPolicy laneKeepingPolicy) throws NetworkException
92      {
93          super(network, id, startNode, endNode, linkType, designLine, simulator);
94          this.laneKeepingPolicy = laneKeepingPolicy;
95      }
96  
97      /**
98       * Clone a CrossSectionLink for a new network.
99       * @param newNetwork Network; the new network to which the clone belongs
100      * @param newSimulator OTSSimulatorInterface; the new simulator for this network
101      * @param link CrossSectionLink; the link to clone from
102      * @throws NetworkException if link already exists in the network, if name of the link is not unique, or if the start node
103      *             or the end node of the link are not registered in the network.
104      */
105     protected CrossSectionLink(final RoadNetwork newNetwork, final OTSSimulatorInterface newSimulator,
106             final CrossSectionLink link) throws NetworkException
107     {
108         super(newNetwork, newSimulator, link);
109         this.laneKeepingPolicy = link.laneKeepingPolicy;
110         for (CrossSectionElement cse : link.crossSectionElementList)
111         {
112             cse.clone(this, newSimulator);
113             // the CrossSectionElement will add itself to the Link (OTS-237)
114         }
115     }
116 
117     /** {@inheritDoc} */
118     @Override
119     public RoadNetwork getNetwork()
120     {
121         return (RoadNetwork) super.getNetwork();
122     }
123 
124     /**
125      * Add a cross section element at the end of the list. <br>
126      * <b>Note:</b> LEFT is seen as a positive lateral direction, RIGHT as a negative lateral direction.
127      * @param cse CrossSectionElement; the cross section element to add.
128      */
129     protected final void addCrossSectionElement(final CrossSectionElement cse)
130     {
131         this.crossSectionElementList.add(cse);
132         if (cse instanceof Lane)
133         {
134             this.lanes.add((Lane) cse);
135             fireEvent(LANE_ADD_EVENT,
136                     new Object[] { getNetwork().getId(), getId(), cse.getId(), (Lane) cse, this.lanes.indexOf(cse) });
137         }
138     }
139 
140     /**
141      * Retrieve a safe copy of the cross section element list.
142      * @return List&lt;CrossSectionElement&gt;; the cross section element list.
143      */
144     public final List<CrossSectionElement> getCrossSectionElementList()
145     {
146         return this.crossSectionElementList == null ? new ArrayList<>() : new ArrayList<>(this.crossSectionElementList);
147     }
148 
149     /**
150      * Retrieve the lane keeping policy.
151      * @return LaneKeepingPolicy; the lane keeping policy on this CrossSectionLink
152      */
153     public final LaneKeepingPolicy getLaneKeepingPolicy()
154     {
155         return this.laneKeepingPolicy;
156     }
157 
158     /**
159      * Find a cross section element with a specified id.
160      * @param id String; the id to search for
161      * @return CrossSectionElement; the cross section element with the given id, or null if not found
162      */
163     public final CrossSectionElement getCrossSectionElement(final String id)
164     {
165         for (CrossSectionElement cse : this.crossSectionElementList)
166         {
167             if (cse.getId().equals(id))
168             {
169                 return cse;
170             }
171         }
172         return null;
173     }
174 
175     /**
176      * Return a safe copy of the list of lanes of this CrossSectionLink.
177      * @return List&lt;Lane&gt;; the list of lanes.
178      */
179     public final List<Lane> getLanes()
180     {
181         return this.lanes == null ? new ArrayList<>() : new ArrayList<>(this.lanes);
182     }
183 
184     /**
185      * @return priority.
186      */
187     public final Priority getPriority()
188     {
189         return this.priority;
190     }
191 
192     /**
193      * @param priority Priority; set priority.
194      */
195     public final void setPriority(final Priority priority)
196     {
197         this.priority = priority;
198     }
199 
200     /**
201      * Sets the demand weight. This is only applicable to links of type CONNECTOR.
202      * @param demandWeight double; demand weight, which is any positive value
203      */
204     public final void setDemandWeight(final double demandWeight)
205     {
206         Throw.when(demandWeight < 0.0, IllegalArgumentException.class, "Demand weight should be positive.");
207         Throw.when(!getLinkType().isConnector(), IllegalArgumentException.class,
208                 "Demand weight can only be set on connectors.");
209         this.demandWeight = demandWeight;
210     }
211 
212     /**
213      * Clears the demand weight. This is only applicable to links of type CONNECTOR.
214      */
215     public final void clearDemandWeight()
216     {
217         this.demandWeight = null;
218     }
219 
220     /**
221      * Returns the demand weight. This is only applicable to links of type CONNECTOR.
222      * @return Double; demand weight, any positive value, or {@code null}
223      */
224     public final Double getDemandWeight()
225     {
226         return this.demandWeight;
227     }
228 
229     /**
230      * Returns the line over which GTUs enter and leave the link at the start node.
231      * @return OTSLine3D; line over which GTUs enter and leave the link at the start node
232      */
233     public OTSLine3D getStartLine()
234     {
235         if (this.startLine == null)
236         {
237             double left = Double.NaN;
238             double right = Double.NaN;
239             for (Lane lane : this.lanes)
240             {
241                 double half = lane.getBeginWidth().si * .5;
242                 if (!Double.isNaN(left))
243                 {
244                     left = Math.max(left, lane.getDesignLineOffsetAtBegin().si + half);
245                     right = Math.min(right, lane.getDesignLineOffsetAtBegin().si - half);
246                 }
247                 else
248                 {
249                     left = lane.getDesignLineOffsetAtBegin().si + half;
250                     right = lane.getDesignLineOffsetAtBegin().si - half;
251                 }
252             }
253             OTSPoint3D start = getStartNode().getPoint();
254             double heading = getStartNode().getHeading() + .5 * Math.PI;
255             double cosHeading = Math.cos(heading);
256             double sinHeading = Math.sin(heading);
257             OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
258             OTSPoint3D rightPoint = new OTSPoint3D(start.x - cosHeading * right, start.y - sinHeading * right);
259             this.startLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid startline on CrossSectionLink.");
260         }
261         return this.startLine;
262     }
263 
264     /**
265      * Returns the line over which GTUs enter and leave the link at the end node.
266      * @return OTSLine3D; line over which GTUs enter and leave the link at the end node
267      */
268     public OTSLine3D getEndLine()
269     {
270         if (this.endLine == null)
271         {
272             double left = Double.NaN;
273             double right = Double.NaN;
274             for (Lane lane : this.lanes)
275             {
276                 double half = lane.getEndWidth().si * .5;
277                 if (!Double.isNaN(left))
278                 {
279                     left = Math.max(left, lane.getDesignLineOffsetAtEnd().si + half);
280                     right = Math.min(right, lane.getDesignLineOffsetAtEnd().si - half);
281                 }
282                 else
283                 {
284                     left = lane.getDesignLineOffsetAtEnd().si + half;
285                     right = lane.getDesignLineOffsetAtEnd().si - half;
286                 }
287             }
288             OTSPoint3D start = getEndNode().getPoint();
289             DirectedPoint p = Try.assign(() -> getEndNode().getLocation(), "Unexpected remote exception.");
290             double heading = p.getRotZ() + .5 * Math.PI;
291             double cosHeading = Math.cos(heading);
292             double sinHeading = Math.sin(heading);
293             OTSPoint3D leftPoint = new OTSPoint3D(start.x + cosHeading * left, start.y + sinHeading * left);
294             OTSPoint3D rightPoint = new OTSPoint3D(start.x + cosHeading * right, start.y + sinHeading * right);
295             this.endLine = Try.assign(() -> new OTSLine3D(leftPoint, rightPoint), "Invalid endline on CrossSectionLink.");
296         }
297         return this.endLine;
298     }
299 
300     /** {@inheritDoc} */
301     @Override
302     public final String toString()
303     {
304         return "CrossSectionLink [name=" + this.getId() + ", nodes=" + getStartNode().getId() + "-" + getEndNode().getId()
305                 + ", crossSectionElementList=" + this.crossSectionElementList + ", lanes=" + this.lanes + ", laneKeepingPolicy="
306                 + this.laneKeepingPolicy + "]";
307     }
308 
309     /** {@inheritDoc} */
310     @Override
311     @SuppressWarnings("checkstyle:designforextension")
312     public CrossSectionLink clone(final Network newNetwork, final OTSSimulatorInterface newSimulator) throws NetworkException
313     {
314         Throw.when(!(newNetwork instanceof RoadNetwork), NetworkException.class,
315                 "CrossSectionLink.clone. newNetwork not of the type Roadnetwork");
316         return new CrossSectionLink((RoadNetwork) newNetwork, newSimulator, this);
317     }
318 
319     /**
320      * Priority of a link.
321      * <p>
322      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
323      * <br>
324      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
325      * <p>
326      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2016 <br>
327      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
328      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
329      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
330      */
331     public enum Priority
332     {
333         /** Traffic has priority. */
334         PRIORITY,
335 
336         /** No priority. */
337         NONE,
338 
339         /** Turn on red. */
340         TURN_ON_RED,
341 
342         /** Yield. */
343         YIELD,
344 
345         /** Need to stop. */
346         STOP,
347 
348         /** Priority according to all-stop rules. */
349         ALL_STOP,
350 
351         /** Priority at bus stop, i.e. bus has right of way if it wants to leave the bus stop. */
352         BUS_STOP;
353 
354         /**
355          * Returns whether this is priority.
356          * @return whether this is priority
357          */
358         public boolean isPriority()
359         {
360             return this.equals(PRIORITY);
361         }
362 
363         /**
364          * Returns whether this is none.
365          * @return whether this is none
366          */
367         public boolean isNone()
368         {
369             return this.equals(NONE);
370         }
371 
372         /**
373          * Returns whether this is turn on red.
374          * @return whether this is turn on red
375          */
376         public boolean isTurnOnRed()
377         {
378             return this.equals(TURN_ON_RED);
379         }
380 
381         /**
382          * Returns whether this is yield.
383          * @return whether this is yield
384          */
385         public boolean isYield()
386         {
387             return this.equals(YIELD);
388         }
389 
390         /**
391          * Returns whether this is stop.
392          * @return whether this is stop
393          */
394         public boolean isStop()
395         {
396             return this.equals(STOP);
397         }
398 
399         /**
400          * Returns whether this is all-stop.
401          * @return whether this is all-stop
402          */
403         public boolean isAllStop()
404         {
405             return this.equals(ALL_STOP);
406         }
407 
408         /**
409          * Returns whether this is bus stop.
410          * @return whether this is bus stop
411          */
412         public boolean isBusStop()
413         {
414             return this.equals(BUS_STOP);
415         }
416 
417     }
418 
419 }