1 package org.opentrafficsim.road.network.factory.xml.demand;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.LinkedHashSet;
6 import java.util.List;
7
8 import org.djunits.unit.FrequencyUnit;
9 import org.djunits.unit.TimeUnit;
10 import org.djunits.value.StorageType;
11 import org.djunits.value.vdouble.scalar.Time;
12 import org.djunits.value.vdouble.vector.FrequencyVector;
13 import org.djunits.value.vdouble.vector.TimeVector;
14 import org.djutils.exceptions.Throw;
15 import org.djutils.exceptions.Try;
16 import org.opentrafficsim.core.network.factory.xml.units.DurationUnits;
17 import org.opentrafficsim.core.network.factory.xml.units.TimeUnits;
18 import org.opentrafficsim.road.gtu.strategical.od.Category;
19 import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
20 import org.opentrafficsim.road.network.factory.xml.XMLParser;
21 import org.opentrafficsim.road.network.factory.xml.XmlParserException;
22 import org.w3c.dom.NamedNodeMap;
23 import org.w3c.dom.Node;
24 import org.w3c.dom.NodeList;
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class DemandTag implements Serializable
39 {
40
41
42 private static final long serialVersionUID = 20180525L;
43
44
45 private final static FrequencyUnit UNIT = FrequencyUnit.PER_HOUR;
46
47
48 org.opentrafficsim.core.network.Node origin;
49
50
51 org.opentrafficsim.core.network.Node destination;
52
53
54 Interpolation interpolation;
55
56
57 Category category;
58
59
60 String categoryName;
61
62
63 DemandType demandType = null;
64
65
66 TimeVector timeVector;
67
68
69 FrequencyVector demandVector;
70
71
72 Double factor;
73
74
75 double[] factors;
76
77
78
79
80
81
82
83 static void parse(final NodeList nodeList, final XmlOdParser parser) throws XmlParserException
84 {
85 for (Node node : XMLParser.getNodesSorted(nodeList, "DEMAND", "ORIGIN", "DESTINATION", "CATEGORY"))
86 {
87 NamedNodeMap attributes = node.getAttributes();
88 DemandTag tag = new DemandTag();
89
90 Node originNode = attributes.getNamedItem("ORIGIN");
91 Throw.when(originNode == null, XmlParserException.class, "Missing ORIGIN attribute in DEMAND tag.");
92 String originId = originNode.getNodeValue().trim();
93 tag.origin = parser.network.getNode(originId);
94 Throw.when(tag.origin == null, XmlParserException.class, "Origin %s is not available.", originId);
95
96 Node destinationNode = attributes.getNamedItem("DESTINATION");
97 Throw.when(destinationNode == null, XmlParserException.class, "Missing DESTINATION attribute in DEMAND tag.");
98 String destinationId = destinationNode.getNodeValue().trim();
99 tag.destination = parser.network.getNode(destinationId);
100 Throw.when(tag.destination == null, XmlParserException.class, "Destination %s is not available.", destinationId);
101
102 Node interpolationNode = attributes.getNamedItem("INTERPOLATION");
103 if (interpolationNode != null)
104 {
105 String interpolation = interpolationNode.getNodeValue().trim();
106 try
107 {
108 tag.interpolation = Interpolation.valueOf(interpolation);
109 }
110 catch (IllegalArgumentException exception)
111 {
112 throw new XmlParserException("INTERPOLATION " + interpolation + " does not exist.", exception);
113 }
114 }
115
116 Node factorNode = attributes.getNamedItem("FACTOR");
117 if (factorNode != null)
118 {
119 tag.factor = parseFactor(factorNode.getNodeValue().trim());
120 }
121
122 Node categoryNode = attributes.getNamedItem("CATEGORY");
123 if (categoryNode != null)
124 {
125 String categoryName = categoryNode.getNodeValue().trim();
126 Throw.when(!parser.categories.containsKey(categoryName), XmlParserException.class,
127 "Category %s is not available.", categoryName);
128 tag.category = parser.categories.get(categoryName).getCategory(parser.categorization);
129 tag.factor = XmlOdParser.nullMultiply(tag.factor, parser.categories.get(categoryName).factor);
130 }
131
132 NodeList childList = node.getChildNodes();
133 List<Double> timeList = new ArrayList<>();
134 List<Double> valueList = new ArrayList<>();
135 List<Node> demandNodes = XMLParser.getNodes(childList, "LEVEL");
136 Throw.when(categoryNode == null && demandNodes.isEmpty(), XmlParserException.class,
137 "DEMAND without CATEGORY attribute should contain demand data.");
138 if (demandNodes.size() == 0)
139 {
140 tag.demandType = DemandType.FACTOR;
141 }
142 for (Node level : demandNodes)
143 {
144 if (tag.demandType == null)
145 {
146 tag.demandType = DemandType.fromLevelNode(level);
147 Throw.when(
148 categoryNode == null && !tag.demandType.equals(DemandType.FREQUENCIES)
149 && !tag.demandType.equals(DemandType.TIMED_FREQUENCIES),
150 XmlParserException.class,
151 "DEMAND without CATEGORY attribute should contain non-factoral demand data.");
152 }
153 NamedNodeMap levelAttributes = level.getAttributes();
154 if (tag.demandType.equals(DemandType.TIMED_FACTORS) || tag.demandType.equals(DemandType.TIMED_FREQUENCIES))
155 {
156 Node timeNode = levelAttributes.getNamedItem("TIME");
157 Throw.when(timeNode == null, XmlParserException.class, "A LEVEL tag is missing attribute TIME.");
158 String timeString = timeNode.getNodeValue().trim();
159 Time time = Try.assign(() -> TimeUnits.parseTime(timeString), XmlParserException.class,
160 "Unable to parse %s as time.", timeString);
161 timeList.add(time.si);
162 }
163 Node valueNode = levelAttributes.getNamedItem("VALUE");
164 Throw.when(valueNode == null, XmlParserException.class, "A LEVEL tag is missing attribute VALUE.");
165 if (tag.demandType.equals(DemandType.TIMED_FACTORS) || tag.demandType.equals(DemandType.FACTORS))
166 {
167 valueList.add(parseFactor(valueNode.getNodeValue().trim()));
168 }
169 if (tag.demandType.equals(DemandType.TIMED_FREQUENCIES) || tag.demandType.equals(DemandType.FREQUENCIES))
170 {
171 String valueString = valueNode.getNodeValue().trim().toLowerCase();
172 valueList.add(Try.assign(() -> DurationUnits.parseFrequency(valueString.replace("veh", "")).getInUnit(UNIT),
173 "Unable to parse %s as frequency.", valueString));
174 }
175 }
176
177 if (tag.demandType.equals(DemandType.TIMED_FACTORS) || tag.demandType.equals(DemandType.TIMED_FREQUENCIES))
178 {
179 tag.timeVector = Try.assign(
180 () -> new TimeVector(timeList.stream().mapToDouble(d -> d).toArray(), TimeUnit.BASE, StorageType.DENSE),
181 "Unexpected exception while converting list of time values to an array.");
182 }
183
184 if (tag.demandType.equals(DemandType.TIMED_FACTORS) || tag.demandType.equals(DemandType.FACTORS))
185 {
186 tag.factors = valueList.stream().mapToDouble(d -> d).toArray();
187 }
188 else if (tag.demandType.equals(DemandType.TIMED_FREQUENCIES) || tag.demandType.equals(DemandType.FREQUENCIES))
189 {
190 tag.demandVector = Try.assign(
191 () -> new FrequencyVector(valueList.stream().mapToDouble(d -> d).toArray(), UNIT, StorageType.DENSE),
192 "Unexpected exception while converting list of time values to an array.");
193 }
194
195 parser.demand.getValue(() -> new LinkedHashSet<>(), tag.origin, tag.destination).add(tag);
196 }
197 }
198
199
200
201
202
203
204
205 public static double parseFactor(final String value) throws XmlParserException
206 {
207 double f;
208 if (value.endsWith("%"))
209 {
210 f = Double.parseDouble(value.substring(0, value.length() - 1).trim()) / 100.0;
211 }
212 else
213 {
214 f = Double.parseDouble(value);
215 }
216 Throw.when(f < 0.0, XmlParserException.class, "Factor %d is not positive.", f);
217 return f;
218 }
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 public enum DemandType
234 {
235
236 FACTOR,
237
238
239 FACTORS,
240
241
242 TIMED_FACTORS,
243
244
245 FREQUENCIES,
246
247
248 TIMED_FREQUENCIES;
249
250
251
252
253
254
255
256 public static DemandType fromLevelNode(final Node level) throws XmlParserException
257 {
258 NamedNodeMap attributes = level.getAttributes();
259 Node timeNode = attributes.getNamedItem("TIME");
260 Node valueNode = attributes.getNamedItem("VALUE");
261 Throw.when(valueNode == null, XmlParserException.class, "LEVEL tag in DEMAND is missing attribute VALUE.");
262 boolean frequency = valueNode.getNodeValue().trim().toLowerCase().contains("veh");
263 if (timeNode != null)
264 {
265 if (frequency)
266 {
267 return TIMED_FREQUENCIES;
268 }
269 return TIMED_FACTORS;
270 }
271 if (frequency)
272 {
273 return FREQUENCIES;
274 }
275 return FACTORS;
276 }
277
278 }
279
280 }