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