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