View Javadoc
1   package org.opentrafficsim.road.network.factory.xml;
2   
3   import java.io.Serializable;
4   import java.util.HashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.djunits.unit.LengthUnit;
9   import org.djunits.value.vdouble.scalar.Angle;
10  import org.djunits.value.vdouble.scalar.Length;
11  import org.opentrafficsim.core.network.NetworkException;
12  import org.opentrafficsim.core.network.factory.xml.units.AngleUnits;
13  import org.opentrafficsim.core.network.factory.xml.units.LengthUnits;
14  import org.opentrafficsim.road.network.factory.xml.units.LaneAttributes;
15  import org.opentrafficsim.road.network.lane.CrossSectionElement;
16  import org.opentrafficsim.road.network.lane.CrossSectionLink;
17  import org.opentrafficsim.road.network.lane.CrossSectionLink.Priority;
18  import org.opentrafficsim.road.network.lane.Lane;
19  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
20  import org.w3c.dom.NamedNodeMap;
21  import org.w3c.dom.Node;
22  import org.w3c.dom.NodeList;
23  import org.xml.sax.SAXException;
24  
25  /**
26   * <p>
27   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
29   * <p>
30   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
31   * initial version Jul 23, 2015 <br>
32   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
33   */
34  final class LinkTag implements Serializable
35  {
36      /** */
37      private static final long serialVersionUID = 20150723L;
38  
39      /** Name. */
40      @SuppressWarnings("checkstyle:visibilitymodifier")
41      String name = null;
42  
43      /** From node tag. */
44      @SuppressWarnings("checkstyle:visibilitymodifier")
45      NodeTag nodeStartTag = null;
46  
47      /** To node tag. */
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49      NodeTag nodeEndTag = null;
50  
51      /** Road type. */
52      @SuppressWarnings("checkstyle:visibilitymodifier")
53      RoadLayoutTag roadLayoutTag = null;
54  
55      /** Offset for the link at the start node. */
56      @SuppressWarnings("checkstyle:visibilitymodifier")
57      Length offsetStart = null;
58  
59      /** Offset for the link at the end node. */
60      @SuppressWarnings("checkstyle:visibilitymodifier")
61      Length offsetEnd = null;
62  
63      /** Extra rotation for the link at the start node. */
64      @SuppressWarnings("checkstyle:visibilitymodifier")
65      Angle rotationStart = null;
66  
67      /** Extra rotation for the link at the end node. */
68      @SuppressWarnings("checkstyle:visibilitymodifier")
69      Angle rotationEnd = null;
70  
71      /** Straight. */
72      @SuppressWarnings("checkstyle:visibilitymodifier")
73      StraightTag straightTag = null;
74  
75      /** PolyLine. */
76      @SuppressWarnings("checkstyle:visibilitymodifier")
77      PolyLineTag polyLineTag = null;
78  
79      /** Arc. */
80      @SuppressWarnings("checkstyle:visibilitymodifier")
81      ArcTag arcTag = null;
82  
83      /** Bezier. */
84      @SuppressWarnings("checkstyle:visibilitymodifier")
85      BezierTag bezierTag = null;
86  
87      /** Map of lane name to lane override. */
88      @SuppressWarnings("checkstyle:visibilitymodifier")
89      Map<String, LaneOverrideTag> laneOverrideTags = new HashMap<>();
90  
91      /** Map of lane name to generators. */
92      @SuppressWarnings("checkstyle:visibilitymodifier")
93      Map<String, GeneratorTag> generatorTags = new HashMap<>();
94  
95      /** Map of lane name to list generators. */
96      @SuppressWarnings("checkstyle:visibilitymodifier")
97      Map<String, ListGeneratorTag> listGeneratorTags = new HashMap<>();
98  
99      /** Map of lane name to list of sensors. */
100     @SuppressWarnings("checkstyle:visibilitymodifier")
101     Map<String, List<SensorTag>> sensorTags = new HashMap<>();
102 
103     /** Map of lane name to traffic lights. */
104     @SuppressWarnings("checkstyle:visibilitymodifier")
105     Map<String, List<TrafficLightTag>> trafficLightTags = new HashMap<>();
106 
107     /** Map of lane name to fill at t=0. */
108     @SuppressWarnings("checkstyle:visibilitymodifier")
109     Map<String, FillTag> fillTags = new HashMap<>();
110 
111     /** Map of lane name to sink tags. */
112     @SuppressWarnings("checkstyle:visibilitymodifier")
113     Map<String, SinkTag> sinkTags = new HashMap<>();
114 
115     /** Map of lane name to generated lanes. */
116     @SuppressWarnings("checkstyle:visibilitymodifier")
117     Map<String, Lane> lanes = new HashMap<>();
118 
119     /** The calculated Link. */
120     @SuppressWarnings("checkstyle:visibilitymodifier")
121     CrossSectionLink link = null;
122 
123     /** The lane keeping policy, i.e., keep left, keep right or keep lane. */
124     @SuppressWarnings("checkstyle:visibilitymodifier")
125     LaneKeepingPolicy laneKeepingPolicy = null;
126 
127     /** The priority for conflicts. */
128     Priority priority = null;
129     
130     /**
131      * Parse the LINK tags.
132      * @param nodeList nodeList the top-level nodes of the XML-file
133      * @param parser the parser with the lists of information
134      * @throws SAXException when parsing of the tag fails
135      * @throws NetworkException when parsing of the tag fails
136      */
137     @SuppressWarnings("checkstyle:needbraces")
138     static void parseLinks(final NodeList nodeList, final XmlNetworkLaneParser parser) throws SAXException, NetworkException
139     {
140         for (Node node : XMLParser.getNodes(nodeList, "LINK"))
141         {
142             NamedNodeMap attributes = node.getAttributes();
143             LinkTag linkTag = new LinkTag();
144 
145             if (attributes.getNamedItem("NAME") == null)
146                 throw new SAXException("LINK: missing attribute NAME");
147             linkTag.name = attributes.getNamedItem("NAME").getNodeValue().trim();
148             if (parser.connectorTags.keySet().contains(linkTag.name) || parser.linkTags.keySet().contains(linkTag.name))
149                 throw new SAXException("LINK: NAME " + linkTag.name + " defined twice");
150 
151             if (attributes.getNamedItem("ROADLAYOUT") == null)
152                 throw new SAXException("LINK: missing attribute ROADLAYOUT for link " + linkTag.name);
153             String roadTypeName = attributes.getNamedItem("ROADLAYOUT").getNodeValue().trim();
154             if (!parser.roadLayoutTags.containsKey(roadTypeName))
155                 throw new SAXException("LINK: ROADLAYOUT " + roadTypeName + " not found for link " + linkTag.name);
156             linkTag.roadLayoutTag = parser.roadLayoutTags.get(roadTypeName);
157 
158             if (attributes.getNamedItem("NODESTART") == null)
159                 throw new SAXException("LINK: missing attribute NODESTART for link " + linkTag.name);
160             String fromNodeStr = attributes.getNamedItem("NODESTART").getNodeValue().trim();
161             linkTag.nodeStartTag = parser.nodeTags.get(fromNodeStr);
162             if (linkTag.nodeStartTag == null)
163                 throw new SAXException("LINK: NODESTART node " + fromNodeStr + " for link " + linkTag.name + " not defined");
164 
165             if (attributes.getNamedItem("NODEEND") == null)
166                 throw new SAXException("LINK: missing attribute NODEEND for link " + linkTag.name);
167             String toNodeStr = attributes.getNamedItem("NODEEND").getNodeValue().trim();
168             linkTag.nodeEndTag = parser.nodeTags.get(toNodeStr);
169             if (linkTag.nodeEndTag == null)
170                 throw new SAXException("LINK: NODEEND node " + toNodeStr + " for link " + linkTag.name + " not defined");
171 
172             if (attributes.getNamedItem("OFFSETSTART") != null)
173                 linkTag.offsetStart = LengthUnits.parseLength(attributes.getNamedItem("OFFSETSTART").getNodeValue());
174 
175             if (attributes.getNamedItem("OFFSETEND") != null)
176                 linkTag.offsetEnd = LengthUnits.parseLength(attributes.getNamedItem("OFFSETEND").getNodeValue());
177 
178             if (attributes.getNamedItem("ROTATIONSTART") != null)
179                 linkTag.rotationStart = AngleUnits.parseAngle(attributes.getNamedItem("ROTATIONSTART").getNodeValue());
180 
181             if (attributes.getNamedItem("ROTATIONEND") != null)
182                 linkTag.rotationEnd = AngleUnits.parseAngle(attributes.getNamedItem("ROTATIONEND").getNodeValue());
183 
184             if (attributes.getNamedItem("PRIORITY") != null)
185                 linkTag.priority = LaneAttributes.parsePriority(attributes.getNamedItem("PRIORITY").getNodeValue());
186             
187             Node lkp = attributes.getNamedItem("LANEKEEPING");
188             if (lkp != null)
189                 linkTag.laneKeepingPolicy = LaneAttributes.parseLaneKeepingPolicy(lkp.getNodeValue().trim());
190             else if (linkTag.roadLayoutTag.laneKeepingPolicy != null)
191                 linkTag.laneKeepingPolicy = linkTag.roadLayoutTag.laneKeepingPolicy;
192             else if (linkTag.roadLayoutTag.roadTypeTag.defaultLaneKeepingPolicy != null)
193                 linkTag.laneKeepingPolicy = linkTag.roadLayoutTag.roadTypeTag.defaultLaneKeepingPolicy;
194             else
195                 throw new SAXException("LINK: cannot determine LANEKEEPING for lane: " + linkTag.name);
196 
197             List<Node> straightNodes = XMLParser.getNodes(node.getChildNodes(), "STRAIGHT");
198             List<Node> polyLineNodes = XMLParser.getNodes(node.getChildNodes(), "POLYLINE");
199             List<Node> arcNodes = XMLParser.getNodes(node.getChildNodes(), "ARC");
200             List<Node> bezierNodes = XMLParser.getNodes(node.getChildNodes(), "BEZIER");
201             if (straightNodes.size() > 1)
202                 throw new SAXException("LINK: more than one STRAIGHT tag for link " + linkTag.name);
203             if (polyLineNodes.size() > 1)
204                 throw new SAXException("LINK: more than one POLYLINE tag for link " + linkTag.name);
205             if (bezierNodes.size() > 1)
206                 throw new SAXException("LINK: more than one BEZIER tag for link " + linkTag.name);
207             if (arcNodes.size() > 1)
208                 throw new SAXException("LINK: more than one ARC tag for link " + linkTag.name);
209             if (straightNodes.size() + polyLineNodes.size() + arcNodes.size() + bezierNodes.size() > 1)
210                 throw new SAXException("LINK: multiple tags (STRAIGHT, POLYLINE, ARC, BEZIER) for link " + linkTag.name);
211             if (straightNodes.size() + polyLineNodes.size() + arcNodes.size() + bezierNodes.size() == 0)
212                 throw new SAXException("LINK: no tags (STRAIGHT, POLYLINE, ARC, BEZIER) for link " + linkTag.name);
213 
214             // parse the STRAIGHT tag
215             if (straightNodes.size() == 1)
216                 StraightTag.parseStraight(straightNodes.get(0), parser, linkTag);
217 
218             // parse the POLYLINE tag
219             if (polyLineNodes.size() == 1)
220                 PolyLineTag.parsePolyLine(polyLineNodes.get(0), parser, linkTag);
221 
222             // parse the ARC tags
223             if (arcNodes.size() == 1)
224                 ArcTag.parseArc(arcNodes.get(0), parser, linkTag);
225 
226             // parse the BEZIER tag
227             if (bezierNodes.size() == 1)
228                 BezierTag.parseBezier(bezierNodes.get(0), parser, linkTag);
229 
230             parser.linkTags.put(linkTag.name, linkTag);
231 
232             // parse the LANEOVERRIDE tags
233             for (Node loNode : XMLParser.getNodes(node.getChildNodes(), "LANEOVERRIDE"))
234             {
235                 LaneOverrideTag.parseLaneOverride(loNode, parser, linkTag);
236             }
237 
238             // parse the GENERATOR tags
239             for (Node genNode : XMLParser.getNodes(node.getChildNodes(), "GENERATOR"))
240             {
241                 GeneratorTag.parseGenerator(genNode, parser, linkTag);
242             }
243 
244             // parse the LISTGENERATOR tags
245             for (Node listGenNode : XMLParser.getNodes(node.getChildNodes(), "LISTGENERATOR"))
246             {
247                 ListGeneratorTag.parseListGenerator(listGenNode, parser, linkTag);
248             }
249 
250             // parse the SENSOR tags
251             for (Node sensorNode : XMLParser.getNodes(node.getChildNodes(), "SENSOR"))
252             {
253                 SensorTag.parseSensor(sensorNode, parser, linkTag);
254             }
255 
256             // parse the TRAFFICLIGHT tags
257             for (Node trafficLightNode : XMLParser.getNodes(node.getChildNodes(), "TRAFFICLIGHT"))
258             {
259                 TrafficLightTag.parseTrafficLight(trafficLightNode, parser, linkTag);
260             }
261 
262             // parse the SINK tags
263             for (Node sinkNode : XMLParser.getNodes(node.getChildNodes(), "SINK"))
264             {
265                 SinkTag.parseSink(sinkNode, parser, linkTag);
266             }
267 
268             // parse the FILL tags
269             for (Node fillNode : XMLParser.getNodes(node.getChildNodes(), "FILL"))
270             {
271                 FillTag.parseFill(fillNode, parser, linkTag);
272             }
273 
274         }
275     }
276 
277     /**
278      * This method parses a length string that can have values such as: BEGIN, END, 10m, END-10m, 98%. Only use the method after
279      * the length of the cross section elements is known!
280      * @param posStr the position string to parse. Lengths are relative to the center line of the cross section element.
281      * @param cse the cross section element to retrieve the center line
282      * @return the corresponding position as a length on the center line
283      * @throws NetworkException when parsing fails
284      */
285     static Length parseBeginEndPosition(final String posStr, final CrossSectionElement cse) throws NetworkException
286     {
287         if (posStr.trim().equals("BEGIN"))
288         {
289             return new Length(0.0, LengthUnit.METER);
290         }
291 
292         double length = cse.getCenterLine().getLengthSI();
293 
294         if (posStr.trim().equals("END"))
295         {
296             return new Length(length, LengthUnit.METER);
297         }
298 
299         if (posStr.endsWith("%"))
300         {
301             String s = posStr.substring(0, posStr.length() - 1).trim();
302             try
303             {
304                 double fraction = Double.parseDouble(s) / 100.0;
305                 if (fraction < 0.0 || fraction > 1.0)
306                 {
307                     throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
308                             + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%");
309                 }
310                 return new Length(length * fraction, LengthUnit.METER);
311             }
312             catch (NumberFormatException nfe)
313             {
314                 throw new NetworkException("parseBeginEndPosition: attribute POSITION with value " + posStr
315                         + " invalid for lane " + cse.toString() + ", should be a percentage between 0 and 100%", nfe);
316             }
317         }
318 
319         if (posStr.trim().startsWith("END-"))
320         {
321             String s = posStr.substring(4).trim();
322             double offset = LengthUnits.parseLength(s).getSI();
323             if (offset > length)
324             {
325                 throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr
326                         + " invalid for lane " + cse.toString() + ": provided negative offset greater than than link length");
327             }
328             return new Length(length - offset, LengthUnit.METER);
329         }
330 
331         Length offset = LengthUnits.parseLength(posStr);
332         if (offset.getSI() > length)
333         {
334             throw new NetworkException("parseBeginEndPosition - attribute POSITION with value " + posStr + " invalid for lane "
335                     + cse.toString() + ": provided offset greater than than link length");
336         }
337         return offset;
338     }
339 
340     /** {@inheritDoc} */
341     @Override
342     public String toString()
343     {
344         return "LinkTag [name=" + this.name + ", nodeStartTag=" + this.nodeStartTag + ", nodeEndTag=" + this.nodeEndTag + "]";
345     }
346 
347 }