View Javadoc
1   package org.opentrafficsim.road.network.factory.vissim;
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.lane.changing.OvertakingConditions;
21  import org.w3c.dom.NamedNodeMap;
22  import org.w3c.dom.Node;
23  import org.xml.sax.SAXException;
24  
25  /**
26   * CrossSectionElement tags as part of the ROADLAYOUT tag.
27   *
28   * <pre>
29   * {@code
30    <xsd:element name="ROADLAYOUT">
31      <xsd:complexType>
32        <xsd:sequence>
33          ...
34          <xsd:choice minOccurs="1" maxOccurs="unbounded">
35  
36            <xsd:element name="LANE" minOccurs="0" maxOccurs="unbounded">
37              <xsd:complexType>
38                <xsd:sequence minOccurs="0" maxOccurs="unbounded">
39                  <xsd:element name="SPEEDLIMIT" minOccurs="1" maxOccurs="unbounded">
40                    <xsd:complexType>
41                      <xsd:attribute name="GTUTYPE" type="xsd:string" use="required" />
42                      <xsd:attribute name="LEGALSPEEDLIMIT" type="SPEEDTYPE" use="optional" />
43                    </xsd:complexType>
44                  </xsd:element>
45                </xsd:sequence>
46                <xsd:attribute name="NAME" type="xsd:string" use="required" />
47                <xsd:attribute name="LANETYPE" type="xsd:string" use="optional" />
48                <xsd:attribute name="OFFSET" type="SIGNEDLENGTHTYPE" use="required" />
49                <xsd:attribute name="WIDTH" type="LENGTHTYPE" use="optional" />
50                <xsd:attribute name="DIRECTION" type="DIRECTIONTYPE" use="required" />
51                <xsd:attribute name="COLOR" type="COLORTYPE" use="optional" />
52                <xsd:attribute name="OVERTAKING" type="OVERTAKINGTYPE" use="optional" />
53              </xsd:complexType>
54            </xsd:element>
55  
56            <xsd:element name="NOTRAFFICLANE" minOccurs="0" maxOccurs="unbounded">
57              <xsd:complexType>
58                <xsd:attribute name="NAME" type="xsd:string" use="optional" />
59                <xsd:attribute name="OFFSET" type="SIGNEDLENGTHTYPE" use="required" />
60                <xsd:attribute name="WIDTH" type="LENGTHTYPE" use="optional" />
61                <xsd:attribute name="COLOR" type="COLORTYPE" use="optional" />
62              </xsd:complexType>
63            </xsd:element>
64  
65            <xsd:element name="SHOULDER" minOccurs="0" maxOccurs="unbounded">
66              <xsd:complexType>
67                <xsd:attribute name="NAME" type="xsd:string" use="optional" />
68                <xsd:attribute name="OFFSET" type="SIGNEDLENGTHTYPE" use="required" />
69                <xsd:attribute name="WIDTH" type="LENGTHTYPE" use="optional" />
70                <xsd:attribute name="COLOR" type="COLORTYPE" use="optional" />
71              </xsd:complexType>
72            </xsd:element>
73  
74            <xsd:element name="STRIPE" minOccurs="0" maxOccurs="unbounded">
75              <xsd:complexType>
76                <xsd:attribute name="NAME" type="xsd:string" use="optional" />
77                <xsd:attribute name="TYPE" type="STRIPETYPE" use="required" />
78                <xsd:attribute name="OFFSET" type="SIGNEDLENGTHTYPE" use="required" />
79                <xsd:attribute name="WIDTH" type="LENGTHTYPE" use="optional" />
80                <xsd:attribute name="COLOR" type="COLORTYPE" use="optional" />
81              </xsd:complexType>
82            </xsd:element>
83  
84          </xsd:choice>
85        </xsd:sequence>
86        ...
87      </xsd:complexType>
88    </xsd:element>
89   * }
90   * </pre>
91   * <p>
92   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
93   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
94   * <p>
95   * $LastChangedDate: 2019-03-16 19:17:15 +0100 (Sat, 16 Mar 2019) $, @version $Revision: 5157 $, by $Author: averbraeck $,
96   * initial version Jul 23, 2015 <br>
97   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
98   */
99  class CrossSectionElementTag implements Serializable
100 {
101     /** */
102     private static final long serialVersionUID = 20150723L;
103 
104     /** Element types. */
105     @SuppressWarnings({"javadoc", "checkstyle:javadocvariable"})
106     enum ElementType
107     {
108         LANE,
109         NOTRAFFICLANE,
110         SHOULDER,
111         STRIPE
112     };
113 
114     /** Stripe types. */
115     @SuppressWarnings({"javadoc", "checkstyle:javadocvariable"})
116     enum StripeType
117     {
118         SOLID,
119         DASHED,
120         BLOCKED,
121         DOUBLE,
122         LEFTONLY,
123         RIGHTONLY
124     };
125 
126     /** Type. */
127     @SuppressWarnings("checkstyle:visibilitymodifier")
128     ElementType elementType = null;
129 
130     /** Name. */
131     @SuppressWarnings("checkstyle:visibilitymodifier")
132     String name = null;
133 
134     /** Lane type in case elementType is a LANE. */
135     @SuppressWarnings("checkstyle:visibilitymodifier")
136     LaneTypeTag laneTypeTag = null;
137 
138     /** Stripe type. */
139     @SuppressWarnings("checkstyle:visibilitymodifier")
140     StripeType stripeType = null;
141 
142     /** Offset. */
143     @SuppressWarnings("checkstyle:visibilitymodifier")
144     Length offset = null;
145 
146     /** Speed limits. */
147     @SuppressWarnings("checkstyle:visibilitymodifier")
148     Map<GTUType, Speed> legalSpeedLimits = null;
149 
150     /** Lane width. */
151     @SuppressWarnings("checkstyle:visibilitymodifier")
152     Length width = null;
153 
154     /** Direction. */
155     @SuppressWarnings("checkstyle:visibilitymodifier")
156     LongitudinalDirectionality direction;
157 
158     /** Animation color. */
159     @SuppressWarnings("checkstyle:visibilitymodifier")
160     Color color;
161 
162     /** Overtaking conditions. */
163     @SuppressWarnings("checkstyle:visibilitymodifier")
164     OvertakingConditions overtakingConditions = null;
165 
166     /**
167      * Parse the ROADLAYOUT.LANE tag.
168      * @param node Node; the node of the XML-file
169      * @param parser VissimNetworkLaneParser; the parser with the lists of information
170      * @param roadLayoutTag RoadLayoutTag; the tag with the enclosing information
171      * @return the cross section element for this part of the road
172      * @throws SAXException when parsing of the tag fails
173      * @throws NetworkException when parsing of the tag fails
174      */
175     @SuppressWarnings("checkstyle:needbraces")
176     static CrossSectionElementTag parseLane(final Node node, final VissimNetworkLaneParser parser,
177             final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
178     {
179         NamedNodeMap attributes = node.getAttributes();
180         CrossSectionElementTag cseTag = new CrossSectionElementTag();
181 
182         if (attributes.getNamedItem("NAME") == null)
183         {
184             throw new SAXException("ROADLAYOUT.LANE: missing attribute NAME for ROADLAYOUT " + roadLayoutTag.name);
185         }
186         String name = attributes.getNamedItem("NAME").getNodeValue().trim();
187         if (roadLayoutTag.cseTags.containsKey(name))
188         {
189             throw new SAXException("ROADLAYOUT.LANE: LANE NAME " + name + " defined twice");
190         }
191         cseTag.name = name;
192 
193         cseTag.elementType = ElementType.LANE;
194 
195         if (attributes.getNamedItem("LANETYPE") != null)
196         {
197             String laneTypeString = attributes.getNamedItem("LANETYPE").getNodeValue().trim();
198             if (!parser.getLaneTypeTags().containsKey(laneTypeString))
199             {
200                 throw new SAXException("ROADLAYOUT.LANE: LANETYPE " + laneTypeString + " for lane " + roadLayoutTag.name + "."
201                         + name + " not defined");
202             }
203             cseTag.laneTypeTag = parser.getLaneTypeTags().get(laneTypeString);
204         }
205 
206         if (attributes.getNamedItem("OFFSET") != null)
207         {
208             cseTag.offset = LengthUnits.parseLength(attributes.getNamedItem("OFFSET").getNodeValue());
209         }
210         else
211         {
212             throw new SAXException("ROADLAYOUT.LANE: missing attribute OFFSET for lane " + roadLayoutTag.name + "." + name);
213         }
214 
215         if (attributes.getNamedItem("WIDTH") != null)
216         {
217             cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
218         }
219         else if (roadLayoutTag.defaultLaneWidth != null)
220         {
221             cseTag.width = roadLayoutTag.defaultLaneWidth;
222         }
223         else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
224         {
225             cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
226         }
227         else
228         {
229             throw new SAXException("ROADLAYOUT.LANE: cannot determine WIDTH for lane: " + roadLayoutTag.name + "." + name);
230         }
231 
232         List<Node> speedLimitList = XMLParser.getNodes(node.getChildNodes(), "SPEEDLIMIT");
233         if (speedLimitList.size() > 0)
234         {
235             cseTag.legalSpeedLimits = new LinkedHashMap<>();
236         }
237         for (Node speedLimitNode : speedLimitList)
238         {
239             NamedNodeMap speedLimitAttributes = speedLimitNode.getAttributes();
240 
241             Node gtuTypeName = speedLimitAttributes.getNamedItem("GTUTYPE");
242             if (gtuTypeName == null)
243             {
244                 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: No GTUTYPE defined");
245             }
246             if (!parser.getGtuTypes().containsKey(gtuTypeName.getNodeValue().trim()))
247             {
248                 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: " + roadLayoutTag.name + " GTUTYPE "
249                         + gtuTypeName.getNodeValue().trim() + " not defined");
250             }
251             GTUType gtuType = parser.getGtuTypes().get(gtuTypeName.getNodeValue().trim());
252 
253             Node speedNode = speedLimitAttributes.getNamedItem("LEGALSPEEDLIMIT");
254             if (speedNode == null)
255             {
256                 throw new NetworkException("ROADLAYOUT.LANE.SPEEDLIMIT: " + roadLayoutTag.name + " GTUTYPE " + gtuType.getId()
257                         + ": LEGALSPEEDLIMIT not defined");
258             }
259             Speed speed = SpeedUnits.parseSpeed(speedNode.getNodeValue().trim());
260 
261             cseTag.legalSpeedLimits.put(gtuType, speed);
262         }
263 
264         if (cseTag.legalSpeedLimits == null)
265         {
266             if (cseTag.laneTypeTag != null && cseTag.laneTypeTag.legalSpeedLimits != null)
267             {
268                 cseTag.legalSpeedLimits = new LinkedHashMap<>(cseTag.laneTypeTag.legalSpeedLimits);
269             }
270             else if (roadLayoutTag.legalSpeedLimits != null)
271             {
272                 cseTag.legalSpeedLimits = new LinkedHashMap<>(roadLayoutTag.legalSpeedLimits);
273             }
274             else if (roadLayoutTag.roadTypeTag.legalSpeedLimits != null)
275             {
276                 cseTag.legalSpeedLimits = new LinkedHashMap<>(roadLayoutTag.roadTypeTag.legalSpeedLimits);
277             }
278             else
279             {
280                 throw new SAXException("ROADLAYOUT.LANE: cannot determine SPEED for lane: " + roadLayoutTag.name + "." + name);
281             }
282         }
283 
284         if (attributes.getNamedItem("DIRECTION") == null)
285         {
286             throw new SAXException("ROADLAYOUT.LANE: missing attribute DIRECTION for lane " + roadLayoutTag.name + "." + name);
287         }
288         cseTag.direction = Directions.parseDirection(attributes.getNamedItem("DIRECTION").getNodeValue());
289 
290         if (attributes.getNamedItem("COLOR") != null)
291         {
292             cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
293         }
294         else
295         {
296             cseTag.color = Color.LIGHT_GRAY;
297         }
298 
299         Node oc = attributes.getNamedItem("OVERTAKING");
300         if (oc != null)
301         {
302             // cseTag.overtakingConditions = LaneAttributes.parseOvertakingConditions(oc.getNodeValue().trim(), parser);
303         }
304         else if (roadLayoutTag.overtakingConditions != null)
305         {
306             cseTag.overtakingConditions = roadLayoutTag.overtakingConditions;
307         }
308         else if (roadLayoutTag.roadTypeTag.defaultOvertakingConditions != null)
309         {
310             cseTag.overtakingConditions = roadLayoutTag.roadTypeTag.defaultOvertakingConditions;
311         }
312         else
313         {
314             throw new SAXException("ROADLAYOUT.LANE: cannot determine OVERTAKING for lane: " + roadLayoutTag.name + "." + name);
315         }
316 
317         roadLayoutTag.cseTags.put(cseTag.name, cseTag);
318         return cseTag;
319     }
320 
321     /**
322      * Parse the ROADLAYOUT.NOTRAFFICLANE tag.
323      * @param node Node; the node of the XML-file
324      * @param parser VissimNetworkLaneParser; the parser with the lists of information
325      * @param roadLayoutTag RoadLayoutTag; the tag with the enclosing information
326      * @return the cross section element for this part of the road
327      * @throws SAXException when parsing of the tag fails
328      * @throws NetworkException when parsing of the tag fails
329      */
330     @SuppressWarnings("checkstyle:needbraces")
331     static CrossSectionElementTag parseNoTrafficLane(final Node node, final VissimNetworkLaneParser parser,
332             final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
333     {
334         NamedNodeMap attributes = node.getAttributes();
335         CrossSectionElementTag cseTag = new CrossSectionElementTag();
336 
337         String name;
338         if (attributes.getNamedItem("NAME") != null)
339         {
340             name = attributes.getNamedItem("NAME").getNodeValue().trim();
341         }
342         else
343         {
344             name = UUID.randomUUID().toString();
345         }
346         if (roadLayoutTag.cseTags.containsKey(name))
347         {
348             throw new SAXException("ROADLAYOUT.NOTRAFFICLANE: LANE NAME " + name + " defined twice");
349         }
350         cseTag.name = name;
351 
352         cseTag.elementType = ElementType.NOTRAFFICLANE;
353 
354         if (attributes.getNamedItem("OFFSET") != null)
355         {
356             cseTag.offset = LengthUnits.parseLength(attributes.getNamedItem("OFFSET").getNodeValue());
357         }
358         else
359         {
360             throw new SAXException("ROADLAYOUT.LANE: missing attribute OFFSET for lane " + roadLayoutTag.name + "." + name);
361         }
362 
363         if (attributes.getNamedItem("WIDTH") != null)
364         {
365             cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
366         }
367         else if (roadLayoutTag.defaultLaneWidth != null)
368         {
369             cseTag.width = roadLayoutTag.defaultLaneWidth;
370         }
371         else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
372         {
373             cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
374         }
375         else
376         {
377             throw new SAXException(
378                     "ROADLAYOUT.NOTRAFFICLANE: cannot determine WIDTH for NOTRAFFICLANE: " + roadLayoutTag.name + "." + name);
379         }
380 
381         if (attributes.getNamedItem("COLOR") != null)
382         {
383             cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
384         }
385         else
386         {
387             cseTag.color = Color.GRAY;
388         }
389 
390         roadLayoutTag.cseTags.put(cseTag.name, cseTag);
391         return cseTag;
392     }
393 
394     /**
395      * Parse the ROADLAYOUT.SHOULDER tag.
396      * @param node Node; the node of the XML-file
397      * @param parser VissimNetworkLaneParser; the parser with the lists of information
398      * @param roadLayoutTag RoadLayoutTag; the tag with the enclosing information
399      * @return the cross section element for this part of the road
400      * @throws SAXException when parsing of the tag fails
401      * @throws NetworkException when parsing of the tag fails
402      */
403     @SuppressWarnings("checkstyle:needbraces")
404     static CrossSectionElementTag parseShoulder(final Node node, final VissimNetworkLaneParser parser,
405             final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
406     {
407         NamedNodeMap attributes = node.getAttributes();
408         CrossSectionElementTag cseTag = new CrossSectionElementTag();
409 
410         String name;
411         if (attributes.getNamedItem("NAME") != null)
412         {
413             name = attributes.getNamedItem("NAME").getNodeValue().trim();
414         }
415         else
416         {
417             name = UUID.randomUUID().toString();
418         }
419         if (roadLayoutTag.cseTags.containsKey(name))
420         {
421             throw new SAXException("ROADLAYOUT.SHOULDER: LANE NAME " + name + " defined twice");
422         }
423         cseTag.name = name;
424 
425         cseTag.elementType = ElementType.SHOULDER;
426 
427         if (attributes.getNamedItem("OFFSET") != null)
428         {
429             cseTag.offset = LengthUnits.parseLength(attributes.getNamedItem("OFFSET").getNodeValue());
430         }
431         else
432         {
433             throw new SAXException("ROADLAYOUT.LANE: missing attribute OFFSET for lane " + roadLayoutTag.name + "." + name);
434         }
435 
436         if (attributes.getNamedItem("WIDTH") != null)
437         {
438             cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
439         }
440         else if (roadLayoutTag.defaultLaneWidth != null)
441         {
442             cseTag.width = roadLayoutTag.defaultLaneWidth;
443         }
444         else if (roadLayoutTag.roadTypeTag.defaultLaneWidth != null)
445         {
446             cseTag.width = roadLayoutTag.roadTypeTag.defaultLaneWidth;
447         }
448         else
449         {
450             throw new SAXException(
451                     "ROADLAYOUT.SHOULDER: cannot determine WIDTH for NOTRAFFICLANE: " + roadLayoutTag.name + "." + name);
452         }
453 
454         if (attributes.getNamedItem("COLOR") != null)
455         {
456             cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
457         }
458         else
459         {
460             cseTag.color = Color.GREEN;
461         }
462 
463         roadLayoutTag.cseTags.put(cseTag.name, cseTag);
464         return cseTag;
465     }
466 
467     /**
468      * Parse the ROADLAYOUT.STRIPE tag.
469      * @param node Node; the node of the XML-file
470      * @param parser VissimNetworkLaneParser; the parser with the lists of information
471      * @param roadLayoutTag RoadLayoutTag; the tag with the enclosing information
472      * @return the cross section element for this part of the road
473      * @throws SAXException when parsing of the tag fails
474      * @throws NetworkException when parsing of the tag fails
475      */
476     @SuppressWarnings("checkstyle:needbraces")
477     static CrossSectionElementTag parseStripe(final Node node, final VissimNetworkLaneParser parser,
478             final RoadLayoutTag roadLayoutTag) throws SAXException, NetworkException
479     {
480         NamedNodeMap attributes = node.getAttributes();
481         CrossSectionElementTag cseTag = new CrossSectionElementTag();
482 
483         String name;
484         if (attributes.getNamedItem("NAME") != null)
485         {
486             name = attributes.getNamedItem("NAME").getNodeValue().trim();
487         }
488         else
489         {
490             name = UUID.randomUUID().toString();
491         }
492         if (roadLayoutTag.cseTags.containsKey(name))
493         {
494             throw new SAXException("ROADLAYOUT.STRIPE: LANE NAME " + name + " defined twice");
495         }
496         cseTag.name = name;
497 
498         cseTag.elementType = ElementType.STRIPE;
499 
500         if (attributes.getNamedItem("TYPE") != null)
501         {
502             cseTag.stripeType = parseStripeType(attributes.getNamedItem("TYPE").getNodeValue());
503         }
504 
505         if (attributes.getNamedItem("OFFSET") != null)
506         {
507             cseTag.offset = LengthUnits.parseLength(attributes.getNamedItem("OFFSET").getNodeValue());
508         }
509         else
510         {
511             throw new SAXException("ROADLAYOUT.LANE: missing attribute OFFSET for lane " + roadLayoutTag.name + "." + name);
512         }
513 
514         if (attributes.getNamedItem("WIDTH") != null)
515         {
516             cseTag.width = LengthUnits.parseLength(attributes.getNamedItem("WIDTH").getNodeValue());
517         }
518         else
519         {
520             cseTag.width = new Length(0.2, LengthUnit.METER);
521         }
522 
523         if (attributes.getNamedItem("COLOR") != null)
524         {
525             cseTag.color = Colors.parseColor(attributes.getNamedItem("COLOR").getNodeValue());
526         }
527         else
528         {
529             cseTag.color = Color.WHITE;
530         }
531 
532         roadLayoutTag.cseTags.put(cseTag.name, cseTag);
533         return cseTag;
534     }
535 
536     /**
537      * @param stripeStr String; the stripe string.
538      * @return the stripe type.
539      * @throws NetworkException in case of unknown model.
540      */
541     private static StripeType parseStripeType(final String stripeStr) throws NetworkException
542     {
543         if (stripeStr.equals("SOLID"))
544         {
545             return StripeType.SOLID;
546         }
547         else if (stripeStr.equals("DASHED"))
548         {
549             return StripeType.DASHED;
550         }
551         else if (stripeStr.equals("BLOCKED"))
552         {
553             return StripeType.BLOCKED;
554         }
555         else if (stripeStr.equals("DOUBLE"))
556         {
557             return StripeType.DOUBLE;
558         }
559         else if (stripeStr.equals("LEFTONLY"))
560         {
561             return StripeType.LEFTONLY;
562         }
563         else if (stripeStr.equals("RIGHTONLY"))
564         {
565             return StripeType.RIGHTONLY;
566         }
567         throw new NetworkException("Unknown stripe type: " + stripeStr);
568     }
569 
570     /** {@inheritDoc} */
571     @Override
572     public String toString()
573     {
574         return "CrossSectionElementTag [elementType=" + this.elementType + ", name=" + this.name + ", laneTypeTag="
575                 + this.laneTypeTag + ", stripeType=" + this.stripeType + ", offset=" + this.offset + ", legalSpeedLimits="
576                 + this.legalSpeedLimits + ", width=" + this.width + ", direction=" + this.direction + ", color=" + this.color
577                 + ", overtakingConditions=" + this.overtakingConditions + "]";
578     }
579 
580 }