View Javadoc
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   * <p>
66   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
67   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
68   * <p>
69   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
70   * initial version Jul 23, 2015 <br>
71   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
72   */
73  public class XmlNetworkLaneParser implements Serializable
74  {
75      /** */
76      private static final long serialVersionUID = 20150723L;
77  
78      /** Global values from the GLOBAL tag. */
79      @SuppressWarnings("visibilitymodifier")
80      protected GlobalTag globalTag;
81  
82      /** The UNprocessed nodes for further reference. */
83      @SuppressWarnings("visibilitymodifier")
84      protected Map<String, NodeTag> nodeTags = new HashMap<>();
85  
86      /** The UNprocessed connectors for further reference. */
87      @SuppressWarnings("visibilitymodifier")
88      protected Map<String, ConnectorTag> connectorTags = new HashMap<>();
89  
90      /** The UNprocessed links for further reference. */
91      @SuppressWarnings("visibilitymodifier")
92      protected Map<String, LinkTag> linkTags = new HashMap<>();
93  
94      /** The GTU tags for further reference. */
95      @SuppressWarnings("visibilitymodifier")
96      public Map<String, GTUTag> gtuTags = new HashMap<>();
97  
98      /** The GTUmix tags for further reference. */
99      @SuppressWarnings("visibilitymodifier")
100     protected Map<String, GTUMixTag> gtuMixTags = new HashMap<>();
101 
102     /** The route tags for further reference. */
103     @SuppressWarnings("visibilitymodifier")
104     protected Map<String, RouteTag> routeTags = new HashMap<>();
105 
106     /** The route mix tags for further reference. */
107     @SuppressWarnings("visibilitymodifier")
108     protected Map<String, RouteMixTag> routeMixTags = new HashMap<>();
109 
110     /** The shortest route tags for further reference. */
111     @SuppressWarnings("visibilitymodifier")
112     protected Map<String, ShortestRouteTag> shortestRouteTags = new HashMap<>();
113 
114     /** The shortest route mix tags for further reference. */
115     @SuppressWarnings("visibilitymodifier")
116     protected Map<String, ShortestRouteMixTag> shortestRouteMixTags = new HashMap<>();
117 
118     /** The road type tags for further reference. */
119     @SuppressWarnings("visibilitymodifier")
120     protected Map<String, RoadTypeTag> roadTypeTags = new HashMap<>();
121 
122     /** The road layout tags for further reference. */
123     @SuppressWarnings("visibilitymodifier")
124     protected Map<String, RoadLayoutTag> roadLayoutTags = new HashMap<>();
125 
126     /** The GTUTypes that have been created. public to make it accessible from LaneAttributes. */
127     @SuppressWarnings("visibilitymodifier")
128     public Map<String, GTUType> gtuTypes = new HashMap<>();
129 
130     /** The LaneType tags that have been created. */
131     @SuppressWarnings("visibilitymodifier")
132     protected Map<String, LaneTypeTag> laneTypeTags = new HashMap<>();
133 
134     /** The LaneTypes that have been created. */
135     @SuppressWarnings("visibilitymodifier")
136     protected Map<String, LaneType> laneTypes = new HashMap<>();
137 
138     /** The simulator for creating the animation. Null if no animation needed. */
139     @SuppressWarnings("visibilitymodifier")
140     protected OTSSimulatorInterface simulator;
141 
142     /** The network to register the nodes, links, roads, lanes, and GTUs in. */
143     @SuppressWarnings("visibilitymodifier")
144     protected OTSNetwork network;
145 
146     /** The network to register the drawing information for the network in. */
147     @SuppressWarnings("visibilitymodifier")
148     protected NetworkAnimation networkAnimation;
149 
150     /** All comments encountered in the XML file. */
151     List<String> xmlComments = new ArrayList<>();
152 
153     /**
154      * @param simulator OTSSimulatorInterface; the simulator for creating the animation. Null if no animation needed.
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      * @param simulator OTSSimulatorInterface; the simulator for creating the animation. Null if no animation needed.
166      * @param colorer GTUColorer; GTU colorer
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      * @param url URL; the file with the network in the agreed xml-grammar.
179      * @param interpretXMLComments boolean; if true; interpret specifically formatted XML comments and modify the network
180      *            accordingly
181      * @return the network with Nodes, Links, and Lanes.
182      * @throws NetworkException in case of parsing problems.
183      * @throws SAXException in case of parsing problems.
184      * @throws ParserConfigurationException in case of parsing problems.
185      * @throws IOException in case of file reading problems.
186      * @throws NamingException in case the animation context cannot be found
187      * @throws GTUException in case of a problem with creating the LaneBlock (which is a GTU right now)
188      * @throws OTSGeometryException when construction of a lane contour or offset design line fails
189      * @throws SimRuntimeException when simulator cannot be used to schedule GTU generation
190      * @throws ParameterException ...
191      * @throws ValueException ...
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      * @param stream InputStream; the input stream with the network in the agreed xml-grammar.
203      * @param interpretXMLComments boolean; if true; interpret specifically formatted XML comments and modify the network
204      *            accordingly
205      * @return the network with Nodes, Links, and Lanes.
206      * @throws NetworkException in case of parsing problems.
207      * @throws SAXException in case of parsing problems.
208      * @throws ParserConfigurationException in case of parsing problems.
209      * @throws IOException in case of file reading problems.
210      * @throws NamingException in case the animation context cannot be found
211      * @throws GTUException in case of a problem with creating the LaneBlock (which is a GTU right now)
212      * @throws OTSGeometryException when construction of a lane contour or offset design line fails
213      * @throws SimRuntimeException when simulator cannot be used to schedule GTU generation
214      * @throws ParameterException ...
215      * @throws ValueException ...
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      * @param url URL; the file with the network in the agreed xml-grammar.
227      * @param otsNetwork OTSNetwork; the network
228      * @param interpretXMLComments boolean; if true; interpret specifically formatted XML comments and modify the network
229      *            accordingly
230      * @return the network with Nodes, Links, and Lanes.
231      * @throws NetworkException in case of parsing problems.
232      * @throws SAXException in case of parsing problems.
233      * @throws ParserConfigurationException in case of parsing problems.
234      * @throws IOException in case of file reading problems.
235      * @throws NamingException in case the animation context cannot be found
236      * @throws GTUException in case of a problem with creating the LaneBlock (which is a GTU right now)
237      * @throws OTSGeometryException when construction of a lane contour or offset design line fails
238      * @throws SimRuntimeException when simulator cannot be used to schedule GTU generation
239      * @throws ParameterException ...
240      * @throws ValueException ...
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      * @param stream InputStream; the input stream with the network in the agreed xml-grammar.
252      * @param otsNetwork OTSNetwork; the network
253      * @param interpretXMLComments boolean; if true; interpret specifically formatted XML comments and modify the network
254      *            accordingly
255      * @return the network with Nodes, Links, and Lanes.
256      * @throws NetworkException in case of parsing problems.
257      * @throws SAXException in case of parsing problems.
258      * @throws ParserConfigurationException in case of parsing problems.
259      * @throws IOException in case of file reading problems.
260      * @throws NamingException in case the animation context cannot be found
261      * @throws GTUException in case of a problem with creating the LaneBlock (which is a GTU right now)
262      * @throws OTSGeometryException when construction of a lane contour or offset design line fails
263      * @throws SimRuntimeException when simulator cannot be used to schedule GTU generation
264      * @throws ParameterException ...
265      * @throws ValueException ...
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         // try
273         // {
274         // if (url.getFile().length() > 0 && !(new File(url.toURI()).exists()))
275         // throw new SAXException("XmlNetworkLaneParser.build: File " + url.getFile() + " does not exist");
276         // }
277         // catch (URISyntaxException exception)
278         // {
279         // throw new SAXException("XmlNetworkLaneParser.build: File " + url.getFile() + " is not properly formatted",
280         // exception);
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         // there should be some definitions using DEFINITIONS tags (could be more than one due to include files)
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         // make the GTUTypes ALL and NONE to get started
313         this.gtuTypes.put("ALL", GTUType.VEHICLE);
314         // this.gtuTypes.put("NONE", GTUType.NONE);
315 
316         // parse the DEFINITIONS tags
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         // parse the NETWORK tag
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         // process nodes and links to calculate coordinates and positions
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         // process the routes
351         for (RouteTag routeTag : this.routeTags.values())
352             routeTag.makeRoute();
353         // TODO shortestRoute, routeMix, ShortestRouteMix
354 
355         // store the structure information in the network
356         makeNetwork();
357         if (interpretXMLComments)
358         {
359             // fixOD(result);
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             // TODO add chosen model in gtuTypeGenerator
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      * Retrieve the OD info from the XML comments and apply it to the network.
395      * @param otsNetwork OTSNetwork; the network
396      * @throws NetworkException should never happen (of course)
397      * @throws OTSGeometryException might happen if a centroid is positioned on top of the entry exit point of a link
398      * @throws NamingException on error
399      * @throws RemoteException on error
400      * @throws ValueException on error
401      * @throws SimRuntimeException on error
402      * @throws ParameterException on error
403      */
404     private void fixOD(final OTSNetwork otsNetwork) throws NetworkException, OTSGeometryException, RemoteException,
405             NamingException, ValueException, ParameterException, SimRuntimeException
406     {
407         // Reduce the list to only OD comments and strip the OD header and parse each into a key-value map.
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         // Find the GTUTypes
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         // Extract the simulation start time
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         // Construct the centroid nodes and the links between the centroid nodes and the generation and extraction nodes
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         // Categorization categorization = new Categorization("AimsunOTSExport", firstGTUType, otherGTUTypes);
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      * Parse a line that should look like a list of key=value pairs.
570      * @param line String; the line to parse
571      * @return Map&lt;String,String&gt;; the parsed line
572      */
573     private Map<String, String> parseODLine(final String line)
574     {
575         Map<String, String> result = new HashMap<>();
576         // For now we'll assume that names of centroids, links and nodes do not contain spaces.
577         for (String pair : line.split(" "))
578         {
579             String[] fields = pair.split("=");
580             // Concatenate elements 1..n
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      * Adds routes.
600      * @throws NetworkException if items cannot be added to the Network
601      */
602     private void makeNetwork() throws NetworkException
603     {
604         for (RouteTag routeTag : this.routeTags.values())
605         {
606             // TODO Make routes GTU specific. See what to do with GTUType.ALL for routes
607             // TODO Automate addition of Routes to network
608             this.network.addRoute(GTUType.VEHICLE, routeTag.route);
609         }
610     }
611 
612     /**
613      * Obtain an immutable copy of the collected XML comments.
614      * @return List&lt;String&gt;; a list of the XML comments
615      */
616     public ImmutableList<String> getXMLComments()
617     {
618         return new ImmutableArrayList<>(this.xmlComments, Immutable.COPY);
619     }
620 
621     /**
622      * @return the network animation data belonging to the network that was parsed.
623      */
624     public final NetworkAnimation getNetworkAnimation()
625     {
626         return this.networkAnimation;
627     }
628 
629     /** {@inheritDoc} */
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 }