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