1 package org.opentrafficsim.road.network.factory.xml;
2
3 import java.awt.Color;
4 import java.io.Serializable;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.UUID;
9
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.djunits.value.vdouble.scalar.Speed;
13 import org.opentrafficsim.core.gtu.GTUType;
14 import org.opentrafficsim.core.network.LongitudinalDirectionality;
15 import org.opentrafficsim.core.network.NetworkException;
16 import org.opentrafficsim.core.network.factory.xml.units.Colors;
17 import org.opentrafficsim.core.network.factory.xml.units.Directions;
18 import org.opentrafficsim.core.network.factory.xml.units.LengthUnits;
19 import org.opentrafficsim.core.network.factory.xml.units.SpeedUnits;
20 import org.opentrafficsim.road.network.factory.xml.units.LaneAttributes;
21 import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
22 import org.w3c.dom.DOMException;
23 import org.w3c.dom.NamedNodeMap;
24 import org.w3c.dom.Node;
25 import org.xml.sax.SAXException;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 class CrossSectionElementTag implements Serializable
102 {
103
104 private static final long serialVersionUID = 20150723L;
105
106
107 @SuppressWarnings({ "javadoc", "checkstyle:javadocvariable" })
108 enum ElementType
109 {
110 LANE,
111 NOTRAFFICLANE,
112 SHOULDER,
113 STRIPE
114 };
115
116
117 @SuppressWarnings({ "javadoc", "checkstyle:javadocvariable" })
118 enum StripeType
119 {
120 SOLID,
121 DASHED,
122 BLOCKED,
123 DOUBLE,
124 LEFTONLY,
125 RIGHTONLY
126 };
127
128
129 @SuppressWarnings("checkstyle:visibilitymodifier")
130 ElementType elementType = null;
131
132
133 @SuppressWarnings("checkstyle:visibilitymodifier")
134 String name = null;
135
136
137 @SuppressWarnings("checkstyle:visibilitymodifier")
138 LaneTypeTag laneTypeTag = null;
139
140
141 @SuppressWarnings("checkstyle:visibilitymodifier")
142 StripeType stripeType = null;
143
144
145 @SuppressWarnings("checkstyle:visibilitymodifier")
146 Length offset = null;
147
148
149 @SuppressWarnings("checkstyle:visibilitymodifier")
150 Length offSetStart = null;
151
152
153 @SuppressWarnings("checkstyle:visibilitymodifier")
154 Length offSetEnd = null;
155
156
157 @SuppressWarnings("checkstyle:visibilitymodifier")
158 Map<GTUType, Speed> legalSpeedLimits = null;
159
160
161 @SuppressWarnings("checkstyle:visibilitymodifier")
162 Length width = null;
163
164
165 @SuppressWarnings("checkstyle:visibilitymodifier")
166 LongitudinalDirectionality direction;
167
168
169 @SuppressWarnings("checkstyle:visibilitymodifier")
170 Color color;
171
172
173 @SuppressWarnings("checkstyle:visibilitymodifier")
174 OvertakingConditions overtakingConditions = null;
175
176
177
178
179
180
181
182
183
184
185 @SuppressWarnings("checkstyle:needbraces")
186 static CrossSectionElementTag parseLane(final Node node, final XmlNetworkLaneParser parser,
187 final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
188 {
189 NamedNodeMap attributes = node.getAttributes();
190 CrossSectionElementTag cseTag = new CrossSectionElementTag();
191
192 if (attributes.getNamedItem("NAME") == null)
193 throw new SAXException("ROADLAYOUT.LANE: missing attribute NAME for ROADLAYOUT " + roadLayoutTag.name);
194 String name = attributes.getNamedItem("NAME").getNodeValue().trim();
195 if (roadLayoutTag.cseTags.containsKey(name))
196 throw new SAXException("ROADLAYOUT.LANE: LANE NAME " + name + " defined twice");
197 cseTag.name = name;
198
199 cseTag.elementType = ElementType.LANE;
200
201 if (attributes.getNamedItem("LANETYPE") != null)
202 {
203 String laneTypeString = attributes.getNamedItem("LANETYPE").getNodeValue().trim();
204 if (!parser.laneTypeTags.containsKey(laneTypeString))
205 throw new SAXException("ROADLAYOUT.LANE: LANETYPE " + laneTypeString + " for lane " + roadLayoutTag.name + "."
206 + name + " not defined");
207 cseTag.laneTypeTag = parser.laneTypeTags.get(laneTypeString);
208 }
209
210
211
212
213
214 parseOffset(cseTag, attributes, roadLayoutTag);
215
216 if (attributes.getNamedItem("WIDTH") != null)
217 cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
218 else if (roadLayoutTag.defaultLaneWidth != null)
219 cseTag.width = roadLayoutTag.defaultLaneWidth;
220 else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
221 cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
222 else
223 throw new SAXException("ROADLAYOUT.LANE: cannot determine WIDTH for lane: " + roadLayoutTag.name + "." + name);
224
225 List<Node> speedLimitList = XMLParser.getNodes(node.getChildNodes(), "SPEEDLIMIT");
226 if (speedLimitList.size() > 0)
227 cseTag.legalSpeedLimits = new LinkedHashMap<>();
228 for (Node speedLimitNode : speedLimitList)
229 {
230 NamedNodeMap speedLimitAttributes = speedLimitNode.getAttributes();
231
232 Node gtuTypeName = speedLimitAttributes.getNamedItem("GTUTYPE");
233 if (gtuTypeName == null)
234 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: No GTUTYPE defined");
235 if (!parser.gtuTypes.containsKey(gtuTypeName.getNodeValue().trim()))
236 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: " + roadLayoutTag.name + " GTUTYPE "
237 + gtuTypeName.getNodeValue().trim() + " not defined");
238 GTUType gtuType = parser.gtuTypes.get(gtuTypeName.getNodeValue().trim());
239
240 Node speedNode = speedLimitAttributes.getNamedItem("LEGALSPEEDLIMIT");
241 if (speedNode == null)
242 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: " + roadLayoutTag.name + " GTUTYPE " + gtuType.getId()
243 + ": LEGALSPEEDLIMIT not defined");
244 Speed speed = SpeedUnits.parseSpeed(speedNode.getNodeValue().trim());
245
246 cseTag.legalSpeedLimits.put(gtuType, speed);
247 }
248
249 if (cseTag.legalSpeedLimits == null)
250 {
251 if (cseTag.laneTypeTag != null && cseTag.laneTypeTag.legalSpeedLimits != null)
252 cseTag.legalSpeedLimits = new LinkedHashMap<>(cseTag.laneTypeTag.legalSpeedLimits);
253 else if (roadLayoutTag.legalSpeedLimits != null)
254 cseTag.legalSpeedLimits = new LinkedHashMap<>(roadLayoutTag.legalSpeedLimits);
255 else if (roadLayoutTag.roadTypeTag.legalSpeedLimits != null)
256 cseTag.legalSpeedLimits = new LinkedHashMap<>(roadLayoutTag.roadTypeTag.legalSpeedLimits);
257 else
258 throw new SAXException("ROADLAYOUT.LANE: cannot determine SPEED for lane: " + roadLayoutTag.name + "." + name);
259 }
260
261 if (attributes.getNamedItem("DIRECTION") == null)
262 throw new SAXException("ROADLAYOUT.LANE: missing attribute DIRECTION for lane " + roadLayoutTag.name + "." + name);
263 cseTag.direction = Directions.parseDirection(attributes.getNamedItem("DIRECTION").getNodeValue());
264
265 if (attributes.getNamedItem("COLOR") != null)
266 cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
267 else
268 cseTag.color = Color.LIGHT_GRAY;
269
270 Node oc = attributes.getNamedItem("OVERTAKING");
271 if (oc != null)
272 cseTag.overtakingConditions = LaneAttributes.parseOvertakingConditions(oc.getNodeValue().trim(), parser);
273 else if (roadLayoutTag.overtakingConditions != null)
274 cseTag.overtakingConditions = roadLayoutTag.overtakingConditions;
275 else if (roadLayoutTag.roadTypeTag.defaultOvertakingConditions != null)
276 cseTag.overtakingConditions = roadLayoutTag.roadTypeTag.defaultOvertakingConditions;
277 else
278 throw new SAXException("ROADLAYOUT.LANE: cannot determine OVERTAKING for lane: " + roadLayoutTag.name + "." + name);
279
280 roadLayoutTag.cseTags.put(cseTag.name, cseTag);
281 return cseTag;
282 }
283
284
285
286
287
288
289
290
291
292
293 @SuppressWarnings("checkstyle:needbraces")
294 static CrossSectionElementTag parseNoTrafficLane(final Node node, final XmlNetworkLaneParser parser,
295 final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
296 {
297 NamedNodeMap attributes = node.getAttributes();
298 CrossSectionElementTag cseTag = new CrossSectionElementTag();
299
300 String name;
301 if (attributes.getNamedItem("NAME") != null)
302 name = attributes.getNamedItem("NAME").getNodeValue().trim();
303 else
304 name = UUID.randomUUID().toString();
305 if (roadLayoutTag.cseTags.containsKey(name))
306 throw new SAXException("ROADLAYOUT.NOTRAFFICLANE: LANE NAME " + name + " defined twice");
307 cseTag.name = name;
308
309 cseTag.elementType = ElementType.NOTRAFFICLANE;
310
311
312
313
314
315 parseOffset(cseTag, attributes, roadLayoutTag);
316
317 if (attributes.getNamedItem("WIDTH") != null)
318 cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
319 else if (roadLayoutTag.defaultLaneWidth != null)
320 cseTag.width = roadLayoutTag.defaultLaneWidth;
321 else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
322 cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
323 else
324 throw new SAXException(
325 "ROADLAYOUT.NOTRAFFICLANE: cannot determine WIDTH for NOTRAFFICLANE: " + roadLayoutTag.name + "." + name);
326
327 if (attributes.getNamedItem("COLOR") != null)
328 cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
329 else
330 cseTag.color = Color.GRAY;
331
332 roadLayoutTag.cseTags.put(cseTag.name, cseTag);
333 return cseTag;
334 }
335
336
337
338
339
340
341
342
343
344
345 @SuppressWarnings("checkstyle:needbraces")
346 static CrossSectionElementTag parseShoulder(final Node node, final XmlNetworkLaneParser parser,
347 final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
348 {
349 NamedNodeMap attributes = node.getAttributes();
350 CrossSectionElementTag cseTag = new CrossSectionElementTag();
351
352 String name;
353 if (attributes.getNamedItem("NAME") != null)
354 name = attributes.getNamedItem("NAME").getNodeValue().trim();
355 else
356 name = UUID.randomUUID().toString();
357 if (roadLayoutTag.cseTags.containsKey(name))
358 throw new SAXException("ROADLAYOUT.SHOULDER: LANE NAME " + name + " defined twice");
359 cseTag.name = name;
360
361 cseTag.elementType = ElementType.SHOULDER;
362
363
364
365
366
367 parseOffset(cseTag, attributes, roadLayoutTag);
368
369 if (attributes.getNamedItem("WIDTH") != null)
370 cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
371 else if (roadLayoutTag.defaultLaneWidth != null)
372 cseTag.width = roadLayoutTag.defaultLaneWidth;
373 else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
374 cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
375 else
376 throw new SAXException(
377 "ROADLAYOUT.SHOULDER: cannot determine WIDTH for NOTRAFFICLANE: " + roadLayoutTag.name + "." + name);
378
379 if (attributes.getNamedItem("COLOR") != null)
380 cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
381 else
382 cseTag.color = Color.GREEN;
383
384 roadLayoutTag.cseTags.put(cseTag.name, cseTag);
385 return cseTag;
386 }
387
388
389
390
391
392
393
394
395
396
397 @SuppressWarnings("checkstyle:needbraces")
398 static CrossSectionElementTag parseStripe(final Node node, final XmlNetworkLaneParser parser,
399 final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
400 {
401 NamedNodeMap attributes = node.getAttributes();
402 CrossSectionElementTag cseTag = new CrossSectionElementTag();
403
404 String name;
405 if (attributes.getNamedItem("NAME") != null)
406 name = attributes.getNamedItem("NAME").getNodeValue().trim();
407 else
408 name = UUID.randomUUID().toString();
409 if (roadLayoutTag.cseTags.containsKey(name))
410 throw new SAXException("ROADLAYOUT.STRIPE: LANE NAME " + name + " defined twice");
411 cseTag.name = name;
412
413 cseTag.elementType = ElementType.STRIPE;
414
415 if (attributes.getNamedItem("TYPE") != null)
416 cseTag.stripeType = parseStripeType(attributes.getNamedItem("TYPE").getNodeValue());
417
418
419
420
421
422 parseOffset(cseTag, attributes, roadLayoutTag);
423
424 if (attributes.getNamedItem("WIDTH") != null)
425 cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
426 else
427 cseTag.width = new Length(0.2, LengthUnit.METER);
428
429 if (attributes.getNamedItem("COLOR") != null)
430 cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
431 else
432 cseTag.color = Color.WHITE;
433
434 roadLayoutTag.cseTags.put(cseTag.name, cseTag);
435 return cseTag;
436 }
437
438
439
440
441
442
443 private static StripeType parseStripeType(final String stripeStr) throws NetworkException
444 {
445 if (stripeStr.equals("SOLID"))
446 {
447 return StripeType.SOLID;
448 }
449 else if (stripeStr.equals("DASHED"))
450 {
451 return StripeType.DASHED;
452 }
453 else if (stripeStr.equals("BLOCKED"))
454 {
455 return StripeType.BLOCKED;
456 }
457 else if (stripeStr.equals("DOUBLE"))
458 {
459 return StripeType.DOUBLE;
460 }
461 else if (stripeStr.equals("LEFTONLY"))
462 {
463 return StripeType.LEFTONLY;
464 }
465 else if (stripeStr.equals("RIGHTONLY"))
466 {
467 return StripeType.RIGHTONLY;
468 }
469 throw new NetworkException("Unknown stripe type: " + stripeStr);
470 }
471
472
473
474
475
476
477
478
479
480 private static void parseOffset(final CrossSectionElementTag cseTag, final NamedNodeMap attributes,
481 final RoadLayoutTag roadLayoutTag) throws SAXException, DOMException, NetworkException
482 {
483 if (attributes.getNamedItem("OFFSET") != null)
484 cseTag.offset = LengthUnits.parseLength(attributes.getNamedItem("OFFSET").getNodeValue());
485
486 if (attributes.getNamedItem("OFFSETSTART") != null)
487 cseTag.offSetStart = LengthUnits.parseLength(attributes.getNamedItem("OFFSETSTART").getNodeValue().trim());
488
489 if (attributes.getNamedItem("OFFSETEND") != null)
490 cseTag.offSetEnd = LengthUnits.parseLength(attributes.getNamedItem("OFFSETEND").getNodeValue().trim());
491
492 if ((cseTag.offset == null && (cseTag.offSetStart == null || cseTag.offSetEnd == null))
493 || (cseTag.offset != null && (cseTag.offSetStart != null || cseTag.offSetEnd != null)))
494 {
495 String namedPart = attributes.getNamedItem("NAME") == null ? "on " + roadLayoutTag.name
496 : roadLayoutTag.name + "." + cseTag.name;
497 throw new SAXException("ROADLAYOUT." + cseTag.elementType
498 + ": missing attribute OFFSET or both STARTOFFSET and ENDOFFSET for cross-section element " + namedPart);
499 }
500 }
501
502
503 @Override
504 public String toString()
505 {
506 return "CrossSectionElementTag [elementType=" + this.elementType + ", name=" + this.name + ", laneTypeTag="
507 + this.laneTypeTag + ", stripeType=" + this.stripeType + ", offset=" + this.offset + ", legalSpeedLimits="
508 + this.legalSpeedLimits + ", width=" + this.width + ", direction=" + this.direction + ", color=" + this.color
509 + ", overtakingConditions=" + this.overtakingConditions + "]";
510 }
511
512 }