1 package org.opentrafficsim.road.network.factory.xml;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.Serializable;
6 import java.net.URL;
7 import java.rmi.RemoteException;
8 import java.util.ArrayList;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.LinkedHashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15
16 import javax.naming.NamingException;
17 import javax.xml.parsers.DocumentBuilder;
18 import javax.xml.parsers.DocumentBuilderFactory;
19 import javax.xml.parsers.ParserConfigurationException;
20
21 import org.djunits.unit.FrequencyUnit;
22 import org.djunits.unit.TimeUnit;
23 import org.djunits.value.StorageType;
24 import org.djunits.value.ValueException;
25 import org.djunits.value.vdouble.vector.FrequencyVector;
26 import org.djunits.value.vdouble.vector.TimeVector;
27 import org.djutils.immutablecollections.Immutable;
28 import org.djutils.immutablecollections.ImmutableArrayList;
29 import org.djutils.immutablecollections.ImmutableList;
30 import org.opentrafficsim.base.parameters.ParameterException;
31 import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
32 import org.opentrafficsim.core.animation.network.NetworkAnimation;
33 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
34 import org.opentrafficsim.core.geometry.OTSGeometryException;
35 import org.opentrafficsim.core.geometry.OTSLine3D;
36 import org.opentrafficsim.core.geometry.OTSPoint3D;
37 import org.opentrafficsim.core.gtu.GTUException;
38 import org.opentrafficsim.core.gtu.GTUType;
39 import org.opentrafficsim.core.gtu.TemplateGTUType;
40 import org.opentrafficsim.core.network.Link;
41 import org.opentrafficsim.core.network.LinkType;
42 import org.opentrafficsim.core.network.NetworkException;
43 import org.opentrafficsim.core.network.OTSNetwork;
44 import org.opentrafficsim.core.network.OTSNode;
45 import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
46 import org.opentrafficsim.road.gtu.generator.od.GTUCharacteristicsGeneratorOD;
47 import org.opentrafficsim.road.gtu.generator.od.ODOptions;
48 import org.opentrafficsim.road.gtu.strategical.od.Categorization;
49 import org.opentrafficsim.road.gtu.strategical.od.Category;
50 import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
51 import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
52 import org.opentrafficsim.road.network.factory.xml.demand.XmlOdParser;
53 import org.opentrafficsim.road.network.lane.CrossSectionLink;
54 import org.opentrafficsim.road.network.lane.LaneType;
55 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
56 import org.w3c.dom.Comment;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.Node;
59 import org.w3c.dom.NodeList;
60 import org.xml.sax.SAXException;
61
62 import nl.tudelft.simulation.dsol.SimRuntimeException;
63
64
65
66
67
68
69
70
71
72
73 public class XmlNetworkLaneParser implements Serializable
74 {
75
76 private static final long serialVersionUID = 20150723L;
77
78
79 @SuppressWarnings("visibilitymodifier")
80 protected GlobalTag globalTag;
81
82
83 @SuppressWarnings("visibilitymodifier")
84 protected Map<String, NodeTag> nodeTags = new HashMap<>();
85
86
87 @SuppressWarnings("visibilitymodifier")
88 protected Map<String, ConnectorTag> connectorTags = new HashMap<>();
89
90
91 @SuppressWarnings("visibilitymodifier")
92 protected Map<String, LinkTag> linkTags = new HashMap<>();
93
94
95 @SuppressWarnings("visibilitymodifier")
96 public Map<String, GTUTag> gtuTags = new HashMap<>();
97
98
99 @SuppressWarnings("visibilitymodifier")
100 protected Map<String, GTUMixTag> gtuMixTags = new HashMap<>();
101
102
103 @SuppressWarnings("visibilitymodifier")
104 protected Map<String, RouteTag> routeTags = new HashMap<>();
105
106
107 @SuppressWarnings("visibilitymodifier")
108 protected Map<String, RouteMixTag> routeMixTags = new HashMap<>();
109
110
111 @SuppressWarnings("visibilitymodifier")
112 protected Map<String, ShortestRouteTag> shortestRouteTags = new HashMap<>();
113
114
115 @SuppressWarnings("visibilitymodifier")
116 protected Map<String, ShortestRouteMixTag> shortestRouteMixTags = new HashMap<>();
117
118
119 @SuppressWarnings("visibilitymodifier")
120 protected Map<String, RoadTypeTag> roadTypeTags = new HashMap<>();
121
122
123 @SuppressWarnings("visibilitymodifier")
124 protected Map<String, RoadLayoutTag> roadLayoutTags = new HashMap<>();
125
126
127 @SuppressWarnings("visibilitymodifier")
128 public Map<String, GTUType> gtuTypes = new HashMap<>();
129
130
131 @SuppressWarnings("visibilitymodifier")
132 protected Map<String, LaneTypeTag> laneTypeTags = new HashMap<>();
133
134
135 @SuppressWarnings("visibilitymodifier")
136 protected Map<String, LaneType> laneTypes = new HashMap<>();
137
138
139 @SuppressWarnings("visibilitymodifier")
140 protected OTSSimulatorInterface simulator;
141
142
143 @SuppressWarnings("visibilitymodifier")
144 protected OTSNetwork network;
145
146
147 @SuppressWarnings("visibilitymodifier")
148 protected NetworkAnimation networkAnimation;
149
150
151 List<String> xmlComments = new ArrayList<>();
152
153
154
155
156 public XmlNetworkLaneParser(final OTSSimulatorInterface simulator)
157 {
158 this.simulator = simulator;
159 LaneTypeTag laneTypeTagNoTraffic = new LaneTypeTag();
160 laneTypeTagNoTraffic.name = "NOTRAFFIC";
161 this.laneTypeTags.put(laneTypeTagNoTraffic.name, laneTypeTagNoTraffic);
162 }
163
164
165
166
167
168 public XmlNetworkLaneParser(final OTSSimulatorInterface simulator, final GTUColorer colorer)
169 {
170 this.simulator = simulator;
171 GTUColorerTag.defaultColorer = colorer;
172 LaneTypeTag laneTypeTagNoTraffic = new LaneTypeTag();
173 laneTypeTagNoTraffic.name = "NOTRAFFIC";
174 this.laneTypeTags.put(laneTypeTagNoTraffic.name, laneTypeTagNoTraffic);
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @SuppressWarnings("checkstyle:needbraces")
194 public final OTSNetwork build(final URL url, boolean interpretXMLComments)
195 throws NetworkException, ParserConfigurationException, SAXException, IOException, NamingException, GTUException,
196 OTSGeometryException, SimRuntimeException, ValueException, ParameterException
197 {
198 return build(url, new OTSNetwork(url.toString()), interpretXMLComments);
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 @SuppressWarnings("checkstyle:needbraces")
218 public final OTSNetwork build(final InputStream stream, boolean interpretXMLComments)
219 throws NetworkException, ParserConfigurationException, SAXException, IOException, NamingException, GTUException,
220 OTSGeometryException, SimRuntimeException, ValueException, ParameterException
221 {
222 return build(stream, new OTSNetwork(stream.toString()), interpretXMLComments);
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 @SuppressWarnings("checkstyle:needbraces")
243 public final OTSNetwork build(final URL url, final OTSNetwork otsNetwork, boolean interpretXMLComments)
244 throws NetworkException, ParserConfigurationException, SAXException, IOException, NamingException, GTUException,
245 OTSGeometryException, SimRuntimeException, ValueException, ParameterException
246 {
247 return build(url.openStream(), otsNetwork, interpretXMLComments);
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 @SuppressWarnings("checkstyle:needbraces")
268 public final OTSNetwork build(final InputStream stream, final OTSNetwork otsNetwork, boolean interpretXMLComments)
269 throws NetworkException, ParserConfigurationException, SAXException, IOException, NamingException, GTUException,
270 OTSGeometryException, SimRuntimeException, ValueException, ParameterException
271 {
272
273
274
275
276
277
278
279
280
281
282 this.network = otsNetwork;
283 this.networkAnimation = new NetworkAnimation(this.network);
284 this.xmlComments.clear();
285
286 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
287 factory.setNamespaceAware(true);
288 factory.setXIncludeAware(true);
289 DocumentBuilder builder = factory.newDocumentBuilder();
290 Document document = builder.parse(stream);
291 NodeList networkNodeList = document.getDocumentElement().getChildNodes();
292
293 for (int i = 0; i < networkNodeList.getLength(); i++)
294 {
295 Node node = networkNodeList.item(i);
296 if (node instanceof Comment)
297 {
298 this.xmlComments.add(node.getTextContent());
299 }
300 }
301
302 if (!document.getDocumentElement().getNodeName().equals("NETWORK"))
303 throw new SAXException("XmlNetworkLaneParser.build: XML document does not start with an NETWORK tag, found "
304 + document.getDocumentElement().getNodeName() + " instead");
305
306
307 List<Node> definitionNodes = XMLParser.getNodes(networkNodeList, "DEFINITIONS");
308
309 if (definitionNodes.size() == 0)
310 throw new SAXException("XmlNetworkLaneParser.build: XML document does not have a DEFINITIONS tag");
311
312
313 this.gtuTypes.put("ALL", GTUType.VEHICLE);
314
315
316
317 for (Node definitionNode : definitionNodes)
318 GlobalTag.parseGlobal(definitionNode.getChildNodes(), this);
319 for (Node definitionNode : definitionNodes)
320 GTUTypeTag.parseGTUTypes(definitionNode.getChildNodes(), this);
321 for (Node definitionNode : definitionNodes)
322 GTUTag.parseGTUs(definitionNode.getChildNodes(), this);
323 for (Node definitionNode : definitionNodes)
324 GTUMixTag.parseGTUMix(definitionNode.getChildNodes(), this);
325 for (Node definitionNode : definitionNodes)
326 RoadTypeTag.parseRoadTypes(definitionNode.getChildNodes(), this);
327 for (Node definitionNode : definitionNodes)
328 LaneTypeTag.parseLaneTypes(definitionNode.getChildNodes(), this);
329 for (Node definitionNode : definitionNodes)
330 RoadLayoutTag.parseRoadTypes(definitionNode.getChildNodes(), this);
331
332
333 NodeTag.parseNodes(networkNodeList, this);
334 RouteTag.parseRoutes(networkNodeList, this);
335 ShortestRouteTag.parseShortestRoutes(networkNodeList, this);
336 RouteMixTag.parseRouteMix(networkNodeList, this);
337 ShortestRouteMixTag.parseShortestRouteMix(networkNodeList, this);
338 ConnectorTag.parseConnectors(networkNodeList, this);
339 LinkTag.parseLinks(networkNodeList, this);
340
341
342 Links.calculateNodeCoordinates(this);
343 for (ConnectorTag connectorTag : this.connectorTags.values())
344 Links.buildConnector(connectorTag, this, this.simulator);
345 for (LinkTag linkTag : this.linkTags.values())
346 Links.buildLink(linkTag, this, this.simulator);
347 for (LinkTag linkTag : this.linkTags.values())
348 Links.applyRoadTypeToLink(linkTag, this, this.simulator);
349
350
351 for (RouteTag routeTag : this.routeTags.values())
352 routeTag.makeRoute();
353
354
355
356 makeNetwork();
357 if (interpretXMLComments)
358 {
359
360 }
361
362 List<Node> od = XMLParser.getNodes(networkNodeList, "OD");
363 if (od.size() == 1)
364 {
365 Set<TemplateGTUType> templates = new LinkedHashSet<>();
366 for (String gtuType : this.gtuTags.keySet())
367 {
368 GTUTag gtuTag = this.gtuTags.get(gtuType);
369 templates.add(new TemplateGTUType(this.gtuTypes.get(gtuType), gtuTag.lengthDist, gtuTag.widthDist,
370 gtuTag.maxSpeedDist));
371 }
372 GTUCharacteristicsGeneratorOD gtuTypeGenerator = new DefaultGTUCharacteristicsGeneratorOD(templates);
373 ODOptions odOptions = new ODOptions().set(ODOptions.GTU_TYPE, gtuTypeGenerator);
374
375 XmlOdParser xmlOdParser =
376 new XmlOdParser(this.simulator, this.network, new LinkedHashSet<>(this.gtuTypes.values()));
377 try
378 {
379 xmlOdParser.apply(od.get(0), odOptions);
380 }
381 catch (XmlParserException exception)
382 {
383 throw new SAXException("Exception while applying OD.", exception);
384 }
385 }
386 else if (od.size() > 1)
387 {
388 throw new SAXException("XmlNetworkLaneParser.build: XML document contains multiple OD tags");
389 }
390 return this.network;
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404 private void fixOD(final OTSNetwork otsNetwork) throws NetworkException, OTSGeometryException, RemoteException,
405 NamingException, ValueException, ParameterException, SimRuntimeException
406 {
407
408 Map<String, Map<String, String>> odInfo = new HashMap<>();
409 for (String comment : getXMLComments())
410 {
411 if (comment.startsWith("OD "))
412 {
413 String odText = comment.substring(3);
414 odInfo.put(odText, parseODLine(odText));
415 }
416 }
417 System.out.println("There are " + odInfo.size() + " OD comments");
418
419 List<GTUType> odGTUTypes = new ArrayList<>(this.gtuTypes.values());
420 for (GTUType gtuType : odGTUTypes)
421 {
422 if (GTUType.VEHICLE.equals(gtuType))
423 {
424 odGTUTypes.remove(gtuType);
425 break;
426 }
427 }
428 double startTime = Double.NaN;
429
430 for (Map<String, String> map : odInfo.values())
431 {
432 String startTimeString = map.get("simulationStartTime");
433 if (null != startTimeString)
434 {
435 startTime = Double.parseDouble(startTimeString);
436 }
437 }
438 if (Double.isNaN(startTime))
439 {
440 throw new NetworkException("Cannot find start time XML comment");
441 }
442
443 Set<org.opentrafficsim.core.network.Node> origins = new HashSet<>();
444 Set<org.opentrafficsim.core.network.Node> destinations = new HashSet<>();
445 Set<String> startTimeStrings = new HashSet<>();
446 Map<String, String> durations = new HashMap<>();
447 for (Map<String, String> map : odInfo.values())
448 {
449 String od = map.get("od");
450 if (null == od)
451 {
452 continue;
453 }
454 String startTimeString = map.get("startTime");
455 if (null != startTimeString)
456 {
457 startTimeStrings.add(startTimeString);
458 String durationString = map.get("duration");
459 if (null == durationString)
460 {
461 throw new NetworkException("No duration specified");
462 }
463 String old = durations.get(startTimeString);
464 if (null != old && (!durationString.equals(old)))
465 {
466 throw new NetworkException("Duration for period starting at " + startTimeString + " changed from " + old
467 + " to " + durationString);
468 }
469 else
470 {
471 durations.put(startTimeString, durationString);
472 }
473 }
474 String centroidName = map.get("centroid");
475 String[] coordinates = map.get("centroidLocation").split(",");
476 OTSPoint3D centroidPoint = new OTSPoint3D(Double.parseDouble(coordinates[0]), Double.parseDouble(coordinates[1]));
477 org.opentrafficsim.core.network.Node centroidNode = otsNetwork.getNode(centroidName);
478 if (null == centroidNode)
479 {
480 centroidNode = new OTSNode(otsNetwork, centroidName, centroidPoint);
481 }
482 String linkId = map.get("link");
483 Link link = otsNetwork.getLink(linkId);
484 if (null == link)
485 {
486 throw new NetworkException("Cannot find link with id \"" + linkId + "\"");
487 }
488 org.opentrafficsim.core.network.Node from = null;
489 org.opentrafficsim.core.network.Node to = null;
490 if ("attracts".equals(od))
491 {
492 destinations.add(centroidNode);
493 from = link.getEndNode();
494 to = centroidNode;
495
496 }
497 else if ("generates".equals(od))
498 {
499 origins.add(centroidNode);
500 from = centroidNode;
501 to = link.getStartNode();
502 }
503 OTSLine3D designLine = new OTSLine3D(from.getPoint(), to.getPoint());
504 String linkName = String.format("connector_from_%s_to_%s", from.getId(), to.getId());
505 Link connectorLink = otsNetwork.getLink(linkName);
506 if (null == connectorLink)
507 {
508 System.out.println("Constructing connector link " + linkName);
509 connectorLink = new CrossSectionLink(otsNetwork, linkName, from, to, LinkType.CONNECTOR, designLine,
510 this.simulator, LaneKeepingPolicy.KEEP_RIGHT);
511 }
512 }
513 if (startTimeStrings.size() > 1)
514 {
515 throw new NetworkException("Cannot handle multiple start times - yet");
516 }
517 if (startTimeStrings.size() == 0)
518 {
519 throw new NetworkException("Missing start time XML comment");
520 }
521 String startTimeString = startTimeStrings.iterator().next();
522 double start = Double.parseDouble(startTimeString);
523 start = 0;
524 double duration = Double.parseDouble(durations.get(startTimeString));
525 TimeVector tv = new TimeVector(new double[] { start, start + duration }, TimeUnit.BASE, StorageType.DENSE);
526
527 Categorization categorization = new Categorization("AimsunOTSExport", GTUType.class);
528 ODMatrix od = new ODMatrix("ODExample", new ArrayList<>(origins), new ArrayList<>(destinations), categorization, tv,
529 Interpolation.STEPWISE);
530 for (Map<String, String> map : odInfo.values())
531 {
532 String flow = map.get("flow");
533 if (null == flow)
534 {
535 continue;
536 }
537 String vehicleClassName = map.get("vehicleClass");
538 GTUType gtuType = null;
539 for (GTUType gt : odGTUTypes)
540 {
541 if (gt.getId().equals(vehicleClassName))
542 {
543 gtuType = gt;
544 }
545 }
546 if (null == gtuType)
547 {
548 throw new NetworkException("Can not find GTUType with name " + vehicleClassName);
549 }
550 Category category = new Category(categorization, gtuType);
551 org.opentrafficsim.core.network.Node from = otsNetwork.getNode(map.get("origin"));
552 org.opentrafficsim.core.network.Node to = otsNetwork.getNode(map.get("destination"));
553 FrequencyVector demand = new FrequencyVector(new double[] { Double.parseDouble(map.get("flow")), 0 },
554 FrequencyUnit.PER_HOUR, StorageType.DENSE);
555 od.putDemandVector(from, to, category, demand);
556 System.out.println(
557 "Adding demand from " + from.getId() + " to " + to.getId() + " category " + category + ": " + demand);
558 }
559 Set<TemplateGTUType> templates = new HashSet<>();
560 for (GTUType gtuType : odGTUTypes)
561 {
562 GTUTag gtuTag = this.gtuTags.get(gtuType.getId());
563 templates.add(new TemplateGTUType(gtuType, gtuTag.lengthDist, gtuTag.widthDist, gtuTag.maxSpeedDist));
564 }
565 od.print();
566 }
567
568
569
570
571
572
573 private Map<String, String> parseODLine(final String line)
574 {
575 Map<String, String> result = new HashMap<>();
576
577 for (String pair : line.split(" "))
578 {
579 String[] fields = pair.split("=");
580
581 for (int index = fields.length; --index >= 2;)
582 {
583 fields[index - 1] += "=" + fields[index];
584 }
585 if (fields.length < 2)
586 {
587 throw new IndexOutOfBoundsException("can not find equals sign in \"" + pair + "\"");
588 }
589 if (fields[1].startsWith("\"") && fields[1].endsWith("\"") && fields[1].length() >= 2)
590 {
591 fields[1] = fields[1].substring(1, fields[1].length() - 1);
592 }
593 result.put(fields[0], fields[1]);
594 }
595 return result;
596 }
597
598
599
600
601
602 private void makeNetwork() throws NetworkException
603 {
604 for (RouteTag routeTag : this.routeTags.values())
605 {
606
607
608 this.network.addRoute(GTUType.VEHICLE, routeTag.route);
609 }
610 }
611
612
613
614
615
616 public ImmutableList<String> getXMLComments()
617 {
618 return new ImmutableArrayList<>(this.xmlComments, Immutable.COPY);
619 }
620
621
622
623
624 public final NetworkAnimation getNetworkAnimation()
625 {
626 return this.networkAnimation;
627 }
628
629
630 @Override
631 public String toString()
632 {
633 return "XmlNetworkLaneParser [globalTag=" + this.globalTag + ", nodeTags.size=" + this.nodeTags.size()
634 + ", linkTags.size=" + this.linkTags.size() + ", gtuTags.size=" + this.gtuTags.size() + ", gtuMixTags.size="
635 + this.gtuMixTags.size() + ", routeTags.size=" + this.routeTags.size() + ", routeMixTags.size="
636 + this.routeMixTags.size() + ", shortestRouteTagssize.=" + this.shortestRouteTags.size()
637 + ", shortestRouteMixTags.size=" + this.shortestRouteMixTags.size() + ", roadTypeTags.size="
638 + this.roadTypeTags.size() + ", gtuTypes.size=" + this.gtuTypes.size() + ", laneTypes.size="
639 + this.laneTypeTags.size() + "]";
640 }
641
642 }