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