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
34
35
36
37
38
39
40
41 final class LinkTag implements Serializable
42 {
43
44 private static final long serialVersionUID = 20150723L;
45
46
47 @SuppressWarnings("checkstyle:visibilitymodifier")
48 String name = null;
49
50
51 @SuppressWarnings("checkstyle:visibilitymodifier")
52 String legalSpeed = null;
53
54
55 @SuppressWarnings("checkstyle:visibilitymodifier")
56 NodeTag nodeStartTag = null;
57
58
59 @SuppressWarnings("checkstyle:visibilitymodifier")
60 NodeTag nodeEndTag = null;
61
62
63 @SuppressWarnings("checkstyle:visibilitymodifier")
64 Length offsetStart = null;
65
66
67 @SuppressWarnings("checkstyle:visibilitymodifier")
68 Length offsetEnd = null;
69
70
71 @SuppressWarnings("checkstyle:visibilitymodifier")
72 Angle rotationStart = null;
73
74
75 @SuppressWarnings("checkstyle:visibilitymodifier")
76 Angle rotationEnd = null;
77
78
79 @SuppressWarnings("checkstyle:visibilitymodifier")
80 StraightTag straightTag = null;
81
82
83
84 @SuppressWarnings("checkstyle:visibilitymodifier")
85 PolyLineTag polyLineTag = null;
86
87
88 @SuppressWarnings("checkstyle:visibilitymodifier")
89 Map<String, LaneOverrideTag> laneOverrideTags = new HashMap<>();
90
91
92 @SuppressWarnings("checkstyle:visibilitymodifier")
93 Map<String, GeneratorTag> generatorTags = new HashMap<>();
94
95
96 @SuppressWarnings("checkstyle:visibilitymodifier")
97 Map<String, ListGeneratorTag> listGeneratorTags = new HashMap<>();
98
99
100 @SuppressWarnings("checkstyle:visibilitymodifier")
101 Map<String, List<SensorTag>> sensorTags = new HashMap<>();
102
103
104 @SuppressWarnings("checkstyle:visibilitymodifier")
105 Map<String, FillTag> fillTags = new HashMap<>();
106
107
108 @SuppressWarnings("checkstyle:visibilitymodifier")
109 Map<String, SinkTag> sinkTags = new HashMap<>();
110
111
112 @SuppressWarnings("checkstyle:visibilitymodifier")
113 Map<String, Lane> lanes = new HashMap<>();
114
115
116 @SuppressWarnings("checkstyle:visibilitymodifier")
117 Map<String, LaneTag> laneTags = new HashMap<>();
118
119 ConnectorTag connectorTag = null;
120
121
122 @SuppressWarnings("checkstyle:visibilitymodifier")
123 CrossSectionLink link = null;
124
125
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
138 boolean connector = false;
139
140 ArcTag arcTag;
141
142 BezierTag bezierTag;
143
144 public RoadLayoutTag roadLayoutTag;
145
146
147
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
178
179
180
181
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
195 LinkTag linkTag = new LinkTag();
196
197
198
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
219 OTSPoint3D[] nodeCoords = parseLinkGeometry(parser, node, linkTag);
220
221
222 createNodesForLink(parser, linkTag, nodeCoords);
223
224
225 List<Node> laneNodes = XMLParser.getNodes(node.getChildNodes(), "lanes");
226
227
228 createLanes(linkTag, laneNodes);
229
230
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
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
317
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
330 String fromNodeStr = "" + parser.getUpperNodeNr();
331 parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
332 String toNodeStr = "" + parser.getUpperNodeNr();
333 parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
334
335
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
361 PolyLineTag.parsePolyLine(coords, parser, linkTag);
362 }
363 else
364 {
365
366 StraightTag.parseStraight(coords, parser, linkTag);
367
368 }
369
370 nodeCoords = Coordinates.parseCoordinates(coords);
371 return nodeCoords;
372 }
373
374
375
376
377
378
379
380
381
382
383
384
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
392 OTSLine3D designLineOTS = createLineString(linkTag);
393 LineString designLine = designLineOTS.getLineString();
394
395
396
397
398 if (splitPosition > margin && splitPosition < designLine.getLength() - margin)
399 {
400
401 LineString designLineStart = SubstringLine.getSubstring(designLine, 0.0, splitPosition);
402 LineString designLineEnd = SubstringLine.getSubstring(designLine, splitPosition, designLine.getLength());
403
404
405
406 LinkTag endLinkTag = new LinkTag(linkTag);
407
408 String linkName = "" + parser.getUpperLinkNr();
409 parser.setUpperLinkNr(parser.getUpperLinkNr() + 1);
410 endLinkTag.name = linkName;
411
412
413
414
415
416 createGeometryStartLink(linkTag, designLineStart, splitNodeTag);
417 createGeometryEndLink(endLinkTag, designLineEnd, splitNodeTag);
418
419
420
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
429 Double newPosition = position - splitPosition;
430 signalHeadTag.positionStr = newPosition.toString();
431
432 endLinkTag.signalHeads.add(new SignalHeadTag(signalHeadTag));
433
434 signalHeadTag.activeOnThisLink = false;
435 linkTag.signalHeadsToRemove.add(signalHeadTag);
436 }
437 }
438
439
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
448 Double newPosition = position - splitPosition;
449 sensorTag.positionStr = newPosition.toString();
450
451 endLinkTag.sensors.add(new SensorTag(sensorTag));
452
453 sensorTag.activeOnThisLink = false;
454 linkTag.sensorTagsToRemove.add(sensorTag);
455
456 }
457 }
458
459
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
471
472
473
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
484 if (i == 0)
485 {
486 linkTag.nodeStartTag.coordinate = new OTSPoint3D(coord);
487 }
488
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
516
517
518
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
617
618
619
620
621
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
679 @Override
680 public String toString()
681 {
682 return "LinkTag [name=" + this.name + "]";
683 }
684
685
686
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
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
709
710
711
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
725 connectorTag.nodeStartTag.node = NodeTag.makeOTSNode(connectorTag.nodeStartTag, parser);
726 connectorTag.nodeEndTag.node = NodeTag.makeOTSNode(connectorTag.nodeEndTag, parser);
727 }
728 }
729
730 }