View Javadoc
1   package org.opentrafficsim.road.network.factory.vissim;
2   
3   import java.awt.Color;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import javax.naming.NamingException;
8   
9   import org.djunits.unit.LengthUnit;
10  import org.djunits.unit.SpeedUnit;
11  import org.djunits.value.vdouble.scalar.Length;
12  import org.djunits.value.vdouble.scalar.Speed;
13  import org.opentrafficsim.core.compatibility.Compatible;
14  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
15  import org.opentrafficsim.core.geometry.Bezier;
16  import org.opentrafficsim.core.geometry.OTSGeometryException;
17  import org.opentrafficsim.core.geometry.OTSLine3D;
18  import org.opentrafficsim.core.geometry.OTSPoint3D;
19  import org.opentrafficsim.core.gtu.GTUException;
20  import org.opentrafficsim.core.gtu.RelativePosition;
21  import org.opentrafficsim.core.network.LateralDirectionality;
22  import org.opentrafficsim.core.network.LinkType;
23  import org.opentrafficsim.core.network.LongitudinalDirectionality;
24  import org.opentrafficsim.core.network.NetworkException;
25  import org.opentrafficsim.road.network.factory.vissim.ArcTag.ArcDirection;
26  import org.opentrafficsim.road.network.lane.CrossSectionElement;
27  import org.opentrafficsim.road.network.lane.CrossSectionLink;
28  import org.opentrafficsim.road.network.lane.Lane;
29  import org.opentrafficsim.road.network.lane.LaneType;
30  import org.opentrafficsim.road.network.lane.object.sensor.SimpleReportingSensor;
31  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
32  import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
33  import org.xml.sax.SAXException;
34  
35  import nl.tudelft.simulation.dsol.SimRuntimeException;
36  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
37  import nl.tudelft.simulation.language.d3.DirectedPoint;
38  
39  /**
40   * <p>
41   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
42   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
43   * <p>
44   * LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 5192 $, by $Author: averbraeck $,
45   * initial version Jul 25, 2015 <br>
46   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
47   */
48  final class Links
49  {
50      /** Utility class. */
51      private Links()
52      {
53          // do not instantiate
54      }
55  
56      /**
57       * Find the nodes one by one that have one coordinate defined, and one not defined, and try to build the network from there.
58       * @param linkTag LinkTag; the link to process
59       * @param parser VissimNetworkLaneParser; the parser with the lists of information
60       * @param simulator OTSSimulatorInterface; to be able to make the animation
61       * @throws OTSGeometryException when both nodes are null.
62       * @throws NamingException when node animation cannot link to the animation context.
63       * @throws NetworkException when tag type not filled
64       */
65      static void buildLink(final LinkTag linkTag, final VissimNetworkLaneParser parser, final OTSSimulatorInterface simulator)
66              throws OTSGeometryException, NamingException, NetworkException
67      {
68          NodeTag from = linkTag.nodeStartTag;
69          OTSPoint3D startPoint = new OTSPoint3D(from.coordinate);
70          double startAngle = 0.0;
71          if (linkTag.offsetStart != null && linkTag.offsetStart.si != 0.0)
72          {
73              // shift the start point perpendicular to the node direction or read from tag
74              double offset = linkTag.offsetStart.si;
75              startPoint = new OTSPoint3D(startPoint.x + offset * Math.cos(startAngle + Math.PI / 2.0),
76                      startPoint.y + offset * Math.sin(startAngle + Math.PI / 2.0), startPoint.z);
77              System.out
78                      .println("fc = " + from.coordinate + ", sa = " + startAngle + ", so = " + offset + ", sp = " + startPoint);
79          }
80  
81          NodeTag to = linkTag.nodeEndTag;
82          OTSPoint3D endPoint = new OTSPoint3D(to.coordinate);
83          double endAngle = 0.0;
84          if (linkTag.offsetEnd != null && linkTag.offsetEnd.si != 0.0)
85          {
86              // shift the start point perpendicular to the node direction or read from tag
87              double offset = linkTag.offsetEnd.si;
88              endPoint = new OTSPoint3D(endPoint.x + offset * Math.cos(endAngle + Math.PI / 2.0),
89                      endPoint.y + offset * Math.sin(endAngle + Math.PI / 2.0), endPoint.z);
90              System.out.println("tc = " + to.coordinate + ", ea = " + endAngle + ", eo = " + offset + ", ep = " + endPoint);
91          }
92  
93          OTSPoint3D[] coordinates = null;
94  
95          if (linkTag.straightTag != null)
96          {
97              coordinates = new OTSPoint3D[2];
98              coordinates[0] = startPoint;
99              coordinates[1] = endPoint;
100         }
101 
102         else if (linkTag.polyLineTag != null)
103         {
104             int intermediatePoints = linkTag.polyLineTag.vertices.length;
105             coordinates = new OTSPoint3D[intermediatePoints + 2];
106             coordinates[0] = startPoint;
107             coordinates[intermediatePoints + 1] = endPoint;
108             for (int p = 0; p < intermediatePoints; p++)
109             {
110                 coordinates[p + 1] = linkTag.polyLineTag.vertices[p];
111             }
112 
113         }
114         else if (linkTag.arcTag != null)
115         {
116             // TODO move the radius if there is an start and end offset? How?
117             int points = (Math.abs(linkTag.arcTag.angle.getInUnit()) <= Math.PI / 2.0) ? 64 : 128;
118             coordinates = new OTSPoint3D[points];
119             coordinates[0] = new OTSPoint3D(from.coordinate.x, from.coordinate.y, from.coordinate.z);
120             coordinates[coordinates.length - 1] = new OTSPoint3D(to.coordinate.x, to.coordinate.y, to.coordinate.z);
121             double angleStep = linkTag.arcTag.angle.getInUnit() / points;
122             double slopeStep = (to.coordinate.z - from.coordinate.z) / points;
123             double radiusSI = linkTag.arcTag.radius.getSI();
124             if (linkTag.arcTag.direction.equals(ArcDirection.RIGHT))
125             {
126                 for (int p = 1; p < points - 1; p++)
127                 {
128                     coordinates[p] = new OTSPoint3D(
129                             linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle - angleStep * p),
130                             linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle - angleStep * p),
131                             from.coordinate.z + slopeStep * p);
132                 }
133             }
134             else
135             {
136                 for (int p = 1; p < points - 1; p++)
137                 {
138                     try
139                     {
140                         System.err.println("linkTag.arcTag.center = " + linkTag.arcTag.center);
141                         System.err.println("linkTag.arcTag.startAngle = " + linkTag.arcTag.startAngle);
142                         coordinates[p] = new OTSPoint3D(
143                                 linkTag.arcTag.center.x + radiusSI * Math.cos(linkTag.arcTag.startAngle + angleStep * p),
144                                 linkTag.arcTag.center.y + radiusSI * Math.sin(linkTag.arcTag.startAngle + angleStep * p),
145                                 from.coordinate.z + slopeStep * p);
146                     }
147                     catch (NullPointerException npe)
148                     {
149                         npe.printStackTrace();
150                         System.err.println(npe.getMessage());
151                     }
152                 }
153             }
154         }
155 
156         else if (linkTag.bezierTag != null)
157         {
158             if (new DirectedPoint(startPoint.x, startPoint.y, startPoint.z, 0, 0, startAngle)
159                     .equals(new DirectedPoint(endPoint.x, endPoint.y, endPoint.z, 0, 0, endAngle)))
160             {
161                 System.out.println("   ");
162             }
163 
164             coordinates = Bezier.cubic(128, new DirectedPoint(startPoint.x, startPoint.y, startPoint.z, 0, 0, startAngle),
165                     new DirectedPoint(endPoint.x, endPoint.y, endPoint.z, 0, 0, endAngle)).getPoints();
166         }
167 
168         else
169         {
170             throw new NetworkException(
171                     "Making link, but link " + linkTag.name + " has no filled straight, arc, or bezier curve");
172         }
173         if (coordinates.length < 2)
174         {
175             throw new OTSGeometryException(
176                     "Degenerate OTSLine3D; has " + coordinates.length + " point" + (coordinates.length != 1 ? "s" : ""));
177         }
178         OTSLine3D designLine = OTSLine3D.createAndCleanOTSLine3D(coordinates);
179 
180         // Directionality has to be added later when the lanes and their direction are known.
181         CrossSectionLink link =
182                 new CrossSectionLink(parser.getNetwork(), linkTag.name, linkTag.nodeStartTag.node, linkTag.nodeEndTag.node,
183                         parser.network.getLinkType(LinkType.DEFAULTS.ROAD), designLine, simulator, linkTag.laneKeepingPolicy);
184         linkTag.link = link;
185     }
186 
187     /**
188      * @param linkTag LinkTag; the link to process
189      * @param parser VissimNetworkLaneParser; the parser with the lists of information
190      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; to be able to make the animation
191      * @throws NetworkException when the stripe cannot be instantiated
192      * @throws NamingException when the /animation/2D tree cannot be found in the context
193      * @throws SAXException when the stripe type cannot be parsed correctly
194      * @throws GTUException when lane block cannot be created
195      * @throws OTSGeometryException when construction of the offset-line or contour fails
196      * @throws SimRuntimeException when construction of the generator fails
197      */
198     @SuppressWarnings({"checkstyle:needbraces", "checkstyle:methodlength"})
199     static void applyRoadTypeToLink(final LinkTag linkTag, final VissimNetworkLaneParser parser,
200             final DEVSSimulatorInterface.TimeDoubleUnit simulator)
201             throws NetworkException, NamingException, SAXException, GTUException, OTSGeometryException, SimRuntimeException
202     {
203         CrossSectionLink csl = linkTag.link;
204 
205         List<CrossSectionElement> cseList = new ArrayList<>();
206         List<Lane> lanes = new ArrayList<>();
207         // TODO Map<GTUType, LongitudinalDirectionality> linkDirections = new HashMap<>();
208         LongitudinalDirectionality linkDirection = LongitudinalDirectionality.DIR_NONE;
209 
210         // add information from the lanes
211         // first the total width is computed
212         // The offset of the lanes will be computed to match this Vissim layout
213         Double roadWidth = 0.0;
214         for (LaneTag laneTag : linkTag.laneTags.values())
215         {
216             roadWidth += Double.parseDouble(laneTag.width);
217         }
218 
219         Color color;
220         if (linkTag.connector)
221         {
222             color = Color.LIGHT_GRAY;
223         }
224         else
225         {
226             color = Color.DARK_GRAY;
227 
228         }
229 
230         // The lanes are ordered from the outside to the inner side of the road
231         Double totalLaneWidth = 0.0;
232 
233         if (!linkTag.connector)
234         {
235             for (LaneTag laneTag : linkTag.laneTags.values())
236             {
237                 String name = laneTag.laneNo;
238                 Double laneWidth = Double.parseDouble(laneTag.width);
239                 Length thisLaneWidth = new Length(laneWidth, LengthUnit.METER);
240 
241                 // the road offset is negative if the lanes are at the right side of the median (for right hand rule)
242                 // Vissim puts the lanes around the centerline of the road (all lanes joined)
243                 // therefore we use a factor 0.5 * roadWidth....
244                 Double negativeOffset = -(0.5 * roadWidth - totalLaneWidth - laneWidth / 2);
245                 Length lateralOffset = new Length(negativeOffset, LengthUnit.METER);
246 
247                 LaneType laneType = parser.network.getLaneType(LaneType.DEFAULTS.FREEWAY);
248                 Speed speedLimit = new Speed(Double.parseDouble(linkTag.legalSpeed), SpeedUnit.KM_PER_HOUR);
249                 // OvertakingConditions overtakingConditions; TODO (not clear yet)
250                 Lane lane = new Lane(csl, name, lateralOffset, thisLaneWidth, laneType, speedLimit);
251                 if (!linkTag.sensors.isEmpty())
252                 {
253                     for (SensorTag sensorTag : linkTag.sensors)
254                     {
255                         if (sensorTag.laneName.equals(laneTag.laneNo))
256                         {
257                             Length pos = new Length(Double.parseDouble(sensorTag.positionStr), LengthUnit.METER);
258                             if (pos.lt(lane.getLength()))
259                             {
260                                 try
261                                 {
262                                     new SimpleReportingSensor(sensorTag.laneName + ".S" + sensorTag.name, lane, pos,
263                                             RelativePosition.FRONT, simulator, Compatible.EVERYTHING);
264                                 }
265                                 catch (Exception exception)
266                                 {
267                                     System.err.println(exception.getMessage());
268                                 }
269                             }
270                         }
271                     }
272                 }
273 
274                 if (!linkTag.signalHeads.isEmpty())
275                 {
276                     for (SignalHeadTag signalHeadTag : linkTag.signalHeads)
277                     {
278                         if (signalHeadTag.laneName.equals(laneTag.laneNo))
279                         {
280                             Length pos = new Length(Double.parseDouble(signalHeadTag.positionStr), LengthUnit.METER);
281                             if (pos.lt(lane.getLength()))
282                             {
283                                 new SimpleTrafficLight(signalHeadTag.no, lane, pos, simulator);
284                             }
285                         }
286                     }
287                 }
288 
289                 cseList.add(lane);
290 
291                 lanes.add(lane);
292                 linkTag.lanes.put(name, lane);
293                 // update totalLaneWidth
294                 totalLaneWidth += Double.parseDouble(laneTag.width);
295 
296                 // TODO: parser.network.addDrawingInfoBase(lane, new DrawingInfoShape<Lane>(color));
297             }
298         }
299 
300     }
301 
302     /**
303      * @param linkTag LinkTag; the link to process
304      * @param parser VissimNetworkLaneParser; the parser with the lists of information
305      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; to be able to make the animation
306      * @throws NetworkException when the stripe cannot be instantiated
307      * @throws NamingException when the /animation/2D tree cannot be found in the context
308      * @throws SAXException when the stripe type cannot be parsed correctly
309      * @throws GTUException when lane block cannot be created
310      * @throws OTSGeometryException when construction of the offset-line or contour fails
311      * @throws SimRuntimeException when construction of the generator fails
312      */
313     @SuppressWarnings({"checkstyle:needbraces", "checkstyle:methodlength"})
314     static void applyRoadTypeToConnector(final LinkTag linkTag, final VissimNetworkLaneParser parser,
315             final DEVSSimulatorInterface.TimeDoubleUnit simulator)
316             throws NetworkException, NamingException, SAXException, GTUException, OTSGeometryException, SimRuntimeException
317     {
318         CrossSectionLink csl = linkTag.link;
319 
320         List<CrossSectionElement> cseList = new ArrayList<>();
321         List<Lane> lanes = new ArrayList<>();
322 
323         // add information from the lanes
324         // first the total width is computed
325         // The offset of the lanes will be computed to match this Vissim layout
326         Double roadWidth = 0.0;
327         for (LaneTag laneTag : linkTag.laneTags.values())
328         {
329             roadWidth += Double.parseDouble(laneTag.width);
330         }
331 
332         Color color;
333         if (linkTag.connector)
334         {
335             color = Color.LIGHT_GRAY;
336         }
337         else
338         {
339             color = Color.DARK_GRAY;
340 
341         }
342 
343         // some generic definitions necessary to create a Lane object
344         LaneType laneType = parser.network.getLaneType(LaneType.DEFAULTS.FREEWAY);
345         Speed speedLimit = new Speed(Double.parseDouble(linkTag.legalSpeed), SpeedUnit.KM_PER_HOUR);
346 
347         // The lanes are ordered from the outside to the inner side of the road
348         // only lanes from connectors are being set
349         if (linkTag.connector)
350         {
351             Double totalFromLaneWidth = -1.75;
352             Double totalToLaneWidth = -1.75;
353             // find the link and lane downstream
354             LinkTag linkToTag = parser.getRealLinkTags().get(linkTag.connectorTag.toLinkNo);
355             Lane laneTo = linkToTag.lanes.get(linkTag.connectorTag.toLaneNo);
356 
357             // find the link and lane upstream
358             LinkTag linkFromTag = parser.getRealLinkTags().get(linkTag.connectorTag.fromLinkNo);
359             Lane laneFrom = linkFromTag.lanes.get(linkTag.connectorTag.fromLaneNo);
360 
361             // loop along all lanes (Tags)
362             for (LaneTag connectLaneTag : linkTag.laneTags.values())
363             {
364                 // the name (number) of the downstream lane (the order is from outer to center)
365                 String name = connectLaneTag.laneNo;
366                 // the width of the current lane
367                 Length thisLaneWidth = new Length(Double.parseDouble(connectLaneTag.width), LengthUnit.METER);
368                 // the total lanewidth of all previous lanes that already have been looped through
369                 Length totfromLaneWidth = new Length(totalFromLaneWidth, LengthUnit.METER);
370                 Length totToLaneWidth = new Length(totalToLaneWidth, LengthUnit.METER);
371 
372                 Length lateralOffsetStart;
373                 Length lateralOffsetEnd =
374                         laneTo.getLateralBoundaryPosition(LateralDirectionality.RIGHT, 0).plus(totToLaneWidth);
375                 // the lateral offset
376                 if (laneFrom != null)
377                 {
378                     lateralOffsetStart =
379                             laneFrom.getLateralBoundaryPosition(LateralDirectionality.RIGHT, 0).plus(totfromLaneWidth);
380                 }
381                 else
382                 {
383                     lateralOffsetStart = lateralOffsetEnd;
384                 }
385 
386                 // the road offset is negative if the lanes are at the right side of the median (for right hand rule)
387                 // OvertakingConditions overtakingConditions; TODO (not clear yet)
388                 Lane lane = new Lane(csl, name, lateralOffsetStart, lateralOffsetEnd, thisLaneWidth, thisLaneWidth, laneType,
389                         speedLimit);
390                 cseList.add(lane);
391                 lanes.add(lane);
392                 linkTag.lanes.put(name, lane);
393 
394                 // update totalLaneWidth
395                 totalFromLaneWidth += Double.parseDouble(connectLaneTag.width);
396                 totalToLaneWidth += Double.parseDouble(connectLaneTag.width);
397 
398                 // TODO: parser.network.addDrawingInfoBase(lane, new DrawingInfoShape<Lane>(color));
399 
400             }
401         }
402 
403     }
404 
405     /**
406      * @param realLinkTag LinkTag;
407      * @param vissimNetworkLaneParser VissimNetworkLaneParser;
408      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit;
409      * @throws NetworkException
410      */
411     public static void createSinkSensor(LinkTag realLinkTag, VissimNetworkLaneParser vissimNetworkLaneParser,
412             DEVSSimulatorInterface.TimeDoubleUnit simulator) throws NetworkException
413     {
414         if (!realLinkTag.connector)
415         {
416             boolean deadLink = true;
417             for (LinkTag linkTag : vissimNetworkLaneParser.getLinkTags().values())
418             {
419                 if (realLinkTag.nodeEndTag.name.equals(linkTag.nodeStartTag.name) && !realLinkTag.equals(linkTag))
420                 {
421                     deadLink = false;
422                 }
423             }
424 
425             if (deadLink)
426             {
427                 for (Lane lane : realLinkTag.lanes.values())
428                 {
429                     Double smallest = Math.min(10, lane.getLength().getInUnit(LengthUnit.METER) - 1);
430                     Length beforeEnd = new Length(smallest, LengthUnit.METER);
431                     Length pos = lane.getLength().minus(beforeEnd);
432                     SinkSensor sensor = new SinkSensor(lane, pos, simulator);
433                     lane.getSensors().add(sensor);
434                 }
435             }
436         }
437 
438     }
439 
440 }