View Javadoc
1   package org.opentrafficsim.road.network.factory.xml;
2   
3   import java.awt.Color;
4   import java.lang.reflect.Constructor;
5   import java.lang.reflect.InvocationTargetException;
6   import java.rmi.RemoteException;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.HashSet;
10  import java.util.LinkedHashMap;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  import java.util.UUID;
15  
16  import javax.naming.NamingException;
17  
18  import org.djunits.unit.AngleUnit;
19  import org.djunits.value.AngleUtil;
20  import org.djunits.value.vdouble.scalar.Direction;
21  import org.djunits.value.vdouble.scalar.Length;
22  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
23  import org.opentrafficsim.core.geometry.Bezier;
24  import org.opentrafficsim.core.geometry.OTSGeometryException;
25  import org.opentrafficsim.core.geometry.OTSLine3D;
26  import org.opentrafficsim.core.geometry.OTSPoint3D;
27  import org.opentrafficsim.core.gtu.GTUException;
28  import org.opentrafficsim.core.gtu.GTUType;
29  import org.opentrafficsim.core.gtu.RelativePosition;
30  import org.opentrafficsim.core.network.LinkType;
31  import org.opentrafficsim.core.network.LongitudinalDirectionality;
32  import org.opentrafficsim.core.network.NetworkException;
33  import org.opentrafficsim.core.network.animation.LinkAnimation;
34  import org.opentrafficsim.road.network.animation.LaneAnimation;
35  import org.opentrafficsim.road.network.animation.ShoulderAnimation;
36  import org.opentrafficsim.road.network.animation.StripeAnimation;
37  import org.opentrafficsim.road.network.factory.xml.ArcTag.ArcDirection;
38  import org.opentrafficsim.road.network.lane.CrossSectionElement;
39  import org.opentrafficsim.road.network.lane.CrossSectionLink;
40  import org.opentrafficsim.road.network.lane.Lane;
41  import org.opentrafficsim.road.network.lane.LaneType;
42  import org.opentrafficsim.road.network.lane.NoTrafficLane;
43  import org.opentrafficsim.road.network.lane.Shoulder;
44  import org.opentrafficsim.road.network.lane.Stripe;
45  import org.opentrafficsim.road.network.lane.Stripe.Permeable;
46  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
47  import org.opentrafficsim.road.network.lane.object.LaneBlock;
48  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
49  import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
50  import org.xml.sax.SAXException;
51  
52  import nl.tudelft.simulation.dsol.SimRuntimeException;
53  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
54  import nl.tudelft.simulation.language.d2.Angle;
55  import nl.tudelft.simulation.language.d3.CartesianPoint;
56  import nl.tudelft.simulation.language.d3.DirectedPoint;
57  import nl.tudelft.simulation.language.reflection.ClassUtil;
58  
59  /**
60   * <p>
61   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
62   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
63   * <p>
64   * LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
65   * initial version Jul 25, 2015 <br>
66   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
67   */
68  final class Links
69  {
70      /** Utility class. */
71      private Links()
72      {
73          // do not instantiate
74      }
75  
76      /**
77       * Find the nodes one by one that have one coordinate defined, and one not defined, and try to build the network from there.
78       * @param parser the parser with the lists of information
79       * @throws NetworkException when both nodes are null.
80       * @throws NamingException when node animation cannot link to the animation context.
81       */
82      @SuppressWarnings("methodlength")
83      static void calculateNodeCoordinates(final XmlNetworkLaneParser parser) throws NetworkException, NamingException
84      {
85          // are there straight tags with nodes without an angle?
86          for (LinkTag linkTag : parser.linkTags.values())
87          {
88              if (linkTag.straightTag != null && linkTag.nodeStartTag.coordinate != null && linkTag.nodeEndTag.coordinate != null)
89              {
90                  if (linkTag.nodeStartTag.angle == null)
91                  {
92                      double dx = linkTag.nodeEndTag.coordinate.x - linkTag.nodeStartTag.coordinate.x;
93                      double dy = linkTag.nodeEndTag.coordinate.y - linkTag.nodeStartTag.coordinate.y;
94                      linkTag.nodeStartTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
95                  }
96                  if (linkTag.nodeEndTag.angle == null)
97                  {
98                      double dx = linkTag.nodeEndTag.coordinate.x - linkTag.nodeStartTag.coordinate.x;
99                      double dy = linkTag.nodeEndTag.coordinate.y - linkTag.nodeStartTag.coordinate.y;
100                     linkTag.nodeEndTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
101                 }
102             }
103         }
104 
105         // are there polyline tags with nodes without an angle?
106         for (LinkTag linkTag : parser.linkTags.values())
107         {
108             if (linkTag.polyLineTag != null && linkTag.nodeStartTag.coordinate != null && linkTag.nodeEndTag.coordinate != null)
109             {
110                 if (linkTag.nodeStartTag.angle == null)
111                 {
112                     double dx = linkTag.polyLineTag.coordinates[0].x - linkTag.nodeStartTag.coordinate.x;
113                     double dy = linkTag.polyLineTag.coordinates[0].y - linkTag.nodeStartTag.coordinate.y;
114                     linkTag.nodeStartTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
115                 }
116                 if (linkTag.nodeEndTag.angle == null)
117                 {
118                     double dx = linkTag.nodeEndTag.coordinate.x
119                             - linkTag.polyLineTag.coordinates[linkTag.polyLineTag.coordinates.length - 1].x;
120                     double dy = linkTag.nodeEndTag.coordinate.y
121                             - linkTag.polyLineTag.coordinates[linkTag.polyLineTag.coordinates.length - 1].y;
122                     linkTag.nodeEndTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
123                 }
124             }
125         }
126 
127         // see if we can find the coordinates of the nodes that have not yet been fixed.
128         Set<NodeTag> nodeTags = new HashSet<>();
129         for (LinkTag linkTag : parser.linkTags.values())
130         {
131             if (linkTag.nodeStartTag.coordinate == null)
132             {
133                 nodeTags.add(linkTag.nodeStartTag);
134             }
135             if (linkTag.nodeEndTag.coordinate == null)
136             {
137                 nodeTags.add(linkTag.nodeEndTag);
138             }
139         }
140 
141         while (nodeTags.size() > 0)
142         {
143             boolean found = false;
144             for (LinkTag linkTag : parser.linkTags.values())
145             {
146                 if (linkTag.straightTag != null || linkTag.arcTag != null)
147                 {
148                     if (nodeTags.contains(linkTag.nodeStartTag) == nodeTags.contains(linkTag.nodeEndTag))
149                     {
150                         continue;
151                     }
152 
153                     if (linkTag.straightTag != null)
154                     {
155                         double lengthSI = linkTag.straightTag.length.getSI();
156                         if (linkTag.nodeEndTag.node == null)
157                         {
158                             CartesianPoint coordinate = new CartesianPoint(linkTag.nodeStartTag.node.getLocation().getX(),
159                                     linkTag.nodeStartTag.node.getLocation().getY(),
160                                     linkTag.nodeStartTag.node.getLocation().getZ());
161                             double angle = linkTag.nodeStartTag.node.getDirection().getSI();
162                             double slope = linkTag.nodeStartTag.node.getSlope().getSI();
163                             coordinate.x += lengthSI * Math.cos(angle);
164                             coordinate.y += lengthSI * Math.sin(angle);
165                             coordinate.z += lengthSI * Math.sin(slope);
166                             NodeTag nodeTag = linkTag.nodeEndTag;
167                             nodeTag.angle = new Direction(angle, AngleUnit.SI);
168                             nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
169                             nodeTag.slope = new Direction(slope, AngleUnit.SI);
170                             linkTag.nodeEndTag.node = NodeTag.makeOTSNode(nodeTag, parser);
171                             nodeTags.remove(linkTag.nodeEndTag);
172                         }
173                         else if (linkTag.nodeStartTag.node == null)
174                         {
175                             CartesianPoint coordinate = new CartesianPoint(linkTag.nodeEndTag.node.getLocation().getX(),
176                                     linkTag.nodeEndTag.node.getLocation().getY(), linkTag.nodeEndTag.node.getLocation().getZ());
177                             double angle = linkTag.nodeEndTag.node.getDirection().getSI();
178                             double slope = linkTag.nodeEndTag.node.getSlope().getSI();
179                             coordinate.x -= lengthSI * Math.cos(angle);
180                             coordinate.y -= lengthSI * Math.sin(angle);
181                             coordinate.z -= lengthSI * Math.sin(slope);
182                             NodeTag nodeTag = linkTag.nodeStartTag;
183                             nodeTag.angle = new Direction(angle, AngleUnit.SI);
184                             nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
185                             nodeTag.slope = new Direction(slope, AngleUnit.SI);
186                             linkTag.nodeStartTag.node = NodeTag.makeOTSNode(nodeTag, parser);
187                             nodeTags.remove(linkTag.nodeStartTag);
188                         }
189                     }
190                     else if (linkTag.arcTag != null)
191                     {
192                         double radiusSI = linkTag.arcTag.radius.getSI();
193                         double angle = linkTag.arcTag.angle.getSI();
194                         ArcDirection direction = linkTag.arcTag.direction;
195 
196                         if (linkTag.nodeEndTag.node == null)
197                         {
198                             CartesianPoint coordinate = new CartesianPoint(0.0, 0.0, 0.0);
199                             double startAngle = linkTag.nodeStartTag.node.getDirection().getSI();
200                             double slope = linkTag.nodeStartTag.node.getSlope().getSI();
201                             double lengthSI = radiusSI * angle;
202                             NodeTag nodeTag = linkTag.nodeEndTag;
203                             if (direction.equals(ArcDirection.LEFT))
204                             {
205                                 linkTag.arcTag.center = new OTSPoint3D(
206                                         linkTag.nodeStartTag.node.getLocation().getX()
207                                                 + radiusSI * Math.cos(startAngle + Math.PI / 2.0),
208                                         linkTag.nodeStartTag.node.getLocation().getY()
209                                                 + radiusSI * Math.sin(startAngle + Math.PI / 2.0),
210                                         0.0);
211                                 linkTag.arcTag.startAngle = startAngle - Math.PI / 2.0;
212                                 coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle + angle);
213                                 coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle + angle);
214                                 nodeTag.angle = new Direction(AngleUtil.normalize(startAngle + angle), AngleUnit.SI);
215                             }
216                             else
217                             {
218                                 linkTag.arcTag.center = new OTSPoint3D(
219                                         linkTag.nodeStartTag.node.getLocation().getX()
220                                                 - radiusSI * Math.cos(startAngle + Math.PI / 2.0),
221                                         linkTag.nodeStartTag.node.getLocation().getY()
222                                                 - radiusSI * Math.sin(startAngle + Math.PI / 2.0),
223                                         0.0);
224                                 linkTag.arcTag.startAngle = startAngle + Math.PI / 2.0;
225                                 coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle - angle);
226                                 coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle - angle);
227                                 nodeTag.angle = new Direction(AngleUtil.normalize(startAngle - angle), AngleUnit.SI);
228                             }
229                             coordinate.z = linkTag.nodeStartTag.node.getLocation().getZ() + lengthSI * Math.sin(slope);
230                             nodeTag.slope = new Direction(slope, AngleUnit.SI);
231                             nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
232                             linkTag.nodeEndTag.node = NodeTag.makeOTSNode(nodeTag, parser);
233                             nodeTags.remove(linkTag.nodeEndTag);
234                         }
235 
236                         else if (linkTag.nodeStartTag.node == null)
237                         {
238                             CartesianPoint coordinate = new CartesianPoint(linkTag.nodeEndTag.node.getLocation().getX(),
239                                     linkTag.nodeEndTag.node.getLocation().getY(), linkTag.nodeEndTag.node.getLocation().getZ());
240                             double endAngle = linkTag.nodeEndTag.node.getDirection().getSI();
241                             double slope = linkTag.nodeEndTag.node.getSlope().getSI();
242                             double lengthSI = radiusSI * angle;
243                             NodeTag nodeTag = linkTag.nodeStartTag;
244                             if (direction.equals(ArcDirection.LEFT))
245                             {
246                                 linkTag.arcTag.center =
247                                         new OTSPoint3D(coordinate.x + radiusSI * Math.cos(endAngle + Math.PI / 2.0),
248                                                 coordinate.y + radiusSI * Math.sin(endAngle + Math.PI / 2.0), 0.0);
249                                 linkTag.arcTag.startAngle = endAngle - Math.PI / 2.0 - angle;
250                                 coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle);
251                                 coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle);
252                                 nodeTag.angle = new Direction(AngleUtil.normalize(linkTag.arcTag.startAngle + Math.PI / 2.0),
253                                         AngleUnit.SI);
254                             }
255                             else
256                             {
257                                 linkTag.arcTag.center =
258                                         new OTSPoint3D(coordinate.x + radiusSI * Math.cos(endAngle - Math.PI / 2.0),
259                                                 coordinate.y + radiusSI * Math.sin(endAngle - Math.PI / 2.0), 0.0);
260                                 linkTag.arcTag.startAngle = endAngle + Math.PI / 2.0 + angle;
261                                 coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle);
262                                 coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle);
263                                 nodeTag.angle = new Direction(AngleUtil.normalize(linkTag.arcTag.startAngle - Math.PI / 2.0),
264                                         AngleUnit.SI);
265                             }
266                             coordinate.z -= lengthSI * Math.sin(slope);
267                             nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
268                             nodeTag.slope = new Direction(slope, AngleUnit.SI);
269                             linkTag.nodeStartTag.node = NodeTag.makeOTSNode(nodeTag, parser);
270                             nodeTags.remove(linkTag.nodeStartTag);
271                         }
272                     }
273                 }
274             }
275             if (!found)
276             {
277                 throw new NetworkException("Cannot find coordinates of one or more nodes");
278             }
279         }
280 
281         // are there straight tags with nodes without an angle?
282         for (LinkTag linkTag : parser.linkTags.values())
283         {
284             if (linkTag.straightTag != null && linkTag.nodeStartTag.coordinate != null && linkTag.nodeEndTag.coordinate != null)
285             {
286                 if (linkTag.nodeStartTag.angle == null)
287                 {
288                     double dx = linkTag.nodeEndTag.coordinate.x - linkTag.nodeStartTag.coordinate.x;
289                     double dy = linkTag.nodeEndTag.coordinate.y - linkTag.nodeStartTag.coordinate.y;
290                     linkTag.nodeStartTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
291                 }
292                 if (linkTag.nodeEndTag.angle == null)
293                 {
294                     double dx = linkTag.nodeEndTag.coordinate.x - linkTag.nodeStartTag.coordinate.x;
295                     double dy = linkTag.nodeEndTag.coordinate.y - linkTag.nodeStartTag.coordinate.y;
296                     linkTag.nodeEndTag.angle = new Direction(Math.atan2(dy, dx), AngleUnit.RADIAN);
297                 }
298             }
299         }
300 
301         // which nodes have not yet been created?
302         for (NodeTag nodeTag : parser.nodeTags.values())
303         {
304             if (nodeTag.coordinate != null && nodeTag.node == null)
305             {
306                 if (nodeTag.angle == null)
307                 {
308                     nodeTag.angle = Direction.ZERO;
309                 }
310                 if (nodeTag.slope == null)
311                 {
312                     nodeTag.slope = Direction.ZERO;
313                 }
314                 nodeTag.node = NodeTag.makeOTSNode(nodeTag, parser);
315             }
316         }
317 
318     }
319 
320     /**
321      * Find the nodes one by one that have one coordinate defined, and one not defined, and try to build the network from there.
322      * @param linkTag the link to process
323      * @param parser the parser with the lists of information
324      * @param simulator to be able to make the animation
325      * @throws OTSGeometryException when both nodes are null.
326      * @throws NamingException when node animation cannot link to the animation context.
327      * @throws NetworkException when tag type not filled
328      */
329     static void buildLink(final LinkTag linkTag, final XmlNetworkLaneParser parser, final OTSDEVSSimulatorInterface simulator)
330             throws OTSGeometryException, NamingException, NetworkException
331     {
332         NodeTag from = linkTag.nodeStartTag;
333         OTSPoint3D startPoint = new OTSPoint3D(from.coordinate);
334         double startAngle = from.angle.si;
335         if (linkTag.offsetStart != null && linkTag.offsetStart.si != 0.0)
336         {
337             // shift the start point perpendicular to the node direction or read from tag
338             double offset = linkTag.offsetStart.si;
339             startPoint = new OTSPoint3D(startPoint.x + offset * Math.cos(startAngle + Math.PI / 2.0),
340                     startPoint.y + offset * Math.sin(startAngle + Math.PI / 2.0), startPoint.z);
341             System.out
342                     .println("fc = " + from.coordinate + ", sa = " + startAngle + ", so = " + offset + ", sp = " + startPoint);
343         }
344 
345         NodeTag to = linkTag.nodeEndTag;
346         OTSPoint3D endPoint = new OTSPoint3D(to.coordinate);
347         double endAngle = to.angle.si;
348         if (linkTag.offsetEnd != null && linkTag.offsetEnd.si != 0.0)
349         {
350             // shift the start point perpendicular to the node direction or read from tag
351             double offset = linkTag.offsetEnd.si;
352             endPoint = new OTSPoint3D(endPoint.x + offset * Math.cos(endAngle + Math.PI / 2.0),
353                     endPoint.y + offset * Math.sin(endAngle + Math.PI / 2.0), endPoint.z);
354             System.out.println("tc = " + to.coordinate + ", ea = " + endAngle + ", eo = " + offset + ", ep = " + endPoint);
355         }
356 
357         OTSPoint3D[] coordinates = null;
358 
359         if (linkTag.straightTag != null)
360         {
361             coordinates = new OTSPoint3D[2];
362             coordinates[0] = startPoint;
363             coordinates[1] = endPoint;
364         }
365 
366         else if (linkTag.polyLineTag != null)
367         {
368             int intermediatePoints = linkTag.polyLineTag.coordinates.length;
369             coordinates = new OTSPoint3D[intermediatePoints + 2];
370             coordinates[0] = startPoint;
371             coordinates[intermediatePoints + 1] = endPoint;
372             for (int p = 0; p < intermediatePoints; p++)
373             {
374                 coordinates[p + 1] = linkTag.polyLineTag.coordinates[p];
375             }
376 
377         }
378         else if (linkTag.arcTag != null)
379         {
380             // calculate the center position
381             double radiusSI = linkTag.arcTag.radius.getSI();
382             double offsetStart = 0.0;
383             if (linkTag.offsetStart != null)
384             {
385                 offsetStart = linkTag.offsetStart.si;
386             }
387             double offsetEnd = 0.0;
388             if (linkTag.offsetEnd != null)
389             {
390                 offsetEnd = linkTag.offsetEnd.si;
391             }
392             List<OTSPoint3D> center = OTSPoint3D.circleIntersections(from.coordinate, radiusSI + offsetStart, to.coordinate,
393                     radiusSI + offsetEnd);
394             OTSPoint3D c = linkTag.arcTag.center =
395                     (linkTag.arcTag.direction.equals(ArcTag.ArcDirection.RIGHT)) ? center.get(0) : center.get(1);
396 
397             // calculate start angle and end angle
398             double sa = linkTag.arcTag.startAngle = Math.atan2(from.coordinate.y - c.y, from.coordinate.x - c.x);
399             double ea = Math.atan2(to.coordinate.y - c.y, to.coordinate.x - c.x);
400             if (linkTag.arcTag.direction.equals(ArcDirection.RIGHT))
401             {
402                 // right -> negative direction, ea should be less than sa
403                 ea = (sa < ea) ? ea + Math.PI * 2.0 : ea;
404             }
405             else
406             {
407                 // left -> positive direction, sa should be less than ea
408                 ea = (ea < sa) ? ea + Math.PI * 2.0 : ea;
409             }
410 
411             int points = (Angle.normalize2Pi(ea - sa) <= Math.PI / 2.0) ? 64 : 128;
412             coordinates = new OTSPoint3D[points];
413             coordinates[0] = new OTSPoint3D(from.coordinate.x + Math.cos(sa) * offsetStart,
414                     from.coordinate.y + Math.sin(sa) * offsetStart, from.coordinate.z);
415             coordinates[coordinates.length - 1] = new OTSPoint3D(to.coordinate.x + Math.cos(ea) * offsetEnd,
416                     to.coordinate.y + Math.sin(ea) * offsetEnd, to.coordinate.z);
417             double angleStep = linkTag.arcTag.angle.getSI() / points;
418             double slopeStep = (to.coordinate.z - from.coordinate.z) / points;
419 
420             if (linkTag.arcTag.direction.equals(ArcDirection.RIGHT))
421             {
422                 for (int p = 1; p < points - 1; p++)
423                 {
424                     double dRad = offsetStart + (offsetEnd - offsetStart) * p / points;
425                     coordinates[p] = new OTSPoint3D(
426                             linkTag.arcTag.center.x + (radiusSI + dRad) * Math.cos(linkTag.arcTag.startAngle - angleStep * p),
427                             linkTag.arcTag.center.y + (radiusSI + dRad) * Math.sin(linkTag.arcTag.startAngle - angleStep * p),
428                             from.coordinate.z + slopeStep * p);
429                 }
430             }
431             else
432             {
433                 for (int p = 1; p < points - 1; p++)
434                 {
435                     double dRad = offsetStart + (offsetEnd - offsetStart) * p / points;
436                     coordinates[p] = new OTSPoint3D(
437                             linkTag.arcTag.center.x + (radiusSI + dRad) * Math.cos(linkTag.arcTag.startAngle + angleStep * p),
438                             linkTag.arcTag.center.y + (radiusSI + dRad) * Math.sin(linkTag.arcTag.startAngle + angleStep * p),
439                             from.coordinate.z + slopeStep * p);
440                 }
441             }
442         }
443 
444         else if (linkTag.bezierTag != null)
445         {
446             coordinates = Bezier
447                     .cubic(128, new DirectedPoint(startPoint.x, startPoint.y, startPoint.z, 0, 0, startAngle),
448                             new DirectedPoint(endPoint.x, endPoint.y, endPoint.z, 0, 0, endAngle), linkTag.bezierTag.shape)
449                     .getPoints();
450         }
451 
452         else
453         {
454             throw new NetworkException(
455                     "Making link, but link " + linkTag.name + " has no filled straight, arc, or bezier curve");
456         }
457 
458         OTSLine3D designLine = OTSLine3D.createAndCleanOTSLine3D(coordinates);
459 
460         // Directionality has to be added later when the lanes and their direction are known.
461         CrossSectionLink link = new CrossSectionLink(parser.network, linkTag.name, linkTag.nodeStartTag.node,
462                 linkTag.nodeEndTag.node, LinkType.ALL, designLine, simulator,
463                 new HashMap<GTUType, LongitudinalDirectionality>(), linkTag.laneKeepingPolicy);
464         try
465         {
466             new LinkAnimation(link, simulator, 0.5f);
467         }
468         catch (RemoteException exception)
469         {
470             exception.printStackTrace();
471         }
472         linkTag.link = link;
473     }
474 
475     /**
476      * @param linkTag the link to process
477      * @param parser the parser with the lists of information
478      * @param simulator to be able to make the animation
479      * @throws NetworkException when the stripe cannot be instantiated
480      * @throws NamingException when the /animation/2D tree cannot be found in the context
481      * @throws SAXException when the stripe type cannot be parsed correctly
482      * @throws GTUException when lane block cannot be created
483      * @throws OTSGeometryException when construction of the offset-line or contour fails
484      * @throws SimRuntimeException when construction of the generator fails
485      */
486     @SuppressWarnings({ "checkstyle:needbraces", "checkstyle:methodlength" })
487     static void applyRoadTypeToLink(final LinkTag linkTag, final XmlNetworkLaneParser parser,
488             final OTSDEVSSimulatorInterface simulator)
489             throws NetworkException, NamingException, SAXException, GTUException, OTSGeometryException, SimRuntimeException
490     {
491         CrossSectionLink csl = linkTag.link;
492         List<CrossSectionElement> cseList = new ArrayList<>();
493         List<Lane> lanes = new ArrayList<>();
494         // TODO Map<GTUType, LongitudinalDirectionality> linkDirections = new HashMap<>();
495         LongitudinalDirectionality linkDirection = LongitudinalDirectionality.DIR_NONE;
496         for (CrossSectionElementTag cseTag : linkTag.roadLayoutTag.cseTags.values())
497         {
498             LaneOverrideTag laneOverrideTag = null;
499             if (linkTag.laneOverrideTags.containsKey(cseTag.name))
500                 laneOverrideTag = linkTag.laneOverrideTags.get(cseTag.name);
501 
502             switch (cseTag.elementType)
503             {
504                 case STRIPE:
505                     switch (cseTag.stripeType)
506                     {
507                         case BLOCKED:
508                         case DASHED:
509                             Stripe dashedLine = new Stripe(csl, cseTag.offset, cseTag.width);
510                             dashedLine.addPermeability(GTUType.ALL, Permeable.BOTH);
511                             if (simulator != null && simulator instanceof AnimatorInterface)
512                             {
513                                 try
514                                 {
515                                     new StripeAnimation(dashedLine, simulator, StripeAnimation.TYPE.DASHED);
516                                 }
517                                 catch (RemoteException exception)
518                                 {
519                                     exception.printStackTrace();
520                                 }
521                             }
522                             cseList.add(dashedLine);
523                             break;
524 
525                         case DOUBLE:
526                             Stripe doubleLine = new Stripe(csl, cseTag.offset, cseTag.width);
527                             if (simulator != null && simulator instanceof AnimatorInterface)
528                             {
529                                 try
530                                 {
531                                     new StripeAnimation(doubleLine, simulator, StripeAnimation.TYPE.DOUBLE);
532                                 }
533                                 catch (RemoteException exception)
534                                 {
535                                     exception.printStackTrace();
536                                 }
537                             }
538                             cseList.add(doubleLine);
539                             break;
540 
541                         case LEFTONLY:
542                             Stripe leftOnlyLine = new Stripe(csl, cseTag.offset, cseTag.width);
543                             leftOnlyLine.addPermeability(GTUType.ALL, Permeable.LEFT); // TODO correct?
544                             if (simulator != null && simulator instanceof AnimatorInterface)
545                             {
546                                 try
547                                 {
548                                     new StripeAnimation(leftOnlyLine, simulator, StripeAnimation.TYPE.LEFTONLY);
549                                 }
550                                 catch (RemoteException exception)
551                                 {
552                                     exception.printStackTrace();
553                                 }
554                             }
555                             cseList.add(leftOnlyLine);
556                             break;
557 
558                         case RIGHTONLY:
559                             Stripe rightOnlyLine = new Stripe(csl, cseTag.offset, cseTag.width);
560                             rightOnlyLine.addPermeability(GTUType.ALL, Permeable.RIGHT); // TODO correct?
561                             if (simulator != null && simulator instanceof AnimatorInterface)
562                             {
563                                 try
564                                 {
565                                     new StripeAnimation(rightOnlyLine, simulator, StripeAnimation.TYPE.RIGHTONLY);
566                                 }
567                                 catch (RemoteException exception)
568                                 {
569                                     exception.printStackTrace();
570                                 }
571                             }
572                             cseList.add(rightOnlyLine);
573                             break;
574 
575                         case SOLID:
576                             Stripe solidLine = new Stripe(csl, cseTag.offset, cseTag.width);
577                             if (simulator != null && simulator instanceof AnimatorInterface)
578                             {
579                                 try
580                                 {
581                                     new StripeAnimation(solidLine, simulator, StripeAnimation.TYPE.SOLID);
582                                 }
583                                 catch (RemoteException exception)
584                                 {
585                                     exception.printStackTrace();
586                                 }
587                             }
588                             cseList.add(solidLine);
589                             break;
590 
591                         default:
592                             throw new SAXException("Unknown Stripe type: " + cseTag.stripeType.toString());
593                     }
594                     break;
595 
596                 case LANE:
597                 {
598                     LongitudinalDirectionality direction = cseTag.direction;
599                     Color color = cseTag.color;
600                     OvertakingConditions overtakingConditions = cseTag.overtakingConditions;
601                     if (laneOverrideTag != null)
602                     {
603                         if (laneOverrideTag.overtakingConditions != null)
604                             overtakingConditions = laneOverrideTag.overtakingConditions;
605                         if (laneOverrideTag.color != null)
606                             color = laneOverrideTag.color;
607                         if (laneOverrideTag.direction != null)
608                             direction = laneOverrideTag.direction;
609                     }
610                     Map<GTUType, LongitudinalDirectionality> directionality = new LinkedHashMap<>();
611                     directionality.put(GTUType.ALL, direction);
612                     if (linkDirection.equals(LongitudinalDirectionality.DIR_NONE))
613                     {
614                         linkDirection = direction;
615                     }
616                     else if (linkDirection.isForward())
617                     {
618                         if (direction.isBackwardOrBoth())
619                         {
620                             linkDirection = LongitudinalDirectionality.DIR_BOTH;
621                         }
622                     }
623                     else if (linkDirection.isBackward())
624                     {
625                         if (direction.isForwardOrBoth())
626                         {
627                             linkDirection = LongitudinalDirectionality.DIR_BOTH;
628                         }
629                     }
630 
631                     // XXX: Quick hack to solve the error that the lane directionality has not (yet) been registered at the link
632                     csl.addDirectionality(GTUType.ALL, linkDirection);
633 
634                     // XXX: LaneTypes with compatibilities might have to be defined in a new way -- LaneType.ALL for now...
635                     Lane lane = new Lane(csl, cseTag.name, cseTag.offset, cseTag.offset, cseTag.width, cseTag.width,
636                             LaneType.ALL, directionality, cseTag.legalSpeedLimits, overtakingConditions);
637                     // System.out.println(OTSGeometry.printCoordinates("#link design line: \nc1,0,0\n#",
638                     // lane.getParentLink().getDesignLine(), "\n "));
639                     // System.out.println(OTSGeometry.printCoordinates("#lane center line: \nc0,1,0\n#", lane.getCenterLine(),
640                     // "\n "));
641                     // System.out.println(OTSGeometry.printCoordinates("#lane contour: \nc0,0,1\n#", lane.getContour(),
642                     // "\n "));
643                     cseList.add(lane);
644                     lanes.add(lane);
645                     linkTag.lanes.put(cseTag.name, lane);
646                     if (simulator != null && simulator instanceof AnimatorInterface)
647                     {
648                         try
649                         {
650                             new LaneAnimation(lane, simulator, color, false);
651                         }
652                         catch (RemoteException exception)
653                         {
654                             exception.printStackTrace();
655                         }
656                     }
657 
658                     // SINK
659                     if (linkTag.sinkTags.keySet().contains(cseTag.name))
660                     {
661                         SinkTag sinkTag = linkTag.sinkTags.get(cseTag.name);
662                         Length position = LinkTag.parseBeginEndPosition(sinkTag.positionStr, lane);
663                         new SinkSensor(lane, position, simulator);
664                     }
665 
666                     // BLOCK
667                     if (linkTag.blockTags.containsKey(cseTag.name))
668                     {
669                         BlockTag blockTag = linkTag.blockTags.get(cseTag.name);
670                         Length position = LinkTag.parseBeginEndPosition(blockTag.positionStr, lane);
671                         new LaneBlock(UUID.randomUUID().toString(), lane, position, Length.ZERO);
672                     }
673 
674                     // TRAFFICLIGHT
675                     if (linkTag.trafficLightTags.containsKey(cseTag.name))
676                     {
677                         for (TrafficLightTag trafficLightTag : linkTag.trafficLightTags.get(cseTag.name))
678                         {
679                             try
680                             {
681                                 Class<?> clazz = Class.forName(trafficLightTag.className);
682                                 Constructor<?> trafficLightConstructor = ClassUtil.resolveConstructor(clazz, new Class[] {
683                                         String.class, Lane.class, Length.class, OTSDEVSSimulatorInterface.class });
684                                 Length position = LinkTag.parseBeginEndPosition(trafficLightTag.positionStr, lane);
685                                 SimpleTrafficLight trafficLight = (SimpleTrafficLight) trafficLightConstructor
686                                         .newInstance(new Object[] { trafficLightTag.name, lane, position, simulator });
687                             }
688                             catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
689                                     | IllegalAccessException | IllegalArgumentException | InvocationTargetException
690                                     | NetworkException exception)
691                             {
692                                 throw new NetworkException("TRAFFICLIGHT: CLASS NAME " + trafficLightTag.className
693                                         + " for traffic light " + trafficLightTag.name + " on lane " + lane.toString()
694                                         + " -- class not found or constructor not right", exception);
695                             }
696                         }
697                     }
698 
699                     // GENERATOR
700                     if (linkTag.generatorTags.containsKey(cseTag.name))
701                     {
702                         GeneratorTag generatorTag = linkTag.generatorTags.get(cseTag.name);
703                         GeneratorTag.makeGenerator(generatorTag, parser, linkTag, simulator);
704                     }
705 
706                     // TODO LISTGENERATOR
707 
708                     // SENSOR
709                     if (linkTag.sensorTags.containsKey(cseTag.name))
710                     {
711                         for (SensorTag sensorTag : linkTag.sensorTags.get(cseTag.name))
712                         {
713                             try
714                             {
715                                 Class<?> clazz = Class.forName(sensorTag.className);
716                                 Constructor<?> sensorConstructor =
717                                         ClassUtil.resolveConstructor(clazz, new Class[] { String.class, Lane.class,
718                                                 Length.class, RelativePosition.TYPE.class, OTSDEVSSimulatorInterface.class });
719                                 Length position = LinkTag.parseBeginEndPosition(sensorTag.positionStr, lane);
720                                 // { String.class, Lane.class, Length.class, RelativePosition.TYPE.class,
721                                 // OTSDEVSSimulatorInterface.class }
722                                 sensorConstructor.newInstance(
723                                         new Object[] { sensorTag.name, lane, position, sensorTag.triggerPosition, simulator });
724                             }
725                             catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
726                                     | IllegalAccessException | IllegalArgumentException | InvocationTargetException
727                                     | NetworkException exception)
728                             {
729                                 throw new NetworkException("SENSOR: CLASS NAME " + sensorTag.className + " for sensor "
730                                         + sensorTag.name + " on lane " + lane.toString()
731                                         + " -- class not found or constructor not right", exception);
732                             }
733                         }
734                     }
735 
736                     // FILL
737                     if (linkTag.fillTags.containsKey(cseTag.name))
738                     {
739                         FillTag fillTag = linkTag.fillTags.get(cseTag.name);
740                         FillTag.makeFill(fillTag, parser, linkTag, simulator);
741                     }
742                     break;
743                 }
744 
745                 case NOTRAFFICLANE:
746                 {
747                     Lane lane = new NoTrafficLane(csl, cseTag.name, cseTag.offset, cseTag.offset, cseTag.width, cseTag.width);
748                     cseList.add(lane);
749                     if (simulator != null && simulator instanceof AnimatorInterface)
750                     {
751                         try
752                         {
753                             Color color = cseTag.color;
754                             if (laneOverrideTag != null)
755                             {
756                                 if (laneOverrideTag.color != null)
757                                     color = laneOverrideTag.color;
758                             }
759                             new LaneAnimation(lane, simulator, color, false);
760                         }
761                         catch (RemoteException exception)
762                         {
763                             exception.printStackTrace();
764                         }
765                     }
766                     break;
767                 }
768 
769                 case SHOULDER:
770                 {
771                     Shoulder shoulder = new Shoulder(csl, cseTag.name, cseTag.offset, cseTag.width);
772                     cseList.add(shoulder);
773                     if (simulator != null && simulator instanceof AnimatorInterface)
774                     {
775                         try
776                         {
777                             Color color = cseTag.color;
778                             if (laneOverrideTag != null)
779                             {
780                                 if (laneOverrideTag.color != null)
781                                     color = laneOverrideTag.color;
782                             }
783                             new ShoulderAnimation(shoulder, simulator, color);
784                         }
785                         catch (RemoteException exception)
786                         {
787                             exception.printStackTrace();
788                         }
789                     }
790                     break;
791                 }
792 
793                 default:
794                     throw new SAXException("Unknown Element type: " + cseTag.elementType.toString());
795             }
796 
797         } // for (CrossSectionElementTag cseTag : roadTypeTag.cseTags.values())
798 
799         // add the calculated direction to the link
800         csl.addDirectionality(GTUType.ALL, linkDirection);
801     }
802 }