View Javadoc
1   package org.opentrafficsim.road.network.factory.vissim;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.Iterator;
7   import java.util.List;
8   import java.util.Map;
9   
10  import javax.naming.NamingException;
11  
12  import org.djunits.unit.LengthUnit;
13  import org.djunits.value.vdouble.scalar.Angle;
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.locationtech.jts.geom.Coordinate;
16  import org.locationtech.jts.geom.LineString;
17  import org.opentrafficsim.core.geometry.OTSGeometryException;
18  import org.opentrafficsim.core.geometry.OTSLine3D;
19  import org.opentrafficsim.core.geometry.OTSPoint3D;
20  import org.opentrafficsim.core.network.NetworkException;
21  import org.opentrafficsim.core.network.factory.xml.units.Coordinates;
22  import org.opentrafficsim.core.network.factory.xml.units.LengthUnits;
23  import org.opentrafficsim.road.network.lane.CrossSectionElement;
24  import org.opentrafficsim.road.network.lane.CrossSectionLink;
25  import org.opentrafficsim.road.network.lane.Lane;
26  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
27  import org.w3c.dom.NamedNodeMap;
28  import org.w3c.dom.Node;
29  import org.w3c.dom.NodeList;
30  import org.xml.sax.SAXException;
31  
32  /**
33   * <p>
34   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
35   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
36   * <p>
37   * $LastChangedDate: 2019-02-27 07:19:36 +0100 (Wed, 27 Feb 2019) $, @version $Revision: 5014 $, by $Author: averbraeck $,
38   * initial version Jul 23, 2015 <br>
39   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
40   */
41  final class LinkTag implements Serializable
42  {
43      /** */
44      private static final long serialVersionUID = 20150723L;
45  
46      /** Name. */
47      @SuppressWarnings("checkstyle:visibilitymodifier")
48      String name = null;
49  
50      /** Name. */
51      @SuppressWarnings("checkstyle:visibilitymodifier")
52      String legalSpeed = null;
53  
54      /** From node tag. */
55      @SuppressWarnings("checkstyle:visibilitymodifier")
56      NodeTag nodeStartTag = null;
57  
58      /** To node tag. */
59      @SuppressWarnings("checkstyle:visibilitymodifier")
60      NodeTag nodeEndTag = null;
61  
62      /** Offset for the link at the start node. */
63      @SuppressWarnings("checkstyle:visibilitymodifier")
64      Length offsetStart = null;
65  
66      /** Offset for the link at the end node. */
67      @SuppressWarnings("checkstyle:visibilitymodifier")
68      Length offsetEnd = null;
69  
70      /** Extra rotation for the link at the start node. */
71      @SuppressWarnings("checkstyle:visibilitymodifier")
72      Angle rotationStart = null;
73  
74      /** Extra rotation for the link at the end node. */
75      @SuppressWarnings("checkstyle:visibilitymodifier")
76      Angle rotationEnd = null;
77  
78      /** Straight. */
79      @SuppressWarnings("checkstyle:visibilitymodifier")
80      StraightTag straightTag = null;
81  
82      //
83      /** PolyLine. */
84      @SuppressWarnings("checkstyle:visibilitymodifier")
85      PolyLineTag polyLineTag = null;
86  
87      /** Map of lane name to lane override. */
88      @SuppressWarnings("checkstyle:visibilitymodifier")
89      Map<String, LaneOverrideTag> laneOverrideTags = new HashMap<>();
90  
91      /** Map of lane name to generators. */
92      @SuppressWarnings("checkstyle:visibilitymodifier")
93      Map<String, GeneratorTag> generatorTags = new HashMap<>();
94  
95      /** Map of lane name to list generators. */
96      @SuppressWarnings("checkstyle:visibilitymodifier")
97      Map<String, ListGeneratorTag> listGeneratorTags = new HashMap<>();
98  
99      /** Map of lane name to list of sensors. */
100     @SuppressWarnings("checkstyle:visibilitymodifier")
101     Map<String, List<SensorTag>> sensorTags = new HashMap<>();
102 
103     /** Map of lane name to fill at t=0. */
104     @SuppressWarnings("checkstyle:visibilitymodifier")
105     Map<String, FillTag> fillTags = new HashMap<>();
106 
107     /** Map of lane name to sink tags. */
108     @SuppressWarnings("checkstyle:visibilitymodifier")
109     Map<String, SinkTag> sinkTags = new HashMap<>();
110 
111     /** Map of lane name to generated lanes. */
112     @SuppressWarnings("checkstyle:visibilitymodifier")
113     Map<String, Lane> lanes = new HashMap<>();
114 
115     /** Map of lane name to generated lanes. */
116     @SuppressWarnings("checkstyle:visibilitymodifier")
117     Map<String, LaneTag> laneTags = new HashMap<>();
118 
119     ConnectorTag connectorTag = null;
120 
121     /** The calculated Link. */
122     @SuppressWarnings("checkstyle:visibilitymodifier")
123     CrossSectionLink link = null;
124 
125     /** The lane keeping policy, i.e., keep left, keep right or keep lane. */
126     @SuppressWarnings("checkstyle:visibilitymodifier")
127     LaneKeepingPolicy laneKeepingPolicy = null;
128 
129     List<SignalHeadTag> signalHeads = new ArrayList<>();
130 
131     List<SensorTag> sensors = new ArrayList<>();
132 
133     List<SignalHeadTag> signalHeadsToRemove = new ArrayList<>();
134 
135     List<SensorTag> sensorTagsToRemove = new ArrayList<>();
136 
137     // a link (false) or a connector (true)
138     boolean connector = false;
139 
140     ArcTag arcTag;
141 
142     BezierTag bezierTag;
143 
144     public RoadLayoutTag roadLayoutTag;
145 
146     /**
147      * @param linkTag LinkTag; link Info from XML
148      */
149     public LinkTag(LinkTag linkTag)
150     {
151         this.connectorTag = linkTag.connectorTag;
152         this.connector = linkTag.connector;
153         this.laneKeepingPolicy = linkTag.laneKeepingPolicy;
154         this.lanes.putAll(linkTag.lanes);
155         this.laneTags.putAll(linkTag.laneTags);
156         this.legalSpeed = linkTag.legalSpeed;
157         if (linkTag.straightTag != null)
158         {
159             this.straightTag = new StraightTag(linkTag.straightTag);
160         }
161         if (linkTag.polyLineTag != null)
162         {
163             this.polyLineTag = new PolyLineTag(linkTag.polyLineTag);
164         }
165         this.nodeEndTag = linkTag.nodeEndTag;
166         this.nodeStartTag = linkTag.nodeStartTag;
167     }
168 
169     /**
170      *
171      */
172     public LinkTag()
173     {
174     }
175 
176     /**
177      * Parse the LINK tags from Vissim fiels (.inpx) .
178      * @param nodeList NodeList; nodeList the top-level nodes of the XML-file
179      * @param parser VissimNetworkLaneParser; the parser with the lists of information
180      * @throws SAXException when parsing of the tag fails
181      * @throws NetworkException when parsing of the tag fails
182      */
183     @SuppressWarnings("checkstyle:needbraces")
184     static void parseLinks(final NodeList nodeList, final VissimNetworkLaneParser parser) throws SAXException, NetworkException
185     {
186 
187         for (Node linksNode : XMLParser.getNodes(nodeList, "links"))
188         {
189 
190             for (Node node : XMLParser.getNodes(linksNode.getChildNodes(), "link"))
191             {
192                 NamedNodeMap attributes = node.getAttributes();
193 
194                 // make a linkTag with the link attributes
195                 LinkTag linkTag = new LinkTag();
196 
197                 // Lane keepingĀ± currently with this default behaviour
198                 // TODO: differentiate by road type
199                 linkTag.laneKeepingPolicy = LaneKeepingPolicy.KEEPLANE;
200 
201                 if (attributes.getNamedItem("no") == null)
202                 {
203                     throw new SAXException("LINK: missing attribute: no");
204                 }
205                 linkTag.name = attributes.getNamedItem("no").getNodeValue().trim();
206                 Integer linkNr = Integer.parseInt(linkTag.name);
207                 if (linkNr > parser.getUpperLinkNr())
208                 {
209                     parser.setUpperLinkNr(linkNr);
210                 }
211 
212                 if (attributes.getNamedItem("assumSpeedOncom") == null)
213                 {
214                     throw new SAXException("LINK: missing attribute assumSpeedOncom");
215                 }
216                 linkTag.legalSpeed = attributes.getNamedItem("assumSpeedOncom").getNodeValue().trim();
217 
218                 // parse the geometry (coordinates of the link (nodes/vertices)) and add them to the LinkTag
219                 OTSPoint3D[] nodeCoords = parseLinkGeometry(parser, node, linkTag);
220 
221                 // create a pair of nodes for every link. They will later be corrected (remove duplicate nodes)
222                 createNodesForLink(parser, linkTag, nodeCoords);
223 
224                 // Parse the lanes. They are part of the link description in Vissim files
225                 List<Node> laneNodes = XMLParser.getNodes(node.getChildNodes(), "lanes");
226 
227                 // the lanes are added as an attribute of the LinkTag
228                 createLanes(linkTag, laneNodes);
229 
230                 // additional info from connectors
231                 List<Node> connectorFromNode = XMLParser.getNodes(node.getChildNodes(), "fromLinkEndPt");
232                 if (connectorFromNode.size() > 0)
233                 {
234                     linkTag.connector = true;
235                     createConnectorInfoFrom(linkTag, connectorFromNode);
236                 }
237 
238                 List<Node> connectorToNode = XMLParser.getNodes(node.getChildNodes(), "toLinkEndPt");
239                 if (connectorFromNode.size() > 0)
240                 {
241                     linkTag.connector = true;
242                     createConnectorInfoTo(linkTag, connectorToNode);
243                 }
244 
245                 // put this link in the map with LinkTags
246                 parser.getLinkTags().put(linkTag.name, linkTag);
247             }
248 
249         }
250 
251     }
252 
253     private static void createConnectorInfoTo(LinkTag linkTag, List<Node> connectorToNode) throws SAXException
254     {
255         NamedNodeMap attributes;
256         if (linkTag.connectorTag == null)
257         {
258             linkTag.connectorTag = new ConnectorTag();
259         }
260         attributes = connectorToNode.get(0).getAttributes();
261         if (attributes.getNamedItem("lane") == null)
262         {
263             throw new SAXException("Connector: missing attribute: link/lane info");
264         }
265         String connect = attributes.getNamedItem("lane").getNodeValue().trim();
266         String[] connectInfo = connect.split("\\s+");
267         linkTag.connectorTag.toLinkNo = connectInfo[0];
268         linkTag.connectorTag.toLaneNo = connectInfo[1];
269         if (attributes.getNamedItem("pos") == null)
270         {
271             throw new SAXException("Connector: missing attribute: pos (position info)");
272         }
273         linkTag.connectorTag.toPositionStr = attributes.getNamedItem("pos").getNodeValue().trim();
274     }
275 
276     private static void createConnectorInfoFrom(LinkTag linkTag, List<Node> connectorFromNode) throws SAXException
277     {
278         NamedNodeMap attributes;
279         if (linkTag.connectorTag == null)
280         {
281             linkTag.connectorTag = new ConnectorTag();
282         }
283         attributes = connectorFromNode.get(0).getAttributes();
284         if (attributes.getNamedItem("lane") == null)
285         {
286             throw new SAXException("Connector: missing attribute: link/lane info");
287         }
288         String connect = attributes.getNamedItem("lane").getNodeValue().trim();
289         String[] connectInfo = connect.split("\\s+");
290         linkTag.connectorTag.fromLinkNo = connectInfo[0];
291         linkTag.connectorTag.fromLaneNo = connectInfo[1];
292 
293         if (attributes.getNamedItem("pos") == null)
294         {
295             throw new SAXException("Connector: missing attribute: pos (position info)");
296         }
297         linkTag.connectorTag.fromPositionStr = attributes.getNamedItem("pos").getNodeValue().trim();
298     }
299 
300     private static void createLanes(LinkTag linkTag, List<Node> laneNodes)
301     {
302         NamedNodeMap attributes;
303         int laneNo = 1;
304         for (Node laneNode : XMLParser.getNodes(laneNodes.get(0).getChildNodes(), "lane"))
305         {
306 
307             attributes = laneNode.getAttributes();
308             LaneTag laneTag = new LaneTag();
309             if (attributes.getLength() > 0)
310             {
311                 laneTag.width = attributes.getNamedItem("width").getNodeValue().trim();
312             }
313             else
314             {
315                 laneTag.width = "3.5";
316                 // must be a connector without lane attributes
317                 // the lane width is determined by its predecessor and successor
318             }
319             laneTag.linkNo = linkTag.name;
320             laneTag.laneNo = "" + laneNo;
321             laneNo++;
322             linkTag.laneTags.put(laneTag.laneNo, laneTag);
323         }
324     }
325 
326     private static void createNodesForLink(final VissimNetworkLaneParser parser, LinkTag linkTag, OTSPoint3D[] nodeCoords)
327             throws SAXException, NetworkException
328     {
329         // generate nodes from every Vissim link/connector
330         String fromNodeStr = "" + parser.getUpperNodeNr();
331         parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
332         String toNodeStr = "" + parser.getUpperNodeNr();
333         parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
334 
335         // parse the NODES, and add them to a nodelist directly
336         NodeTag.parseNodes(parser, fromNodeStr, toNodeStr, nodeCoords);
337 
338         linkTag.nodeStartTag = parser.getNodeTags().get(fromNodeStr);
339         linkTag.nodeEndTag = parser.getNodeTags().get(toNodeStr);
340     }
341 
342     private static OTSPoint3D[] parseLinkGeometry(final VissimNetworkLaneParser parser, Node node, LinkTag linkTag)
343             throws SAXException, NetworkException
344     {
345         List<Node> geometry = XMLParser.getNodes(node.getChildNodes(), "geometry");
346         List<Node> pointsNodes = XMLParser.getNodes(geometry.get(0).getChildNodes(), "points3D");
347         String coords = "";
348         int numberOfPoints = 0;
349         for (Node pointNode : XMLParser.getNodes(pointsNodes.get(0).getChildNodes(), "point3D"))
350         {
351             NamedNodeMap polyLineAttributes = pointNode.getAttributes();
352             coords += "(" + polyLineAttributes.getNamedItem("x").getNodeValue() + ", "
353                     + polyLineAttributes.getNamedItem("y").getNodeValue() + ")";
354             numberOfPoints++;
355         }
356 
357         OTSPoint3D[] nodeCoords = null;
358         if (numberOfPoints > 2)
359         {
360             // process the intermediate vertices only
361             PolyLineTag.parsePolyLine(coords, parser, linkTag);
362         }
363         else
364         {
365             // parse the STRAIGHT tag
366             StraightTag.parseStraight(coords, parser, linkTag);
367             // add coordinates to the nodes and vertices
368         }
369         // coords of begin and end Node
370         nodeCoords = Coordinates.parseCoordinates(coords);
371         return nodeCoords;
372     }
373 
374     /**
375      * Split the links at a certain point along the link.
376      * @param splitNodeTag NodeTag; Node located at the intersection point of the connector and the link
377      * @param linkTag LinkTag; Tag of the link that meets the connector
378      * @param parser VissimNetworkLaneParser; the VissimParser with info to create a network
379      * @param splitPosition Double; position at the link where the split is expected
380      * @param margin Double; if the splitPosition is at the start or end of the LinkTag, the connector is supposed to be a chain
381      *            and not a split
382      * @param isConnectorToLink boolean; is this is a connector towards a link (true) or starting from a link (false)
383      * @return Mat&lt;String, LinkTab&gt;
384      * @throws OTSGeometryException ...
385      */
386     public static Map<String, LinkTag> splitLink(final NodeTag splitNodeTag, final LinkTag linkTag,
387             final VissimNetworkLaneParser parser, final Double splitPosition, final Double margin,
388             final boolean isConnectorToLink) throws OTSGeometryException
389     {
390 
391         // generate a LineString of the "real" Link
392         OTSLine3D designLineOTS = createLineString(linkTag);
393         LineString designLine = designLineOTS.getLineString();
394 
395         // only split if the splitPosition is not:
396         // (1) at or very near the start of a link
397         // (2) at or very near the end of a link
398         if (splitPosition > margin && splitPosition < designLine.getLength() - margin)
399         {
400             // split the geometry in two parts (cut by the connector)
401             LineString designLineStart = SubstringLine.getSubstring(designLine, 0.0, splitPosition);
402             LineString designLineEnd = SubstringLine.getSubstring(designLine, splitPosition, designLine.getLength());
403 
404             // the linkTag is split in two parts....
405             // first create a copy of the current toLink
406             LinkTag endLinkTag = new LinkTag(linkTag);
407             // add a unique name
408             String linkName = "" + parser.getUpperLinkNr();
409             parser.setUpperLinkNr(parser.getUpperLinkNr() + 1);
410             endLinkTag.name = linkName;
411 
412             // the first link has the same characteristics as the old link, but implements a new endNode (the endNode of the
413             // Connector) and an updated geometry.
414             // the Second link (endLinkTag) copies the characteristics from the old link, but creates a new startNode, endNode
415             // and geometry.
416             createGeometryStartLink(linkTag, designLineStart, splitNodeTag);
417             createGeometryEndLink(endLinkTag, designLineEnd, splitNodeTag);
418 
419             // Furthermore, the signalHeads and sensors are moved to one of the new links
420             // First, relocate the signalheads over the links that are split
421             Iterator<SignalHeadTag> signalHeads = linkTag.signalHeads.iterator();
422             while (signalHeads.hasNext())
423             {
424                 SignalHeadTag signalHeadTag = signalHeads.next();
425                 Double position = Double.parseDouble(signalHeadTag.positionStr);
426                 if (position > splitPosition)
427                 {
428                     // update the position of the signalHead!
429                     Double newPosition = position - splitPosition;
430                     signalHeadTag.positionStr = newPosition.toString();
431                     // add to the newly constructed link
432                     endLinkTag.signalHeads.add(new SignalHeadTag(signalHeadTag));
433                     // remove from the other part of the link
434                     signalHeadTag.activeOnThisLink = false;
435                     linkTag.signalHeadsToRemove.add(signalHeadTag);
436                 }
437             }
438 
439             // relocate the signalheads over the links that are split
440             Iterator<SensorTag> sensors = linkTag.sensors.iterator();
441             while (sensors.hasNext())
442             {
443                 SensorTag sensorTag = sensors.next();
444                 Double position = Double.parseDouble(sensorTag.positionStr);
445                 if (position > splitPosition)
446                 {
447                     // update the position of the Sensor!
448                     Double newPosition = position - splitPosition;
449                     sensorTag.positionStr = newPosition.toString();
450                     // add to the newly constructed link
451                     endLinkTag.sensors.add(new SensorTag(sensorTag));
452                     // remove from the other part of the link
453                     sensorTag.activeOnThisLink = false;
454                     linkTag.sensorTagsToRemove.add(sensorTag);
455                     // sensors.remove();
456                 }
457             }
458 
459             // put this link in the map with LinkTags
460             Map<String, LinkTag> newLinkTags = new HashMap<>();
461             newLinkTags.put(endLinkTag.name, endLinkTag);
462             return newLinkTags;
463 
464         }
465         return null;
466 
467     }
468 
469     /**
470      * Creates the Node info for start and end node and intermediate vertices of the StartLink
471      * @param linkTag LinkTag;
472      * @param designLine LineString;
473      * @param nodeTag NodeTag;
474      */
475     private static void createGeometryStartLink(LinkTag linkTag, LineString designLine, NodeTag nodeTag)
476     {
477         Coordinate[] coords = designLine.getCoordinates();
478         OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
479         int i = 0;
480 
481         for (Coordinate coord : coords)
482         {
483             // startNode point
484             if (i == 0)
485             {
486                 linkTag.nodeStartTag.coordinate = new OTSPoint3D(coord);
487             }
488             // endNode point
489             if (i == coords.length - 1)
490             {
491                 nodeTag.coordinate = new OTSPoint3D(coord);
492                 linkTag.nodeEndTag = nodeTag;
493             }
494             if (coords.length > 2 && (i > 0 && i < coords.length - 1))
495             {
496                 vertices[i - 1] = new OTSPoint3D(coord);
497             }
498             i++;
499         }
500         if (linkTag.polyLineTag != null)
501         {
502             if (coords.length <= 2)
503             {
504                 linkTag.polyLineTag = null;
505                 linkTag.straightTag = new StraightTag();
506             }
507             else
508             {
509                 linkTag.polyLineTag.vertices = vertices;
510             }
511         }
512     }
513 
514     /**
515      * Creates the Node info for start and end node and intermediate vertices of the EndLink
516      * @param linkTag LinkTag;
517      * @param designLine LineString;
518      * @param nodeTag NodeTag;
519      */
520     private static void createGeometryEndLink(LinkTag linkTag, LineString designLine, NodeTag nodeTag)
521     {
522         Coordinate[] coords = designLine.getCoordinates();
523         OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
524         int i = 0;
525         for (Coordinate coord : coords)
526         {
527             if (i == 0)
528             {
529                 nodeTag.coordinate = new OTSPoint3D(coord);
530                 linkTag.nodeStartTag = nodeTag;
531             }
532             if (i == coords.length - 1)
533             {
534                 linkTag.nodeEndTag.coordinate = new OTSPoint3D(coord);
535             }
536             if (coords.length > 2 && (i > 0 && i < coords.length - 1))
537             {
538                 vertices[i - 1] = new OTSPoint3D(coord);
539             }
540             i++;
541         }
542         if (linkTag.polyLineTag != null)
543         {
544             if (coords.length <= 2)
545             {
546                 linkTag.polyLineTag = null;
547                 linkTag.straightTag = new StraightTag();
548             }
549             else
550             {
551                 linkTag.polyLineTag.vertices = vertices;
552             }
553         }
554     }
555 
556     private static void createGeometryShortenedLink(LinkTag linkTag, LineString designLine)
557     {
558         Coordinate[] coords = designLine.getCoordinates();
559         OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
560         int i = 0;
561         for (Coordinate coord : coords)
562         {
563             if (i == 0)
564             {
565                 linkTag.nodeStartTag.coordinate = new OTSPoint3D(coord);
566             }
567             if (i == coords.length - 1)
568             {
569                 linkTag.nodeEndTag.coordinate = new OTSPoint3D(coord);
570             }
571             if (coords.length > 2 && (i > 0 && i < coords.length - 1))
572             {
573                 vertices[i - 1] = new OTSPoint3D(coord);
574             }
575             i++;
576         }
577         if (linkTag.polyLineTag != null)
578         {
579             if (coords.length <= 2)
580             {
581                 linkTag.polyLineTag = null;
582                 linkTag.straightTag = new StraightTag();
583             }
584             else
585             {
586                 linkTag.polyLineTag.vertices = vertices;
587             }
588         }
589     }
590 
591     public static OTSLine3D createLineString(LinkTag linkTag) throws OTSGeometryException
592     {
593         OTSPoint3D[] coordinates = null;
594         if (linkTag.straightTag != null)
595         {
596             coordinates = new OTSPoint3D[2];
597             coordinates[0] = linkTag.nodeStartTag.coordinate;
598             coordinates[1] = linkTag.nodeEndTag.coordinate;
599         }
600         else if (linkTag.polyLineTag != null)
601         {
602             int intermediatePoints = linkTag.polyLineTag.vertices.length;
603             coordinates = new OTSPoint3D[intermediatePoints + 2];
604             coordinates[0] = linkTag.nodeStartTag.coordinate;
605             coordinates[intermediatePoints + 1] = linkTag.nodeEndTag.coordinate;
606             for (int p = 0; p < intermediatePoints; p++)
607             {
608                 coordinates[p + 1] = linkTag.polyLineTag.vertices[p];
609             }
610         }
611         return OTSLine3D.createAndCleanOTSLine3D(coordinates);
612 
613     }
614 
615     /**
616      * This method parses a length string that can have values such as: BEGIN, END, 10m, END-10m, 98%. Only use the method after
617      * the length of the cross section elements is known!
618      * @param posStr String; the position string to parse. Lengths are relative to the center line of the cross section element.
619      * @param cse CrossSectionElement; the cross section element to retrieve the center line
620      * @return the corresponding position as a length on the center line
621      * @throws NetworkException when parsing fails
622      */
623     static Length parseBeginEndPosition(final String posStr, final CrossSectionElement cse) throws NetworkException
624     {
625         if (posStr.trim().equals("BEGIN"))
626         {
627             return new Length(0.0, LengthUnit.METER);
628         }
629 
630         double length = cse.getCenterLine().getLengthSI();
631 
632         if (posStr.trim().equals("END"))
633         {
634             return new Length(length, LengthUnit.METER);
635         }
636 
637         if (posStr.endsWith("%"))
638         {
639             String s = posStr.substring(0, posStr.length() - 1).trim();
640             try
641             {
642                 double fraction = Double.parseDouble(s) / 100.0;
643                 if (fraction < 0.0 || fraction > 1.0)
644                 {
645                     throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
646                             + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%");
647                 }
648                 return new Length(length * fraction, LengthUnit.METER);
649             }
650             catch (NumberFormatException nfe)
651             {
652                 throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
653                         + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%", nfe);
654             }
655         }
656 
657         if (posStr.trim().startsWith("END-"))
658         {
659             String s = posStr.substring(4).trim();
660             double offset = LengthUnits.parseLength(s).getSI();
661             if (offset > length)
662             {
663                 throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr
664                         + " invalid for lane " + cse.toString() + ": provided negative offset greater than than link length");
665             }
666             return new Length(length - offset, LengthUnit.METER);
667         }
668 
669         Length offset = LengthUnits.parseLength(posStr);
670         if (offset.getSI() > length)
671         {
672             throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr + " invalid for lane "
673                     + cse.toString() + ": provided offset greater than than link length");
674         }
675         return offset;
676     }
677 
678     /** {@inheritDoc} */
679     @Override
680     public String toString()
681     {
682         return "LinkTag [name=" + this.name + "]";
683     }
684 
685     /**
686      * @param parser VissimNetworkLaneParser; the VissimParser with info to create a network
687      */
688     public static void addSignalHeads(VissimNetworkLaneParser parser)
689     {
690         for (SignalHeadTag signalHeadTag : parser.getSignalHeadTags().values())
691         {
692             parser.getLinkTags().get(signalHeadTag.linkName).signalHeads.add(signalHeadTag);
693         }
694     }
695 
696     /**
697      * @param parser VissimNetworkLaneParser; the VissimParser with info to create a network
698      */
699     public static void addDetectors(VissimNetworkLaneParser parser)
700     {
701         for (SensorTag sensorTag : parser.getSensorTags().values())
702         {
703             parser.getLinkTags().get(sensorTag.linkName).sensors.add(sensorTag);
704         }
705     }
706 
707     /**
708      * @param parser VissimNetworkLaneParser; the VissimParser with info to create a network
709      * @throws OTSGeometryException:
710      * @throws NamingException:
711      * @throws NetworkException:
712      */
713     public static void shortenConnectors(VissimNetworkLaneParser parser)
714             throws OTSGeometryException, NetworkException, NamingException
715     {
716         for (LinkTag connectorTag : parser.getConnectorTags().values())
717         {
718             OTSLine3D designLineOTS = LinkTag.createLineString(connectorTag);
719             LineString designLine = designLineOTS.getLineString();
720             Double length = designLine.getLength();
721             Double decreaseLength = length / 3.0;
722             LineString shortenedDesignLine = SubstringLine.getSubstring(designLine, decreaseLength, length - decreaseLength);
723             createGeometryShortenedLink(connectorTag, shortenedDesignLine);
724             // make a new OTS node, because the coordinates have changed
725             connectorTag.nodeStartTag.node = NodeTag.makeOTSNode(connectorTag.nodeStartTag, parser);
726             connectorTag.nodeEndTag.node = NodeTag.makeOTSNode(connectorTag.nodeEndTag, parser);
727         }
728     }
729 
730 }