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.opentrafficsim.core.geometry.OTSGeometryException;
16 import org.opentrafficsim.core.geometry.OTSLine3D;
17 import org.opentrafficsim.core.geometry.OTSPoint3D;
18 import org.opentrafficsim.core.network.NetworkException;
19 import org.opentrafficsim.core.network.factory.xml.units.Coordinates;
20 import org.opentrafficsim.core.network.factory.xml.units.LengthUnits;
21 import org.opentrafficsim.road.network.lane.CrossSectionElement;
22 import org.opentrafficsim.road.network.lane.CrossSectionLink;
23 import org.opentrafficsim.road.network.lane.Lane;
24 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
25 import org.w3c.dom.NamedNodeMap;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28 import org.xml.sax.SAXException;
29
30 import com.vividsolutions.jts.geom.Coordinate;
31 import com.vividsolutions.jts.geom.LineString;
32
33
34
35
36
37
38
39
40
41
42 final class LinkTag implements Serializable
43 {
44
45 private static final long serialVersionUID = 20150723L;
46
47
48 @SuppressWarnings("checkstyle:visibilitymodifier")
49 String name = null;
50
51
52 @SuppressWarnings("checkstyle:visibilitymodifier")
53 String legalSpeed = null;
54
55
56 @SuppressWarnings("checkstyle:visibilitymodifier")
57 NodeTag nodeStartTag = null;
58
59
60 @SuppressWarnings("checkstyle:visibilitymodifier")
61 NodeTag nodeEndTag = null;
62
63
64 @SuppressWarnings("checkstyle:visibilitymodifier")
65 Length offsetStart = null;
66
67
68 @SuppressWarnings("checkstyle:visibilitymodifier")
69 Length offsetEnd = null;
70
71
72 @SuppressWarnings("checkstyle:visibilitymodifier")
73 Angle rotationStart = null;
74
75
76 @SuppressWarnings("checkstyle:visibilitymodifier")
77 Angle rotationEnd = null;
78
79
80 @SuppressWarnings("checkstyle:visibilitymodifier")
81 StraightTag straightTag = null;
82
83
84
85 @SuppressWarnings("checkstyle:visibilitymodifier")
86 PolyLineTag polyLineTag = null;
87
88
89 @SuppressWarnings("checkstyle:visibilitymodifier")
90 Map<String, LaneOverrideTag> laneOverrideTags = new HashMap<>();
91
92
93 @SuppressWarnings("checkstyle:visibilitymodifier")
94 Map<String, GeneratorTag> generatorTags = new HashMap<>();
95
96
97 @SuppressWarnings("checkstyle:visibilitymodifier")
98 Map<String, ListGeneratorTag> listGeneratorTags = new HashMap<>();
99
100
101 @SuppressWarnings("checkstyle:visibilitymodifier")
102 Map<String, List<SensorTag>> sensorTags = new HashMap<>();
103
104
105 @SuppressWarnings("checkstyle:visibilitymodifier")
106 Map<String, FillTag> fillTags = new HashMap<>();
107
108
109 @SuppressWarnings("checkstyle:visibilitymodifier")
110 Map<String, SinkTag> sinkTags = new HashMap<>();
111
112
113 @SuppressWarnings("checkstyle:visibilitymodifier")
114 Map<String, Lane> lanes = new HashMap<>();
115
116
117 @SuppressWarnings("checkstyle:visibilitymodifier")
118 Map<String, LaneTag> laneTags = new HashMap<>();
119
120 ConnectorTag connectorTag = null;
121
122
123 @SuppressWarnings("checkstyle:visibilitymodifier")
124 CrossSectionLink link = null;
125
126
127 @SuppressWarnings("checkstyle:visibilitymodifier")
128 LaneKeepingPolicy laneKeepingPolicy = null;
129
130 List<SignalHeadTag> signalHeads = new ArrayList<>();
131
132 List<SensorTag> sensors = new ArrayList<>();
133
134 List<SignalHeadTag> signalHeadsToRemove = new ArrayList<>();
135
136 List<SensorTag> sensorTagsToRemove = new ArrayList<>();
137
138
139 boolean connector = false;
140
141 ArcTag arcTag;
142
143 BezierTag bezierTag;
144
145 public RoadLayoutTag roadLayoutTag;
146
147
148
149
150 public LinkTag(LinkTag linkTag)
151 {
152 this.connectorTag = linkTag.connectorTag;
153 this.connector = linkTag.connector;
154 this.laneKeepingPolicy = linkTag.laneKeepingPolicy;
155 this.lanes.putAll(linkTag.lanes);
156 this.laneTags.putAll(linkTag.laneTags);
157 this.legalSpeed = linkTag.legalSpeed;
158 if (linkTag.straightTag != null)
159 {
160 this.straightTag = new StraightTag(linkTag.straightTag);
161 }
162 if (linkTag.polyLineTag != null)
163 {
164 this.polyLineTag = new PolyLineTag(linkTag.polyLineTag);
165 }
166 this.nodeEndTag = linkTag.nodeEndTag;
167 this.nodeStartTag = linkTag.nodeStartTag;
168 }
169
170
171
172
173 public LinkTag()
174 {
175 }
176
177
178
179
180
181
182
183
184 @SuppressWarnings("checkstyle:needbraces")
185 static void parseLinks(final NodeList nodeList, final VissimNetworkLaneParser parser) throws SAXException, NetworkException
186 {
187
188 for (Node linksNode : XMLParser.getNodes(nodeList, "links"))
189 {
190
191 for (Node node : XMLParser.getNodes(linksNode.getChildNodes(), "link"))
192 {
193 NamedNodeMap attributes = node.getAttributes();
194
195
196 LinkTag linkTag = new LinkTag();
197
198
199
200 linkTag.laneKeepingPolicy = LaneKeepingPolicy.KEEP_LANE;
201
202 if (attributes.getNamedItem("no") == null)
203 {
204 throw new SAXException("LINK: missing attribute: no");
205 }
206 linkTag.name = attributes.getNamedItem("no").getNodeValue().trim();
207 Integer linkNr = Integer.parseInt(linkTag.name);
208 if (linkNr > parser.getUpperLinkNr())
209 {
210 parser.setUpperLinkNr(linkNr);
211 }
212
213 if (attributes.getNamedItem("assumSpeedOncom") == null)
214 {
215 throw new SAXException("LINK: missing attribute assumSpeedOncom");
216 }
217 linkTag.legalSpeed = attributes.getNamedItem("assumSpeedOncom").getNodeValue().trim();
218
219
220 OTSPoint3D[] nodeCoords = parseLinkGeometry(parser, node, linkTag);
221
222
223 createNodesForLink(parser, linkTag, nodeCoords);
224
225
226 List<Node> laneNodes = XMLParser.getNodes(node.getChildNodes(), "lanes");
227
228
229 createLanes(linkTag, laneNodes);
230
231
232 List<Node> connectorFromNode = XMLParser.getNodes(node.getChildNodes(), "fromLinkEndPt");
233 if (connectorFromNode.size() > 0)
234 {
235 linkTag.connector = true;
236 createConnectorInfoFrom(linkTag, connectorFromNode);
237 }
238
239 List<Node> connectorToNode = XMLParser.getNodes(node.getChildNodes(), "toLinkEndPt");
240 if (connectorFromNode.size() > 0)
241 {
242 linkTag.connector = true;
243 createConnectorInfoTo(linkTag, connectorToNode);
244 }
245
246
247 parser.getLinkTags().put(linkTag.name, linkTag);
248 }
249
250 }
251
252 }
253
254 private static void createConnectorInfoTo(LinkTag linkTag, List<Node> connectorToNode) throws SAXException
255 {
256 NamedNodeMap attributes;
257 if (linkTag.connectorTag == null)
258 {
259 linkTag.connectorTag = new ConnectorTag();
260 }
261 attributes = connectorToNode.get(0).getAttributes();
262 if (attributes.getNamedItem("lane") == null)
263 {
264 throw new SAXException("Connector: missing attribute: link/lane info");
265 }
266 String connect = attributes.getNamedItem("lane").getNodeValue().trim();
267 String[] connectInfo = connect.split("\\s+");
268 linkTag.connectorTag.toLinkNo = connectInfo[0];
269 linkTag.connectorTag.toLaneNo = connectInfo[1];
270 if (attributes.getNamedItem("pos") == null)
271 {
272 throw new SAXException("Connector: missing attribute: pos (position info)");
273 }
274 linkTag.connectorTag.toPositionStr = attributes.getNamedItem("pos").getNodeValue().trim();
275 }
276
277 private static void createConnectorInfoFrom(LinkTag linkTag, List<Node> connectorFromNode) throws SAXException
278 {
279 NamedNodeMap attributes;
280 if (linkTag.connectorTag == null)
281 {
282 linkTag.connectorTag = new ConnectorTag();
283 }
284 attributes = connectorFromNode.get(0).getAttributes();
285 if (attributes.getNamedItem("lane") == null)
286 {
287 throw new SAXException("Connector: missing attribute: link/lane info");
288 }
289 String connect = attributes.getNamedItem("lane").getNodeValue().trim();
290 String[] connectInfo = connect.split("\\s+");
291 linkTag.connectorTag.fromLinkNo = connectInfo[0];
292 linkTag.connectorTag.fromLaneNo = connectInfo[1];
293
294 if (attributes.getNamedItem("pos") == null)
295 {
296 throw new SAXException("Connector: missing attribute: pos (position info)");
297 }
298 linkTag.connectorTag.fromPositionStr = attributes.getNamedItem("pos").getNodeValue().trim();
299 }
300
301 private static void createLanes(LinkTag linkTag, List<Node> laneNodes)
302 {
303 NamedNodeMap attributes;
304 int laneNo = 1;
305 for (Node laneNode : XMLParser.getNodes(laneNodes.get(0).getChildNodes(), "lane"))
306 {
307
308 attributes = laneNode.getAttributes();
309 LaneTag laneTag = new LaneTag();
310 if (attributes.getLength() > 0)
311 {
312 laneTag.width = attributes.getNamedItem("width").getNodeValue().trim();
313 }
314 else
315 {
316 laneTag.width = "3.5";
317
318
319 }
320 laneTag.linkNo = linkTag.name;
321 laneTag.laneNo = "" + laneNo;
322 laneNo++;
323 linkTag.laneTags.put(laneTag.laneNo, laneTag);
324 }
325 }
326
327 private static void createNodesForLink(final VissimNetworkLaneParser parser, LinkTag linkTag, OTSPoint3D[] nodeCoords)
328 throws SAXException, NetworkException
329 {
330
331 String fromNodeStr = "" + parser.getUpperNodeNr();
332 parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
333 String toNodeStr = "" + parser.getUpperNodeNr();
334 parser.setUpperNodeNr(parser.getUpperNodeNr() + 1);
335
336
337 NodeTag.parseNodes(parser, fromNodeStr, toNodeStr, nodeCoords);
338
339 linkTag.nodeStartTag = parser.getNodeTags().get(fromNodeStr);
340 linkTag.nodeEndTag = parser.getNodeTags().get(toNodeStr);
341 }
342
343 private static OTSPoint3D[] parseLinkGeometry(final VissimNetworkLaneParser parser, Node node, LinkTag linkTag)
344 throws SAXException, NetworkException
345 {
346 List<Node> geometry = XMLParser.getNodes(node.getChildNodes(), "geometry");
347 List<Node> pointsNodes = XMLParser.getNodes(geometry.get(0).getChildNodes(), "points3D");
348 String coords = "";
349 int numberOfPoints = 0;
350 for (Node pointNode : XMLParser.getNodes(pointsNodes.get(0).getChildNodes(), "point3D"))
351 {
352 NamedNodeMap polyLineAttributes = pointNode.getAttributes();
353 coords += "(" + polyLineAttributes.getNamedItem("x").getNodeValue() + ", "
354 + polyLineAttributes.getNamedItem("y").getNodeValue() + ")";
355 numberOfPoints++;
356 }
357
358 OTSPoint3D[] nodeCoords = null;
359 if (numberOfPoints > 2)
360 {
361
362 PolyLineTag.parsePolyLine(coords, parser, linkTag);
363 }
364 else
365 {
366
367 StraightTag.parseStraight(coords, parser, linkTag);
368
369 }
370
371 nodeCoords = Coordinates.parseCoordinates(coords);
372 return nodeCoords;
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386
387 public static Map<String, LinkTag> splitLink(final NodeTag splitNodeTag, final LinkTag linkTag,
388 final VissimNetworkLaneParser parser, final Double splitPosition, final Double margin,
389 final boolean isConnectorToLink) throws OTSGeometryException
390 {
391
392
393 OTSLine3D designLineOTS = createLineString(linkTag);
394 LineString designLine = designLineOTS.getLineString();
395
396
397
398
399 if (splitPosition > margin && splitPosition < designLine.getLength() - margin)
400 {
401
402 LineString designLineStart = SubstringLine.getSubstring(designLine, 0.0, splitPosition);
403 LineString designLineEnd = SubstringLine.getSubstring(designLine, splitPosition, designLine.getLength());
404
405
406
407 LinkTag endLinkTag = new LinkTag(linkTag);
408
409 String linkName = "" + parser.getUpperLinkNr();
410 parser.setUpperLinkNr(parser.getUpperLinkNr() + 1);
411 endLinkTag.name = linkName;
412
413
414
415
416
417 createGeometryStartLink(linkTag, designLineStart, splitNodeTag);
418 createGeometryEndLink(endLinkTag, designLineEnd, splitNodeTag);
419
420
421
422 Iterator<SignalHeadTag> signalHeads = linkTag.signalHeads.iterator();
423 while (signalHeads.hasNext())
424 {
425 SignalHeadTag signalHeadTag = signalHeads.next();
426 Double position = Double.parseDouble(signalHeadTag.positionStr);
427 if (position > splitPosition)
428 {
429
430 Double newPosition = position - splitPosition;
431 signalHeadTag.positionStr = newPosition.toString();
432
433 endLinkTag.signalHeads.add(new SignalHeadTag(signalHeadTag));
434
435 signalHeadTag.activeOnThisLink = false;
436 linkTag.signalHeadsToRemove.add(signalHeadTag);
437 }
438 }
439
440
441 Iterator<SensorTag> sensors = linkTag.sensors.iterator();
442 while (sensors.hasNext())
443 {
444 SensorTag sensorTag = sensors.next();
445 Double position = Double.parseDouble(sensorTag.positionStr);
446 if (position > splitPosition)
447 {
448
449 Double newPosition = position - splitPosition;
450 sensorTag.positionStr = newPosition.toString();
451
452 endLinkTag.sensors.add(new SensorTag(sensorTag));
453
454 sensorTag.activeOnThisLink = false;
455 linkTag.sensorTagsToRemove.add(sensorTag);
456
457 }
458 }
459
460
461 Map<String, LinkTag> newLinkTags = new HashMap<>();
462 newLinkTags.put(endLinkTag.name, endLinkTag);
463 return newLinkTags;
464
465 }
466 return null;
467
468 }
469
470
471
472
473
474
475
476 private static void createGeometryStartLink(LinkTag linkTag, LineString designLine, NodeTag nodeTag)
477 {
478 Coordinate[] coords = designLine.getCoordinates();
479 OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
480 int i = 0;
481
482 for (Coordinate coord : coords)
483 {
484
485 if (i == 0)
486 {
487 linkTag.nodeStartTag.coordinate = new OTSPoint3D(coord);
488 }
489
490 if (i == coords.length - 1)
491 {
492 nodeTag.coordinate = new OTSPoint3D(coord);
493 linkTag.nodeEndTag = nodeTag;
494 }
495 if (coords.length > 2 && (i > 0 && i < coords.length - 1))
496 {
497 vertices[i - 1] = new OTSPoint3D(coord);
498 }
499 i++;
500 }
501 if (linkTag.polyLineTag != null)
502 {
503 if (coords.length <= 2)
504 {
505 linkTag.polyLineTag = null;
506 linkTag.straightTag = new StraightTag();
507 }
508 else
509 {
510 linkTag.polyLineTag.vertices = vertices;
511 }
512 }
513 }
514
515
516
517
518
519
520
521 private static void createGeometryEndLink(LinkTag linkTag, LineString designLine, NodeTag nodeTag)
522 {
523 Coordinate[] coords = designLine.getCoordinates();
524 OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
525 int i = 0;
526 for (Coordinate coord : coords)
527 {
528 if (i == 0)
529 {
530 nodeTag.coordinate = new OTSPoint3D(coord);
531 linkTag.nodeStartTag = nodeTag;
532 }
533 if (i == coords.length - 1)
534 {
535 linkTag.nodeEndTag.coordinate = new OTSPoint3D(coord);
536 }
537 if (coords.length > 2 && (i > 0 && i < coords.length - 1))
538 {
539 vertices[i - 1] = new OTSPoint3D(coord);
540 }
541 i++;
542 }
543 if (linkTag.polyLineTag != null)
544 {
545 if (coords.length <= 2)
546 {
547 linkTag.polyLineTag = null;
548 linkTag.straightTag = new StraightTag();
549 }
550 else
551 {
552 linkTag.polyLineTag.vertices = vertices;
553 }
554 }
555 }
556
557 private static void createGeometryShortenedLink(LinkTag linkTag, LineString designLine)
558 {
559 Coordinate[] coords = designLine.getCoordinates();
560 OTSPoint3D[] vertices = new OTSPoint3D[coords.length - 2];
561 int i = 0;
562 for (Coordinate coord : coords)
563 {
564 if (i == 0)
565 {
566 linkTag.nodeStartTag.coordinate = new OTSPoint3D(coord);
567 }
568 if (i == coords.length - 1)
569 {
570 linkTag.nodeEndTag.coordinate = new OTSPoint3D(coord);
571 }
572 if (coords.length > 2 && (i > 0 && i < coords.length - 1))
573 {
574 vertices[i - 1] = new OTSPoint3D(coord);
575 }
576 i++;
577 }
578 if (linkTag.polyLineTag != null)
579 {
580 if (coords.length <= 2)
581 {
582 linkTag.polyLineTag = null;
583 linkTag.straightTag = new StraightTag();
584 }
585 else
586 {
587 linkTag.polyLineTag.vertices = vertices;
588 }
589 }
590 }
591
592 public static OTSLine3D createLineString(LinkTag linkTag) throws OTSGeometryException
593 {
594 OTSPoint3D[] coordinates = null;
595 if (linkTag.straightTag != null)
596 {
597 coordinates = new OTSPoint3D[2];
598 coordinates[0] = linkTag.nodeStartTag.coordinate;
599 coordinates[1] = linkTag.nodeEndTag.coordinate;
600 }
601 else if (linkTag.polyLineTag != null)
602 {
603 int intermediatePoints = linkTag.polyLineTag.vertices.length;
604 coordinates = new OTSPoint3D[intermediatePoints + 2];
605 coordinates[0] = linkTag.nodeStartTag.coordinate;
606 coordinates[intermediatePoints + 1] = linkTag.nodeEndTag.coordinate;
607 for (int p = 0; p < intermediatePoints; p++)
608 {
609 coordinates[p + 1] = linkTag.polyLineTag.vertices[p];
610 }
611 }
612 return OTSLine3D.createAndCleanOTSLine3D(coordinates);
613
614 }
615
616
617
618
619
620
621
622
623
624 static Length parseBeginEndPosition(final String posStr, final CrossSectionElement cse) throws NetworkException
625 {
626 if (posStr.trim().equals("BEGIN"))
627 {
628 return new Length(0.0, LengthUnit.METER);
629 }
630
631 double length = cse.getCenterLine().getLengthSI();
632
633 if (posStr.trim().equals("END"))
634 {
635 return new Length(length, LengthUnit.METER);
636 }
637
638 if (posStr.endsWith("%"))
639 {
640 String s = posStr.substring(0, posStr.length() - 1).trim();
641 try
642 {
643 double fraction = Double.parseDouble(s) / 100.0;
644 if (fraction < 0.0 || fraction > 1.0)
645 {
646 throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
647 + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%");
648 }
649 return new Length(length * fraction, LengthUnit.METER);
650 }
651 catch (NumberFormatException nfe)
652 {
653 throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
654 + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%", nfe);
655 }
656 }
657
658 if (posStr.trim().startsWith("END-"))
659 {
660 String s = posStr.substring(4).trim();
661 double offset = LengthUnits.parseLength(s).getSI();
662 if (offset > length)
663 {
664 throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr
665 + " invalid for lane " + cse.toString() + ": provided negative offset greater than than link length");
666 }
667 return new Length(length - offset, LengthUnit.METER);
668 }
669
670 Length offset = LengthUnits.parseLength(posStr);
671 if (offset.getSI() > length)
672 {
673 throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr + " invalid for lane "
674 + cse.toString() + ": provided offset greater than than link length");
675 }
676 return offset;
677 }
678
679
680 @Override
681 public String toString()
682 {
683 return "LinkTag [name=" + this.name + "]";
684 }
685
686
687
688
689 public static void addSignalHeads(VissimNetworkLaneParser parser)
690 {
691 for (SignalHeadTag signalHeadTag : parser.getSignalHeadTags().values())
692 {
693 parser.getLinkTags().get(signalHeadTag.linkName).signalHeads.add(signalHeadTag);
694 }
695 }
696
697
698
699
700 public static void addDetectors(VissimNetworkLaneParser parser)
701 {
702 for (SensorTag sensorTag : parser.getSensorTags().values())
703 {
704 parser.getLinkTags().get(sensorTag.linkName).sensors.add(sensorTag);
705 }
706 }
707
708
709
710
711
712
713
714 public static void shortenConnectors(VissimNetworkLaneParser parser)
715 throws OTSGeometryException, NetworkException, NamingException
716 {
717 for (LinkTag connectorTag : parser.getConnectorTags().values())
718 {
719 OTSLine3D designLineOTS = LinkTag.createLineString(connectorTag);
720 LineString designLine = designLineOTS.getLineString();
721 Double length = designLine.getLength();
722 Double decreaseLength = length / 3.0;
723 LineString shortenedDesignLine = SubstringLine.getSubstring(designLine, decreaseLength, length - decreaseLength);
724 createGeometryShortenedLink(connectorTag, shortenedDesignLine);
725
726 connectorTag.nodeStartTag.node = NodeTag.makeOTSNode(connectorTag.nodeStartTag, parser);
727 connectorTag.nodeEndTag.node = NodeTag.makeOTSNode(connectorTag.nodeEndTag, parser);
728 }
729 }
730
731 }