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