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