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