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