Links.java

  1. package org.opentrafficsim.road.network.factory.xml;

  2. import java.awt.Color;
  3. import java.io.Serializable;
  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 javax.naming.NamingException;

  15. import org.djunits.unit.AngleUnit;
  16. import org.djunits.value.AngleUtil;
  17. import org.djunits.value.vdouble.scalar.Direction;
  18. import org.djunits.value.vdouble.scalar.Length;
  19. import org.djunits.value.vdouble.scalar.Speed;
  20. import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
  21. import org.opentrafficsim.core.geometry.Bezier;
  22. import org.opentrafficsim.core.geometry.OTSGeometryException;
  23. import org.opentrafficsim.core.geometry.OTSLine3D;
  24. import org.opentrafficsim.core.geometry.OTSPoint3D;
  25. import org.opentrafficsim.core.gtu.GTUException;
  26. import org.opentrafficsim.core.gtu.GTUType;
  27. import org.opentrafficsim.core.gtu.RelativePosition;
  28. import org.opentrafficsim.core.network.LinkType;
  29. import org.opentrafficsim.core.network.LongitudinalDirectionality;
  30. import org.opentrafficsim.core.network.NetworkException;
  31. import org.opentrafficsim.core.network.OTSNetwork;
  32. import org.opentrafficsim.road.gtu.lane.object.AbstractTrafficLight;
  33. import org.opentrafficsim.road.gtu.lane.object.LaneBlock;
  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.AbstractSensor;
  39. import org.opentrafficsim.road.network.lane.CrossSectionElement;
  40. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  41. import org.opentrafficsim.road.network.lane.Lane;
  42. import org.opentrafficsim.road.network.lane.NoTrafficLane;
  43. import org.opentrafficsim.road.network.lane.Sensor;
  44. import org.opentrafficsim.road.network.lane.Shoulder;
  45. import org.opentrafficsim.road.network.lane.SinkSensor;
  46. import org.opentrafficsim.road.network.lane.Stripe;
  47. import org.opentrafficsim.road.network.lane.Stripe.Permeable;
  48. import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
  49. import org.xml.sax.SAXException;

  50. import nl.tudelft.simulation.dsol.SimRuntimeException;
  51. import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
  52. import nl.tudelft.simulation.language.d3.DirectedPoint;
  53. import nl.tudelft.simulation.language.reflection.ClassUtil;

  54. /**
  55.  * <p>
  56.  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  57.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  58.  * <p>
  59.  * LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  60.  * initial version Jul 25, 2015 <br>
  61.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  62.  */
  63. final class Links
  64. {
  65.     /** Utility class. */
  66.     private Links()
  67.     {
  68.         // do not instantiate
  69.     }

  70.     /** Helper class to temporarily store coordinate. */
  71.     private static class XYZ implements Serializable
  72.     {
  73.         /** */
  74.         private static final long serialVersionUID = 20150725L;

  75.         /** The x coordinate. */
  76.         @SuppressWarnings("checkstyle:visibilitymodifier")
  77.         double x;

  78.         /** The y coordinate. */
  79.         @SuppressWarnings("checkstyle:visibilitymodifier")
  80.         double y;

  81.         /** The z coordinate. */
  82.         @SuppressWarnings("checkstyle:visibilitymodifier")
  83.         double z;

  84.         /**
  85.          * @param x the x coordinate
  86.          * @param y the y coordinate
  87.          * @param z the z coordinate
  88.          */
  89.         public XYZ(final double x, final double y, final double z)
  90.         {
  91.             super();
  92.             this.x = x;
  93.             this.y = y;
  94.             this.z = z;
  95.         }

  96.         /** {@inheritDoc} */
  97.         @Override
  98.         public final String toString()
  99.         {
  100.             return "XYZ [x=" + this.x + ", y=" + this.y + ", z=" + this.z + "]";
  101.         }
  102.     }

  103.     /**
  104.      * Find the nodes one by one that have one coordinate defined, and one not defined, and try to build the network from there.
  105.      * @param parser the parser with the lists of information
  106.      * @throws NetworkException when both nodes are null.
  107.      * @throws NamingException when node animation cannot link to the animation context.
  108.      */
  109.     @SuppressWarnings("methodlength")
  110.     static void calculateNodeCoordinates(final XmlNetworkLaneParser parser) throws NetworkException, NamingException
  111.     {
  112.         Set<LinkTag> links = new HashSet<>(parser.linkTags.values());
  113.         while (!links.isEmpty())
  114.         {
  115.             System.out.println(links);
  116.             boolean found = false;
  117.             for (LinkTag linkTag : links)
  118.             {
  119.                 if (linkTag.nodeStartTag.node != null && linkTag.nodeEndTag.node != null)
  120.                 {
  121.                     links.remove(linkTag);
  122.                     found = true;
  123.                     break;
  124.                 }
  125.                 if (linkTag.nodeStartTag.node != null && linkTag.nodeEndTag.node == null)
  126.                 {
  127.                     calculateNodeCoordinates(linkTag, parser);
  128.                     links.remove(linkTag);
  129.                     found = true;
  130.                     break;
  131.                 }
  132.                 if (linkTag.nodeStartTag.node == null && linkTag.nodeEndTag.node != null)
  133.                 {
  134.                     calculateNodeCoordinates(linkTag, parser);
  135.                     links.remove(linkTag);
  136.                     found = true;
  137.                     break;
  138.                 }
  139.             }
  140.             if (!found)
  141.             {
  142.                 String linkStr = "";
  143.                 boolean first = true;
  144.                 for (LinkTag linkTag : links)
  145.                 {
  146.                     linkStr += first ? "[" : ", ";
  147.                     linkStr += linkTag.name;
  148.                     first = false;
  149.                 }
  150.                 linkStr += "]";
  151.                 throw new NetworkException("Links parser found unconnected links in network: " + linkStr);
  152.             }
  153.         }
  154.     }

  155.     /**
  156.      * One of the nodes probably has a coordinate and the other not. Calculate the other coordinate and save the Node.
  157.      * @param linkTag the parsed information from the XML file.
  158.      * @param parser the parser with the lists of information
  159.      * @throws NetworkException when both nodes are null.
  160.      * @throws NamingException when node animation cannot link to the animation context.
  161.      */
  162.     @SuppressWarnings("checkstyle:methodlength")
  163.     static void calculateNodeCoordinates(final LinkTag linkTag, final XmlNetworkLaneParser parser) throws NetworkException,
  164.             NamingException
  165.     {
  166.         // if all are defined, return...
  167.         if (linkTag.nodeStartTag.node != null && linkTag.nodeStartTag.angle != null && linkTag.nodeEndTag.node != null
  168.                 && linkTag.nodeEndTag.angle != null)
  169.         {
  170.             System.err.println("Shouldn't happen");
  171.             return;
  172.         }

  173.         // calculate dx, dy and dz for the straight or the arc.
  174.         if (linkTag.nodeStartTag.node != null && linkTag.nodeEndTag.node != null)
  175.         {
  176.             System.err.println("Why here?");

  177.             // ARC with both points defined
  178.             if (linkTag.arcTag != null)
  179.             {
  180.                 double radiusSI = linkTag.arcTag.radius.getSI();
  181.                 ArcDirection direction = linkTag.arcTag.direction;
  182.                 OTSPoint3D coordinate =
  183.                         new OTSPoint3D(linkTag.nodeStartTag.node.getLocation().getX(), linkTag.nodeStartTag.node.getLocation()
  184.                                 .getY(), linkTag.nodeStartTag.node.getLocation().getZ());
  185.                 double startAngle = linkTag.nodeStartTag.node.getDirection().getSI();

  186.                 if (direction.equals(ArcDirection.LEFT))
  187.                 {
  188.                     linkTag.arcTag.center =
  189.                             new OTSPoint3D(coordinate.x + radiusSI * Math.cos(startAngle + Math.PI / 2.0), coordinate.y
  190.                                     + radiusSI * Math.sin(startAngle + Math.PI / 2.0), 0.0);
  191.                     linkTag.arcTag.startAngle = startAngle - Math.PI / 2.0;
  192.                 }
  193.                 else
  194.                 {
  195.                     linkTag.arcTag.center =
  196.                             new OTSPoint3D(coordinate.x + radiusSI * Math.cos(startAngle - Math.PI / 2.0), coordinate.y
  197.                                     + radiusSI * Math.sin(startAngle - Math.PI / 2.0), 0.0);
  198.                     linkTag.arcTag.startAngle = startAngle + Math.PI / 2.0;
  199.                 }
  200.                 return;
  201.             }

  202.             // STRAIGHT with both nodes defined
  203.             if (linkTag.straightTag != null)
  204.             {
  205.                 if (linkTag.straightTag.length != null)
  206.                 {
  207.                     throw new NetworkException("Parsing network. Link: " + linkTag.name
  208.                             + ", Start node and end node given, but also a length specified");
  209.                 }
  210.                 linkTag.straightTag.length = linkTag.nodeStartTag.node.getPoint().distance(linkTag.nodeEndTag.node.getPoint());
  211.                 // set the angles of the nodes
  212.                 double angle =
  213.                         Math.atan2(linkTag.nodeEndTag.node.getLocation().y - linkTag.nodeStartTag.node.getLocation().y,
  214.                                 linkTag.nodeEndTag.node.getLocation().x - linkTag.nodeStartTag.node.getLocation().x);
  215.                 // TODO test for over-specification (i.e. node direction was already specified)
  216.                 linkTag.nodeStartTag.angle = new Direction(angle, AngleUnit.SI);
  217.                 linkTag.nodeEndTag.angle = new Direction(angle, AngleUnit.SI);
  218.                 double slope = linkTag.nodeStartTag.node.getSlope().getSI();
  219.                 linkTag.nodeStartTag.slope = new Direction(slope, AngleUnit.SI);
  220.                 slope = linkTag.nodeEndTag.node.getSlope().getSI();
  221.                 linkTag.nodeEndTag.slope = new Direction(slope, AngleUnit.SI);
  222.             }
  223.         }

  224.         if (linkTag.nodeStartTag.node == null && linkTag.nodeEndTag.node == null)
  225.         {
  226.             throw new NetworkException("Parsing network. Link: " + linkTag.name + ", both From-node and To-node are null");
  227.         }

  228.         if (linkTag.straightTag != null)
  229.         {
  230.             double lengthSI = linkTag.straightTag.length.getSI();
  231.             if (linkTag.nodeEndTag.node == null)
  232.             {
  233.                 XYZ coordinate =
  234.                         new XYZ(linkTag.nodeStartTag.node.getLocation().getX(), linkTag.nodeStartTag.node.getLocation().getY(),
  235.                                 linkTag.nodeStartTag.node.getLocation().getZ());
  236.                 double angle = linkTag.nodeStartTag.node.getDirection().getSI();
  237.                 double slope = linkTag.nodeStartTag.node.getSlope().getSI();
  238.                 coordinate.x += lengthSI * Math.cos(angle);
  239.                 coordinate.y += lengthSI * Math.sin(angle);
  240.                 coordinate.z += lengthSI * Math.sin(slope);
  241.                 NodeTag nodeTag = linkTag.nodeEndTag;
  242.                 nodeTag.angle = new Direction(angle, AngleUnit.SI);
  243.                 nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
  244.                 nodeTag.slope = new Direction(slope, AngleUnit.SI);
  245.                 linkTag.nodeEndTag.node = NodeTag.makeOTSNode(nodeTag, parser);
  246.             }
  247.             else if (linkTag.nodeStartTag.node == null)
  248.             {
  249.                 XYZ coordinate =
  250.                         new XYZ(linkTag.nodeEndTag.node.getLocation().getX(), linkTag.nodeEndTag.node.getLocation().getY(),
  251.                                 linkTag.nodeEndTag.node.getLocation().getZ());
  252.                 double angle = linkTag.nodeEndTag.node.getDirection().getSI();
  253.                 double slope = linkTag.nodeEndTag.node.getSlope().getSI();
  254.                 coordinate.x -= lengthSI * Math.cos(angle);
  255.                 coordinate.y -= lengthSI * Math.sin(angle);
  256.                 coordinate.z -= lengthSI * Math.sin(slope);
  257.                 NodeTag nodeTag = linkTag.nodeStartTag;
  258.                 nodeTag.angle = new Direction(angle, AngleUnit.SI);
  259.                 nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
  260.                 nodeTag.slope = new Direction(slope, AngleUnit.SI);
  261.                 linkTag.nodeStartTag.node = NodeTag.makeOTSNode(nodeTag, parser);
  262.             }
  263.         }
  264.         else if (linkTag.arcTag != null)
  265.         {
  266.             double radiusSI = linkTag.arcTag.radius.getSI();
  267.             double angle = linkTag.arcTag.angle.getSI();
  268.             ArcDirection direction = linkTag.arcTag.direction;

  269.             if (linkTag.nodeEndTag.node == null)
  270.             {
  271.                 XYZ coordinate = new XYZ(0.0, 0.0, 0.0);
  272.                 double startAngle = linkTag.nodeStartTag.node.getDirection().getSI();
  273.                 double slope = linkTag.nodeStartTag.node.getSlope().getSI();
  274.                 double lengthSI = radiusSI * angle;
  275.                 NodeTag nodeTag = linkTag.nodeEndTag;
  276.                 if (direction.equals(ArcDirection.LEFT))
  277.                 {
  278.                     linkTag.arcTag.center =
  279.                             new OTSPoint3D(linkTag.nodeStartTag.node.getLocation().getX() + radiusSI
  280.                                     * Math.cos(startAngle + Math.PI / 2.0), linkTag.nodeStartTag.node.getLocation().getY()
  281.                                     + radiusSI * Math.sin(startAngle + Math.PI / 2.0), 0.0);
  282.                     linkTag.arcTag.startAngle = startAngle - Math.PI / 2.0;
  283.                     coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle + angle);
  284.                     coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle + angle);
  285.                     nodeTag.angle = new Direction(AngleUtil.normalize(startAngle + angle), AngleUnit.SI);
  286.                 }
  287.                 else
  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 Direction(AngleUtil.normalize(startAngle - angle), AngleUnit.SI);
  297.                 }
  298.                 coordinate.z = linkTag.nodeStartTag.node.getLocation().getZ() + lengthSI * Math.sin(slope);
  299.                 nodeTag.slope = new Direction(slope, AngleUnit.SI);
  300.                 nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
  301.                 linkTag.nodeEndTag.node = NodeTag.makeOTSNode(nodeTag, parser);
  302.             }

  303.             else if (linkTag.nodeStartTag.node == null)
  304.             {
  305.                 XYZ coordinate =
  306.                         new XYZ(linkTag.nodeEndTag.node.getLocation().getX(), linkTag.nodeEndTag.node.getLocation().getY(),
  307.                                 linkTag.nodeEndTag.node.getLocation().getZ());
  308.                 double endAngle = linkTag.nodeEndTag.node.getDirection().getSI();
  309.                 double slope = linkTag.nodeEndTag.node.getSlope().getSI();
  310.                 double lengthSI = radiusSI * angle;
  311.                 NodeTag nodeTag = linkTag.nodeStartTag;
  312.                 if (direction.equals(ArcDirection.LEFT))
  313.                 {
  314.                     linkTag.arcTag.center =
  315.                             new OTSPoint3D(coordinate.x + radiusSI * Math.cos(endAngle + Math.PI / 2.0), coordinate.y
  316.                                     + radiusSI * Math.sin(endAngle + Math.PI / 2.0), 0.0);
  317.                     linkTag.arcTag.startAngle = endAngle - Math.PI / 2.0 - angle;
  318.                     coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle);
  319.                     coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle);
  320.                     nodeTag.angle = new Direction(AngleUtil.normalize(linkTag.arcTag.startAngle + Math.PI / 2.0), AngleUnit.SI);
  321.                 }
  322.                 else
  323.                 {
  324.                     linkTag.arcTag.center =
  325.                             new OTSPoint3D(coordinate.x + radiusSI * Math.cos(endAngle - Math.PI / 2.0), coordinate.y
  326.                                     + radiusSI * Math.sin(endAngle - Math.PI / 2.0), 0.0);
  327.                     linkTag.arcTag.startAngle = endAngle + Math.PI / 2.0 + angle;
  328.                     coordinate.x = linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle);
  329.                     coordinate.y = linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle);
  330.                     nodeTag.angle = new Direction(AngleUtil.normalize(linkTag.arcTag.startAngle - Math.PI / 2.0), AngleUnit.SI);
  331.                 }
  332.                 coordinate.z -= lengthSI * Math.sin(slope);
  333.                 nodeTag.coordinate = new OTSPoint3D(coordinate.x, coordinate.y, coordinate.z);
  334.                 nodeTag.slope = new Direction(slope, AngleUnit.SI);
  335.                 linkTag.nodeStartTag.node = NodeTag.makeOTSNode(nodeTag, parser);
  336.             }
  337.         }
  338.         else
  339.         {
  340.             System.err.println("Problem!");
  341.         }
  342.     }

  343.     /**
  344.      * Find the nodes one by one that have one coordinate defined, and one not defined, and try to build the network from there.
  345.      * @param linkTag the link to process
  346.      * @param parser the parser with the lists of information
  347.      * @param simulator to be able to make the animation
  348.      * @throws OTSGeometryException when both nodes are null.
  349.      * @throws NamingException when node animation cannot link to the animation context.
  350.      * @throws NetworkException when tag type not filled
  351.      */
  352.     static void buildLink(final LinkTag linkTag, final XmlNetworkLaneParser parser, final OTSDEVSSimulatorInterface simulator)
  353.             throws OTSGeometryException, NamingException, NetworkException
  354.     {
  355.         NodeTag from = linkTag.nodeStartTag;
  356.         OTSPoint3D startPoint = new OTSPoint3D(from.coordinate);
  357.         double startAngle = linkTag.rotationStart != null ? linkTag.rotationStart.si : from.angle.si;
  358.         if (linkTag.offsetStart != null && linkTag.offsetStart.si != 0.0)
  359.         {
  360.             // shift the start point perpendicular to the node direction or read from tag
  361.             double offset = linkTag.offsetStart.si;
  362.             startPoint =
  363.                     new OTSPoint3D(startPoint.x + offset * Math.cos(startAngle + Math.PI / 2.0), startPoint.y + offset
  364.                             * Math.sin(startAngle + Math.PI / 2.0), startPoint.z);
  365.             System.out
  366.                     .println("fc = " + from.coordinate + ", sa = " + startAngle + ", so = " + offset + ", sp = " + startPoint);
  367.         }

  368.         NodeTag to = linkTag.nodeEndTag;
  369.         OTSPoint3D endPoint = new OTSPoint3D(to.coordinate);
  370.         double endAngle = linkTag.rotationEnd != null ? linkTag.rotationEnd.si : to.angle.si;
  371.         if (linkTag.offsetEnd != null && linkTag.offsetEnd.si != 0.0)
  372.         {
  373.             // shift the start point perpendicular to the node direction or read from tag
  374.             double offset = linkTag.offsetEnd.si;
  375.             endPoint =
  376.                     new OTSPoint3D(endPoint.x + offset * Math.cos(endAngle + Math.PI / 2.0), endPoint.y + offset
  377.                             * Math.sin(endAngle + Math.PI / 2.0), endPoint.z);
  378.             System.out.println("tc = " + to.coordinate + ", ea = " + endAngle + ", eo = " + offset + ", ep = " + endPoint);
  379.         }

  380.         OTSPoint3D[] coordinates = null;

  381.         if (linkTag.straightTag != null)
  382.         {
  383.             coordinates = new OTSPoint3D[2];
  384.             coordinates[0] = startPoint;
  385.             coordinates[1] = endPoint;
  386.         }

  387.         else if (linkTag.polyLineTag != null)
  388.         {
  389.             int intermediatePoints = linkTag.polyLineTag.coordinates.length;
  390.             coordinates = new OTSPoint3D[intermediatePoints + 2];
  391.             coordinates[0] = startPoint;
  392.             coordinates[intermediatePoints + 1] = endPoint;
  393.             for (int p = 0; p < intermediatePoints; p++)
  394.             {
  395.                 coordinates[p + 1] = linkTag.polyLineTag.coordinates[p];
  396.             }

  397.         }
  398.         else if (linkTag.arcTag != null)
  399.         {
  400.             // TODO move the radius if there is an start and end offset? How?
  401.             int points = (Math.abs(linkTag.arcTag.angle.getSI()) <= Math.PI / 2.0) ? 64 : 128;
  402.             coordinates = new OTSPoint3D[points];
  403.             coordinates[0] = new OTSPoint3D(from.coordinate.x, from.coordinate.y, from.coordinate.z);
  404.             coordinates[coordinates.length - 1] = new OTSPoint3D(to.coordinate.x, to.coordinate.y, to.coordinate.z);
  405.             double angleStep = linkTag.arcTag.angle.getSI() / points;
  406.             double slopeStep = (to.coordinate.z - from.coordinate.z) / points;
  407.             double radiusSI = linkTag.arcTag.radius.getSI();
  408.             if (linkTag.arcTag.direction.equals(ArcDirection.RIGHT))
  409.             {
  410.                 for (int p = 1; p < points - 1; p++)
  411.                 {
  412.                     coordinates[p] =
  413.                             new OTSPoint3D(linkTag.arcTag.center.x + radiusSI
  414.                                     * Math.cos(linkTag.arcTag.startAngle - angleStep * p), linkTag.arcTag.center.y + radiusSI
  415.                                     * Math.sin(linkTag.arcTag.startAngle - angleStep * p), from.coordinate.z + slopeStep * p);
  416.                 }
  417.             }
  418.             else
  419.             {
  420.                 for (int p = 1; p < points - 1; p++)
  421.                 {
  422.                     try
  423.                     {
  424.                         System.err.println("linkTag.arcTag.center = " + linkTag.arcTag.center);
  425.                         System.err.println("linkTag.arcTag.startAngle = " + linkTag.arcTag.startAngle);
  426.                         coordinates[p] =
  427.                                 new OTSPoint3D(linkTag.arcTag.center.x + radiusSI
  428.                                         * Math.cos(linkTag.arcTag.startAngle + angleStep * p), linkTag.arcTag.center.y
  429.                                         + radiusSI * Math.sin(linkTag.arcTag.startAngle + angleStep * p), from.coordinate.z
  430.                                         + slopeStep * p);
  431.                     }
  432.                     catch (NullPointerException npe)
  433.                     {
  434.                         npe.printStackTrace();
  435.                         System.err.println(npe.getMessage());
  436.                     }
  437.                 }
  438.             }
  439.         }

  440.         else if (linkTag.bezierTag != null)
  441.         {
  442.             coordinates =
  443.                     Bezier.cubic(128, new DirectedPoint(startPoint.x, startPoint.y, startPoint.z, 0, 0, startAngle),
  444.                             new DirectedPoint(endPoint.x, endPoint.y, endPoint.z, 0, 0, endAngle)).getPoints();
  445.         }

  446.         else
  447.         {
  448.             throw new NetworkException("Making link, but link " + linkTag.name
  449.                     + " has no filled straight, arc, or bezier curve");
  450.         }

  451.         OTSLine3D designLine = new OTSLine3D(coordinates);

  452.         // Directionality has to be added later when the lanes and their direction are known.
  453.         CrossSectionLink link =
  454.                 new CrossSectionLink(linkTag.name, linkTag.nodeStartTag.node, linkTag.nodeEndTag.node, LinkType.ALL,
  455.                         designLine, new HashMap<GTUType, LongitudinalDirectionality>(), linkTag.laneKeepingPolicy);
  456.         linkTag.link = link;
  457.     }

  458.     /**
  459.      * @param linkTag the link to process
  460.      * @param parser the parser with the lists of information
  461.      * @param simulator to be able to make the animation
  462.      * @throws NetworkException when the stripe cannot be instantiated
  463.      * @throws NamingException when the /animation/2D tree cannot be found in the context
  464.      * @throws SAXException when the stripe type cannot be parsed correctly
  465.      * @throws GTUException when lane block cannot be created
  466.      * @throws OTSGeometryException when construction of the offset-line or contour fails
  467.      * @throws SimRuntimeException when construction of the generator fails
  468.      */
  469.     @SuppressWarnings({ "checkstyle:needbraces", "checkstyle:methodlength" })
  470.     static void applyRoadTypeToLink(final LinkTag linkTag, final XmlNetworkLaneParser parser,
  471.             final OTSDEVSSimulatorInterface simulator) throws NetworkException, NamingException, SAXException, GTUException,
  472.             OTSGeometryException, SimRuntimeException
  473.     {
  474.         CrossSectionLink csl = linkTag.link;
  475.         List<CrossSectionElement> cseList = new ArrayList<>();
  476.         List<Lane> lanes = new ArrayList<>();
  477.         // TODO Map<GTUType, LongitudinalDirectionality> linkDirections = new HashMap<>();
  478.         LongitudinalDirectionality linkDirection = LongitudinalDirectionality.DIR_NONE;
  479.         for (CrossSectionElementTag cseTag : linkTag.roadTypeTag.cseTags.values())
  480.         {
  481.             LaneOverrideTag laneOverrideTag = null;
  482.             if (linkTag.laneOverrideTags.containsKey(cseTag.name))
  483.                 laneOverrideTag = linkTag.laneOverrideTags.get(cseTag.name);

  484.             switch (cseTag.elementType)
  485.             {
  486.                 case STRIPE:
  487.                     switch (cseTag.stripeType)
  488.                     {
  489.                         case BLOCKED:
  490.                         case DASHED:
  491.                             Stripe dashedLine = new Stripe(csl, cseTag.offset, cseTag.width);
  492.                             dashedLine.addPermeability(GTUType.ALL, Permeable.BOTH);
  493.                             if (simulator != null && simulator instanceof AnimatorInterface)
  494.                             {
  495.                                 try
  496.                                 {
  497.                                     new StripeAnimation(dashedLine, simulator, StripeAnimation.TYPE.DASHED);
  498.                                 }
  499.                                 catch (RemoteException exception)
  500.                                 {
  501.                                     exception.printStackTrace();
  502.                                 }
  503.                             }
  504.                             cseList.add(dashedLine);
  505.                             break;

  506.                         case DOUBLE:
  507.                             Stripe doubleLine = new Stripe(csl, cseTag.offset, cseTag.width);
  508.                             if (simulator != null && simulator instanceof AnimatorInterface)
  509.                             {
  510.                                 try
  511.                                 {
  512.                                     new StripeAnimation(doubleLine, simulator, StripeAnimation.TYPE.DOUBLE);
  513.                                 }
  514.                                 catch (RemoteException exception)
  515.                                 {
  516.                                     exception.printStackTrace();
  517.                                 }
  518.                             }
  519.                             cseList.add(doubleLine);
  520.                             break;

  521.                         case LEFTONLY:
  522.                             Stripe leftOnlyLine = new Stripe(csl, cseTag.offset, cseTag.width);
  523.                             leftOnlyLine.addPermeability(GTUType.ALL, Permeable.LEFT); // TODO correct?
  524.                             if (simulator != null && simulator instanceof AnimatorInterface)
  525.                             {
  526.                                 try
  527.                                 {
  528.                                     new StripeAnimation(leftOnlyLine, simulator, StripeAnimation.TYPE.LEFTONLY);
  529.                                 }
  530.                                 catch (RemoteException exception)
  531.                                 {
  532.                                     exception.printStackTrace();
  533.                                 }
  534.                             }
  535.                             cseList.add(leftOnlyLine);
  536.                             break;

  537.                         case RIGHTONLY:
  538.                             Stripe rightOnlyLine = new Stripe(csl, cseTag.offset, cseTag.width);
  539.                             rightOnlyLine.addPermeability(GTUType.ALL, Permeable.RIGHT); // TODO correct?
  540.                             if (simulator != null && simulator instanceof AnimatorInterface)
  541.                             {
  542.                                 try
  543.                                 {
  544.                                     new StripeAnimation(rightOnlyLine, simulator, StripeAnimation.TYPE.RIGHTONLY);
  545.                                 }
  546.                                 catch (RemoteException exception)
  547.                                 {
  548.                                     exception.printStackTrace();
  549.                                 }
  550.                             }
  551.                             cseList.add(rightOnlyLine);
  552.                             break;

  553.                         case SOLID:
  554.                             Stripe solidLine = new Stripe(csl, cseTag.offset, cseTag.width);
  555.                             if (simulator != null && simulator instanceof AnimatorInterface)
  556.                             {
  557.                                 try
  558.                                 {
  559.                                     new StripeAnimation(solidLine, simulator, StripeAnimation.TYPE.SOLID);
  560.                                 }
  561.                                 catch (RemoteException exception)
  562.                                 {
  563.                                     exception.printStackTrace();
  564.                                 }
  565.                             }
  566.                             cseList.add(solidLine);
  567.                             break;

  568.                         default:
  569.                             throw new SAXException("Unknown Stripe type: " + cseTag.stripeType.toString());
  570.                     }
  571.                     break;

  572.                 case LANE:
  573.                 {
  574.                     LongitudinalDirectionality direction = cseTag.direction;
  575.                     Color color = cseTag.color;
  576.                     OvertakingConditions overtakingConditions = cseTag.overtakingConditions;
  577.                     Speed speed = cseTag.speed;
  578.                     if (laneOverrideTag != null)
  579.                     {
  580.                         if (laneOverrideTag.overtakingConditions != null)
  581.                             overtakingConditions = laneOverrideTag.overtakingConditions;
  582.                         if (laneOverrideTag.color != null)
  583.                             color = laneOverrideTag.color;
  584.                         if (laneOverrideTag.direction != null)
  585.                             direction = laneOverrideTag.direction;
  586.                         if (laneOverrideTag.speed != null)
  587.                             speed = laneOverrideTag.speed;
  588.                     }
  589.                     Map<GTUType, LongitudinalDirectionality> directionality = new LinkedHashMap<>();
  590.                     directionality.put(GTUType.ALL, direction);
  591.                     if (linkDirection.equals(LongitudinalDirectionality.DIR_NONE))
  592.                     {
  593.                         linkDirection = direction;
  594.                     }
  595.                     else if (linkDirection.isForward())
  596.                     {
  597.                         if (direction.isBackwardOrBoth())
  598.                         {
  599.                             linkDirection = LongitudinalDirectionality.DIR_BOTH;
  600.                         }
  601.                     }
  602.                     else if (linkDirection.isBackward())
  603.                     {
  604.                         if (direction.isForwardOrBoth())
  605.                         {
  606.                             linkDirection = LongitudinalDirectionality.DIR_BOTH;
  607.                         }
  608.                     }
  609.                     Map<GTUType, Speed> speedLimit = new LinkedHashMap<>();
  610.                     speedLimit.put(GTUType.ALL, speed);

  611.                    
  612.                     // XXX: Quick hack to solve the error that the lane directionality has not (yet) been registered at the link
  613.                     csl.addDirectionality(GTUType.ALL, linkDirection);

  614.                    
  615.                     Lane lane =
  616.                             new Lane(csl, cseTag.name, cseTag.offset, cseTag.offset, cseTag.width, cseTag.width,
  617.                                     cseTag.laneType, directionality, speedLimit, overtakingConditions);
  618.                     // System.out.println(OTSGeometry.printCoordinates("#link design line: \nc1,0,0\n#",
  619.                     // lane.getParentLink().getDesignLine(), "\n "));
  620.                     // System.out.println(OTSGeometry.printCoordinates("#lane center line: \nc0,1,0\n#", lane.getCenterLine(),
  621.                     // "\n "));
  622.                     // System.out.println(OTSGeometry.printCoordinates("#lane contour: \nc0,0,1\n#", lane.getContour(),
  623.                     // "\n "));
  624.                     cseList.add(lane);
  625.                     lanes.add(lane);
  626.                     linkTag.lanes.put(cseTag.name, lane);
  627.                     if (simulator != null && simulator instanceof AnimatorInterface)
  628.                     {
  629.                         try
  630.                         {
  631.                             new LaneAnimation(lane, simulator, color, false);
  632.                         }
  633.                         catch (RemoteException exception)
  634.                         {
  635.                             exception.printStackTrace();
  636.                         }
  637.                     }

  638.                     // SINK
  639.                     if (linkTag.sinkTags.keySet().contains(cseTag.name))
  640.                     {
  641.                         SinkTag sinkTag = linkTag.sinkTags.get(cseTag.name);
  642.                         Length position = LinkTag.parseBeginEndPosition(sinkTag.positionStr, lane);
  643.                         Sensor sensor = new SinkSensor(lane, position, simulator);
  644.                         lane.addSensor(sensor, GTUType.ALL);
  645.                     }

  646.                     // BLOCK
  647.                     if (linkTag.blockTags.containsKey(cseTag.name))
  648.                     {
  649.                         BlockTag blockTag = linkTag.blockTags.get(cseTag.name);
  650.                         Length position = LinkTag.parseBeginEndPosition(blockTag.positionStr, lane);
  651.                         new LaneBlock(lane, position, simulator, null, parser.network);
  652.                     }

  653.                     // TRAFFICLIGHT
  654.                     if (linkTag.trafficLightTags.containsKey(cseTag.name))
  655.                     {
  656.                         for (TrafficLightTag trafficLightTag : linkTag.trafficLightTags.get(cseTag.name))
  657.                         {
  658.                             try
  659.                             {
  660.                                 Class<?> clazz = Class.forName(trafficLightTag.className);
  661.                                 Constructor<?> trafficLightConstructor =
  662.                                         ClassUtil.resolveConstructor(clazz, new Class[] { String.class, Lane.class,
  663.                                                 Length.class, OTSDEVSSimulatorInterface.class, OTSNetwork.class });
  664.                                 Length position = LinkTag.parseBeginEndPosition(trafficLightTag.positionStr, lane);
  665.                                 AbstractTrafficLight trafficLight =
  666.                                         (AbstractTrafficLight) trafficLightConstructor.newInstance(new Object[] {
  667.                                                 trafficLightTag.name, lane, position, simulator, parser.network });
  668.                             }
  669.                             catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
  670.                                     | IllegalAccessException | IllegalArgumentException | InvocationTargetException
  671.                                     | NetworkException exception)
  672.                             {
  673.                                 throw new NetworkException("TRAFFICLIGHT: CLASS NAME " + trafficLightTag.className
  674.                                         + " for traffic light " + trafficLightTag.name + " on lane " + lane.toString()
  675.                                         + " -- class not found or constructor not right", exception);
  676.                             }
  677.                         }
  678.                     }

  679.                     // GENERATOR
  680.                     if (linkTag.generatorTags.containsKey(cseTag.name))
  681.                     {
  682.                         GeneratorTag generatorTag = linkTag.generatorTags.get(cseTag.name);
  683.                         GeneratorTag.makeGenerator(generatorTag, parser, linkTag, simulator);
  684.                     }

  685.                     // TODO LISTGENERATOR

  686.                     // SENSOR
  687.                     if (linkTag.sensorTags.containsKey(cseTag.name))
  688.                     {
  689.                         for (SensorTag sensorTag : linkTag.sensorTags.get(cseTag.name))
  690.                         {
  691.                             try
  692.                             {
  693.                                 Class<?> clazz = Class.forName(sensorTag.className);
  694.                                 Constructor<?> sensorConstructor =
  695.                                         ClassUtil.resolveConstructor(clazz, new Class[] { Lane.class, Length.class,
  696.                                                 RelativePosition.TYPE.class, String.class, OTSDEVSSimulatorInterface.class });
  697.                                 Length position = LinkTag.parseBeginEndPosition(sensorTag.positionStr, lane);
  698.                                 AbstractSensor sensor =
  699.                                         (AbstractSensor) sensorConstructor.newInstance(new Object[] { lane, position,
  700.                                                 sensorTag.triggerPosition, sensorTag.name, simulator });
  701.                                 lane.addSensor(sensor, GTUType.ALL);
  702.                             }
  703.                             catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
  704.                                     | IllegalAccessException | IllegalArgumentException | InvocationTargetException
  705.                                     | NetworkException exception)
  706.                             {
  707.                                 throw new NetworkException("SENSOR: CLASS NAME " + sensorTag.className + " for sensor "
  708.                                         + sensorTag.name + " on lane " + lane.toString()
  709.                                         + " -- class not found or constructor not right", exception);
  710.                             }
  711.                         }
  712.                     }

  713.                     // FILL
  714.                     if (linkTag.fillTags.containsKey(cseTag.name))
  715.                     {
  716.                         FillTag fillTag = linkTag.fillTags.get(cseTag.name);
  717.                         FillTag.makeFill(fillTag, parser, linkTag, simulator);
  718.                     }
  719.                     break;
  720.                 }

  721.                 case NOTRAFFICLANE:
  722.                 {
  723.                     Lane lane = new NoTrafficLane(csl, cseTag.name, cseTag.offset, cseTag.offset, cseTag.width, cseTag.width);
  724.                     cseList.add(lane);
  725.                     if (simulator != null && simulator instanceof AnimatorInterface)
  726.                     {
  727.                         try
  728.                         {
  729.                             Color color = cseTag.color;
  730.                             if (laneOverrideTag != null)
  731.                             {
  732.                                 if (laneOverrideTag.color != null)
  733.                                     color = laneOverrideTag.color;
  734.                             }
  735.                             new LaneAnimation(lane, simulator, color, false);
  736.                         }
  737.                         catch (RemoteException exception)
  738.                         {
  739.                             exception.printStackTrace();
  740.                         }
  741.                     }
  742.                     break;
  743.                 }

  744.                 case SHOULDER:
  745.                 {
  746.                     Shoulder shoulder = new Shoulder(csl, cseTag.name, cseTag.offset, cseTag.width);
  747.                     cseList.add(shoulder);
  748.                     if (simulator != null && simulator instanceof AnimatorInterface)
  749.                     {
  750.                         try
  751.                         {
  752.                             Color color = cseTag.color;
  753.                             if (laneOverrideTag != null)
  754.                             {
  755.                                 if (laneOverrideTag.color != null)
  756.                                     color = laneOverrideTag.color;
  757.                             }
  758.                             new ShoulderAnimation(shoulder, simulator, color);
  759.                         }
  760.                         catch (RemoteException exception)
  761.                         {
  762.                             exception.printStackTrace();
  763.                         }
  764.                     }
  765.                     break;
  766.                 }

  767.                 default:
  768.                     throw new SAXException("Unknown Element type: " + cseTag.elementType.toString());
  769.             }

  770.         } // for (CrossSectionElementTag cseTag : roadTypeTag.cseTags.values())

  771.         // add the calculated direction to the link
  772.         csl.addDirectionality(GTUType.ALL, linkDirection);
  773.     }
  774. }