1 package org.opentrafficsim.road.network.factory.xml.demand;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.Serializable;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.LinkedHashMap;
11 import java.util.LinkedHashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.SortedSet;
16 import java.util.TreeSet;
17
18 import javax.xml.parsers.DocumentBuilder;
19 import javax.xml.parsers.DocumentBuilderFactory;
20 import javax.xml.parsers.ParserConfigurationException;
21
22 import org.djunits.unit.TimeUnit;
23 import org.djunits.value.StorageType;
24 import org.djunits.value.vdouble.scalar.Time;
25 import org.djunits.value.vdouble.vector.FrequencyVector;
26 import org.djunits.value.vdouble.vector.TimeVector;
27 import org.opentrafficsim.base.parameters.ParameterException;
28 import org.opentrafficsim.core.gtu.GTUType;
29 import org.opentrafficsim.core.gtu.NestedCache;
30 import org.opentrafficsim.core.gtu.Try;
31 import org.opentrafficsim.core.network.OTSNetwork;
32 import org.opentrafficsim.core.network.factory.xml.units.TimeUnits;
33 import org.opentrafficsim.road.gtu.generator.od.ODApplier;
34 import org.opentrafficsim.road.gtu.generator.od.ODOptions;
35 import org.opentrafficsim.road.gtu.strategical.od.Categorization;
36 import org.opentrafficsim.road.gtu.strategical.od.Category;
37 import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
38 import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
39 import org.opentrafficsim.road.network.factory.xml.XMLParser;
40 import org.opentrafficsim.road.network.factory.xml.XmlParserException;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.xml.sax.SAXException;
46
47 import nl.tudelft.simulation.dsol.SimRuntimeException;
48 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
49 import nl.tudelft.simulation.language.Throw;
50
51
52
53
54
55
56
57
58
59
60
61
62 public class XmlOdParser implements Serializable
63 {
64
65
66 private static final long serialVersionUID = 20180525L;
67
68
69 private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
70
71
72 final OTSNetwork network;
73
74
75 private final Map<String, GTUType> gtuTypes = new HashMap<>();
76
77
78 Categorization categorization;
79
80
81 Map<String, CategoryTag> categories = new LinkedHashMap<>();
82
83
84 TimeVector globalTime;
85
86
87 Interpolation globalInterpolation;
88
89
90 NestedCache<Set<DemandTag>> demand =
91 new NestedCache<>(org.opentrafficsim.core.network.Node.class, org.opentrafficsim.core.network.Node.class);
92
93
94
95
96
97
98
99 public XmlOdParser(final DEVSSimulatorInterface.TimeDoubleUnit simulator, final OTSNetwork network, final Set<GTUType> gtuTypes)
100 {
101 Throw.whenNull(simulator, "Simulator should not be null.");
102 Throw.whenNull(network, "Network should not be null.");
103 this.simulator = simulator;
104 this.network = network;
105 for (GTUType gtuType : gtuTypes)
106 {
107 this.gtuTypes.put(gtuType.getId(), gtuType);
108 }
109 }
110
111
112
113
114
115
116
117 public GTUType getGTUType(final String id) throws XmlParserException
118 {
119 Throw.when(!this.gtuTypes.containsKey(id), XmlParserException.class, "GTU type %s is not available.", id);
120 return this.gtuTypes.get(id);
121 }
122
123
124
125
126
127
128 public final void apply(final URL url) throws XmlParserException
129 {
130 apply(url, new ODOptions());
131 }
132
133
134
135
136
137
138
139 public void apply(final URL url, final ODOptions odOptions) throws XmlParserException
140 {
141 try
142 {
143 apply(url.openStream(), odOptions);
144 }
145 catch (IOException exception)
146 {
147 throw new XmlParserException(exception);
148 }
149 }
150
151
152
153
154
155
156 public final void apply(final InputStream stream) throws XmlParserException
157 {
158 apply(stream, new ODOptions());
159 }
160
161
162
163
164
165
166
167 public final void apply(final InputStream stream, final ODOptions odOptions) throws XmlParserException
168 {
169 applyOD(build(stream), odOptions);
170 }
171
172
173
174
175
176
177 public final void apply(final Node xmlNode) throws XmlParserException
178 {
179 apply(xmlNode, new ODOptions());
180 }
181
182
183
184
185
186
187
188 public void apply(final Node xmlNode, final ODOptions odOptions) throws XmlParserException
189 {
190 applyOD(build(xmlNode), odOptions);
191 }
192
193
194
195
196
197
198
199 private void applyOD(final ODMatrix od, final ODOptions odOptions) throws XmlParserException
200 {
201 try
202 {
203 ODApplier.applyOD(this.network, od, this.simulator, odOptions);
204 }
205 catch (ParameterException | SimRuntimeException exception)
206 {
207 throw new XmlParserException(exception);
208 }
209 }
210
211
212
213
214
215
216
217 public final ODMatrix build(final URL url) throws XmlParserException
218 {
219 try
220 {
221 return build(url.openStream());
222 }
223 catch (IOException exception)
224 {
225 throw new XmlParserException(exception);
226 }
227 }
228
229
230
231
232
233
234
235 public final ODMatrix build(final InputStream stream) throws XmlParserException
236 {
237 try
238 {
239 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
240 DocumentBuilder builder = factory.newDocumentBuilder();
241 Document document = builder.parse(stream);
242 return build(document.getDocumentElement());
243 }
244 catch (ParserConfigurationException | SAXException | IOException exception)
245 {
246 throw new XmlParserException(exception);
247 }
248 }
249
250
251
252
253
254
255
256 public final ODMatrix build(final Node xmlNode) throws XmlParserException
257 {
258 Throw.when(!xmlNode.getNodeName().equals("OD"), XmlParserException.class,
259 "OD should be parsed from a node OD, found %s instead.", xmlNode.getNodeName());
260
261
262 NamedNodeMap attributes = xmlNode.getAttributes();
263 Node nameNode = attributes.getNamedItem("NAME");
264 String name = nameNode == null ? "xml od" : nameNode.getNodeValue().trim();
265
266
267 Node globalInterpolationNode = attributes.getNamedItem("GLOBALINTERPOLATION");
268 if (globalInterpolationNode != null)
269 {
270 String globalInterpolationString = globalInterpolationNode.getNodeValue().trim();
271 try
272 {
273 this.globalInterpolation = Interpolation.valueOf(globalInterpolationNode.getNodeValue().trim());
274 }
275 catch (IllegalArgumentException exception)
276 {
277 throw new XmlParserException("INTERPOLATION " + globalInterpolationString + " does not exist.", exception);
278 }
279 }
280 else
281 {
282 this.globalInterpolation = Interpolation.LINEAR;
283 }
284
285
286 Node globalFractionNode = attributes.getNamedItem("GLOBALFACTOR");
287 Double globalFraction = null;
288 if (globalFractionNode != null)
289 {
290 String globalFractionString = globalFractionNode.getNodeValue().trim();
291 globalFraction = DemandTag.parseFactor(globalFractionString);
292 }
293
294
295
296
297 NodeList odNodeList = xmlNode.getChildNodes();
298 List<Node> nodes = XMLParser.getNodes(odNodeList, "GLOBALTIME");
299 Throw.when(nodes.size() > 1, XmlParserException.class, "Multiple GLOBALTIME tags, only 1 is allowed.");
300 SortedSet<Time> timeSet = new TreeSet<>();
301 if (!nodes.isEmpty())
302 {
303 for (Node timeNode : XMLParser.getNodes(nodes.get(0).getChildNodes(), "TIME"))
304 {
305 NamedNodeMap timeAttributes = timeNode.getAttributes();
306 Node valueNode = timeAttributes.getNamedItem("VALUE");
307 Throw.when(valueNode == null, XmlParserException.class, "LEVEL tag is missing VALUE attribute.");
308 timeSet.add(Try.assign(() -> TimeUnits.parseTime(valueNode.getNodeValue()), XmlParserException.class,
309 "Unable to parse time %s.", valueNode.getNodeValue()));
310 }
311 }
312 double[] timeArray = new double[timeSet.size()];
313 Iterator<Time> it = timeSet.iterator();
314 for (int i = 0; i < timeSet.size(); i++)
315 {
316 timeArray[i] = it.next().si;
317 }
318 this.globalTime = Try.assign(() -> new TimeVector(timeArray, TimeUnit.BASE, StorageType.DENSE),
319 XmlParserException.class, "Unexpected exception while creating global time vector.");
320 CategoryTag.parse(odNodeList, this);
321 DemandTag.parse(odNodeList, this);
322
323
324 List<org.opentrafficsim.core.network.Node> origins = new ArrayList<>();
325 List<org.opentrafficsim.core.network.Node> destinations = new ArrayList<>();
326 for (Object oKey : this.demand.getKeys())
327 {
328 origins.add((org.opentrafficsim.core.network.Node) oKey);
329 for (Object dKey : this.demand.getChild(oKey).getKeys())
330 {
331 if (!destinations.contains(dKey))
332 {
333 destinations.add((org.opentrafficsim.core.network.Node) dKey);
334 }
335 }
336 }
337 ODMatrix odMatrix =
338 new ODMatrix(name, origins, destinations, this.categorization, this.globalTime, this.globalInterpolation);
339
340
341 for (org.opentrafficsim.core.network.Node origin : origins)
342 {
343 for (org.opentrafficsim.core.network.Node destination : destinations)
344 {
345 Set<DemandTag> set = this.demand.getValue(() -> new LinkedHashSet<>(), origin, destination);
346 if (!set.isEmpty())
347 {
348
349 DemandTag main = null;
350 if (!this.categorization.equals(Categorization.UNCATEGORIZED))
351 {
352 for (DemandTag tag : set)
353 {
354 if (tag.category == null)
355 {
356 Throw.when(main != null, XmlParserException.class,
357 "Multiple DEMAND tags define main demand from %s to %s.", origin.getId(),
358 destination.getId());
359 Throw.when(set.size() == 1, XmlParserException.class,
360 "Categorized demand from %s to %s has single DEMAND, and without category.",
361 origin.getId(), destination.getId());
362 main = tag;
363 }
364 }
365 }
366 for (DemandTag tag : set)
367 {
368 Throw.when(this.categorization.equals(Categorization.UNCATEGORIZED) && set.size() > 1,
369 XmlParserException.class, "Multiple DEMAND tags define demand from %s to %s.", origin.getId(),
370 destination.getId());
371 if (tag.equals(main))
372 {
373
374 continue;
375 }
376 TimeVector timeVector = tag.timeVector == null
377 ? (main == null || main.timeVector == null ? this.globalTime : main.timeVector)
378 : tag.timeVector;
379 Interpolation interpolation = tag.interpolation == null
380 ? (main == null || main.interpolation == null ? this.globalInterpolation : main.interpolation)
381 : tag.interpolation;
382
383 Category category = this.categorization.equals(Categorization.UNCATEGORIZED) ? Category.UNCATEGORIZED
384 : tag.category;
385
386 Double factor = globalFraction != null ? globalFraction : null;
387 Double mainFraction = main == null ? null : main.factor;
388 factor = nullMultiply(factor, mainFraction);
389 factor = nullMultiply(factor, tag.factor);
390 FrequencyVector demandVector = tag.demandVector;
391 if (demandVector == null)
392 {
393 demandVector = main.demandVector;
394 }
395 if (tag.factors != null)
396 {
397 double[] factors;
398 if (factor == null)
399 {
400 factors = tag.factors;
401 }
402 else
403 {
404 factors = new double[tag.factors.length];
405 for (int i = 0; i < tag.factors.length; i++)
406 {
407 factors[i] = tag.factors[i] * factor;
408 }
409 }
410 odMatrix.putDemandVector(origin, destination, category, demandVector, timeVector, interpolation,
411 factors);
412 }
413 else if (factor != null)
414 {
415 odMatrix.putDemandVector(origin, destination, category, demandVector, timeVector, interpolation,
416 factor);
417 }
418 else
419 {
420 odMatrix.putDemandVector(origin, destination, category, demandVector, timeVector, interpolation);
421 }
422 }
423 }
424 }
425 }
426
427 return odMatrix;
428 }
429
430
431
432
433
434
435
436 final static Double nullMultiply(final Double d1, final Double d2)
437 {
438 if (d1 == null)
439 {
440 return d2;
441 }
442 if (d2 == null)
443 {
444 return d1;
445 }
446 return d1 * d2;
447 }
448
449 }