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