View Javadoc
1   package org.opentrafficsim.road.network.factory.opendrive;
2   
3   import java.io.Serializable;
4   import java.util.List;
5   
6   import org.opentrafficsim.core.network.NetworkException;
7   import org.w3c.dom.NamedNodeMap;
8   import org.w3c.dom.Node;
9   import org.w3c.dom.NodeList;
10  import org.xml.sax.SAXException;
11  
12  /**
13   * <p>
14   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
15   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
16   * <p>
17   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
18   * initial version Jul 23, 2015 <br>
19   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
20   */
21  class LinkTag implements Serializable
22  {
23  
24      /** */
25      private static final long serialVersionUID = 20150723L;
26  
27      /** Predecessor id. */
28      @SuppressWarnings("checkstyle:visibilitymodifier")
29      String predecessorId = null;
30  
31      /** Predecessor Type. */
32      @SuppressWarnings("checkstyle:visibilitymodifier")
33      String predecessorType = null;
34  
35      /** Predecessor contact point. */
36      @SuppressWarnings("checkstyle:visibilitymodifier")
37      ContactPointEnum predecessorContactPoint = null;
38  
39      /** Successor id. */
40      @SuppressWarnings("checkstyle:visibilitymodifier")
41      String successorId = null;
42  
43      /** Successor Type. */
44      @SuppressWarnings("checkstyle:visibilitymodifier")
45      String successorType = null;
46  
47      /** Successor contact point. */
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49      ContactPointEnum successorContactPoint = null;
50  
51      /** Left neighbor road id. */
52      @SuppressWarnings("checkstyle:visibilitymodifier")
53      String leftNeighborRoadId = null;
54  
55      /** Left neighbor direction. */
56      @SuppressWarnings("checkstyle:visibilitymodifier")
57      NeighborDirection leftNeighborDirection = null;
58  
59      /** Right neighbor road id. */
60      @SuppressWarnings("checkstyle:visibilitymodifier")
61      String rightNeighborRoadId = null;
62  
63      /** Right neighbor direction. */
64      @SuppressWarnings("checkstyle:visibilitymodifier")
65      NeighborDirection rightNeighborDirection = null;
66  
67      /**
68       * Parse the attributes of the road tag. The sub-elements are parsed in separate classes.
69       * @param nodeList NodeList; the list of subnodes of the road node
70       * @param parser OpenDriveNetworkLaneParser; the parser with the lists of information
71       * @param roadTag RoadTag; the RoadTag to which this element belongs
72       * @throws SAXException when parsing of the tag fails
73       * @throws NetworkException when parsing of the tag fails
74       */
75      @SuppressWarnings("checkstyle:needbraces")
76      static void parseLink(final NodeList nodeList, final OpenDriveNetworkLaneParser parser, final RoadTag roadTag)
77              throws SAXException, NetworkException
78      {
79          int linkCount = 0;
80          for (Node node : XMLParser.getNodes(nodeList, "link"))
81          {
82              linkCount++;
83              LinkTag linkTag = new LinkTag();
84              roadTag.linkTag = linkTag;
85  
86              /* PREDECESSOR */
87  
88              List<Node> predecessorNodes = XMLParser.getNodes(node.getChildNodes(), "predecessor");
89              if (predecessorNodes.size() > 1)
90                  throw new SAXException("ROAD: more than one LINK.PREDECESSOR tag for road id=" + roadTag.id);
91              if (predecessorNodes.size() == 1)
92              {
93                  Node predecessorNode = predecessorNodes.get(0);
94                  NamedNodeMap attributes = predecessorNode.getAttributes();
95  
96                  Node elementId = attributes.getNamedItem("elementId");
97                  if (elementId == null)
98                      throw new SAXException("ROAD.LINK.PREDECESSOR: missing attribute elementId for ROAD.ID=" + roadTag.id);
99  
100                 Node elementType = attributes.getNamedItem("elementType");
101                 if (elementType == null)
102                     throw new SAXException("ROAD.LINK.PREDECESSOR: missing attribute elementType for ROAD.ID=" + roadTag.id);
103                 if ("road".equals(elementType.getNodeValue().trim()) || "junction".equals(elementType.getNodeValue().trim()))
104                 {
105                     linkTag.predecessorType = elementType.getNodeValue().trim();
106                     linkTag.predecessorId = elementId.getNodeValue().trim();
107                 }
108                 else
109                     throw new SAXException("ROAD.LINK.PREDECESSOR: elementType for ROAD.ID=" + roadTag.id
110                             + " is neither 'road' nor 'junction' but: " + elementType.getNodeValue().trim());
111 
112                 Node contactPoint = attributes.getNamedItem("contactPoint");
113                 if (contactPoint != null)
114                 // System.out.println("ROAD " + roadTag.id + " has no contactPoint for PREDECESSOR " +
115                 // elementType.getNodeValue().trim() + elementId.getNodeValue().trim());
116                 // else
117                 {
118                     if ("start".equals(contactPoint.getNodeValue().trim()))
119                         linkTag.predecessorContactPoint = ContactPointEnum.START;
120                     else if ("end".equals(contactPoint.getNodeValue().trim()))
121                         linkTag.predecessorContactPoint = ContactPointEnum.END;
122                     else
123                         throw new SAXException("ROAD.LINK.PREDECESSOR: contactPoint for ROAD.ID=" + roadTag.id
124                                 + " is neither 'start' nor 'end' but: " + contactPoint.getNodeValue().trim());
125                 }
126             }
127 
128             /* SUCCESSOR */
129 
130             List<Node> successorNodes = XMLParser.getNodes(node.getChildNodes(), "successor");
131             if (successorNodes.size() > 1)
132                 throw new SAXException("ROAD: more than one LINK.SUCCESSOR tag for road id=" + roadTag.id);
133             if (successorNodes.size() == 1)
134             {
135                 Node successorNode = successorNodes.get(0);
136                 NamedNodeMap attributes = successorNode.getAttributes();
137 
138                 Node elementId = attributes.getNamedItem("elementId");
139                 if (elementId == null)
140                     throw new SAXException("ROAD.LINK.SUCCESSOR: missing attribute elementId for ROAD.ID=" + roadTag.id);
141 
142                 Node elementType = attributes.getNamedItem("elementType");
143                 if (elementType == null)
144                     throw new SAXException("ROAD.LINK.SUCCESSOR: missing attribute elementType for ROAD.ID=" + roadTag.id);
145                 if ("road".equals(elementType.getNodeValue().trim()) || "junction".equals(elementType.getNodeValue().trim()))
146                 {
147                     linkTag.successorType = elementType.getNodeValue().trim();
148                     linkTag.successorId = elementId.getNodeValue().trim();
149                 }
150                 else
151                     throw new SAXException("ROAD.LINK.SUCCESSOR: elementType for ROAD.ID=" + roadTag.id
152                             + " is neither 'road' nor 'junction' but: " + elementType.getNodeValue().trim());
153 
154                 Node contactPoint = attributes.getNamedItem("contactPoint");
155                 if (contactPoint != null)
156                 // System.out.println("ROAD " + roadTag.id + " has no contactPoint for SUCCESSOR " +
157                 // elementType.getNodeValue().trim() + elementId.getNodeValue().trim());
158                 // else
159                 {
160                     if ("start".equals(contactPoint.getNodeValue().trim()))
161                         linkTag.successorContactPoint = ContactPointEnum.START;
162                     else if ("end".equals(contactPoint.getNodeValue().trim()))
163                         linkTag.successorContactPoint = ContactPointEnum.END;
164                     else
165                         throw new SAXException("ROAD.LINK.SUCCESSOR: contactPoint for ROAD.ID=" + roadTag.id
166                                 + " is neither 'start' nor 'end' but: " + contactPoint.getNodeValue().trim());
167                 }
168             }
169 
170             /* NEIGHBOR */
171 
172             List<Node> neighborNodes = XMLParser.getNodes(node.getChildNodes(), "neighbor");
173             if (neighborNodes.size() > 2)
174                 throw new SAXException("ROAD: more than two LINK.NEIGHBOR tags for road id=" + roadTag.id);
175             boolean left = false;
176             boolean right = false;
177             for (Node neighborNode : neighborNodes)
178             {
179                 NamedNodeMap attributes = neighborNode.getAttributes();
180 
181                 Node elementId = attributes.getNamedItem("elementId");
182                 if (elementId == null)
183                     throw new SAXException("ROAD.LINK.NEIGHBOR: missing attribute elementId for ROAD.ID=" + roadTag.id);
184 
185                 Node direction = attributes.getNamedItem("direction");
186                 NeighborDirection ndir = null;
187                 if (direction == null)
188                     throw new SAXException("ROAD.LINK.NEIGHBOR: missing attribute direction for ROAD.ID=" + roadTag.id);
189                 if ("same".equals(direction.getNodeValue().trim()))
190                     ndir = NeighborDirection.SAME;
191                 else if ("opposite".equals(direction.getNodeValue().trim()))
192                     ndir = NeighborDirection.OPPOSITE;
193                 else
194                     throw new SAXException("ROAD.LINK.NEIGHBOR: contactPoint for ROAD.ID=" + roadTag.id
195                             + " is neither 'same' nor 'opposite' but: " + direction.getNodeValue().trim());
196 
197                 Node side = attributes.getNamedItem("side");
198                 if (side == null)
199                     throw new SAXException("ROAD.LINK.NEIGHBOR: missing attribute side for ROAD.ID=" + roadTag.id);
200                 if ("left".equals(side.getNodeValue().trim()))
201                 {
202                     if (left)
203                         throw new SAXException("ROAD.LINK.NEIGHBOR: left side defined twice for ROAD.ID=" + roadTag.id);
204                     left = true;
205                     linkTag.leftNeighborRoadId = elementId.getNodeValue().trim();
206                     linkTag.leftNeighborDirection = ndir;
207                 }
208                 else if ("right".equals(side.getNodeValue().trim()))
209                 {
210                     if (right)
211                         throw new SAXException("ROAD.LINK.NEIGHBOR: right side defined twice for ROAD.ID=" + roadTag.id);
212                     right = true;
213                     linkTag.rightNeighborRoadId = elementId.getNodeValue().trim();
214                     linkTag.rightNeighborDirection = ndir;
215                 }
216                 else
217                     throw new SAXException("ROAD.LINK.NEIGHBOR: side for ROAD.ID=" + roadTag.id
218                             + " is neither 'left' nor 'right' but: " + side.getNodeValue().trim());
219             }
220 
221         }
222 
223         if (linkCount > 1)
224             throw new SAXException("ROAD: more than one LINK tag for road id=" + roadTag.id);
225     }
226 
227     /** Enum for road linking contact points: is the link with the start or the end? */
228     enum ContactPointEnum
229     {
230         /** Start. */
231         START,
232         /** End. */
233         END;
234     }
235 
236     /** Enum for neighbor direction: same or opposite direction? */
237     enum NeighborDirection
238     {
239         /** Same. */
240         SAME,
241         /** Opposite. */
242         OPPOSITE;
243     }
244 
245     /** {@inheritDoc} */
246     @Override
247     public final String toString()
248     {
249         return "LinkTag [predecessorId=" + this.predecessorId + ", predecessorType=" + this.predecessorType
250                 + ", predecessorContactPoint=" + this.predecessorContactPoint + ", successorId=" + this.successorId
251                 + ", successorType=" + this.successorType + ", successorContactPoint=" + this.successorContactPoint
252                 + ", leftNeighborRoadId=" + this.leftNeighborRoadId + ", leftNeighborDirection=" + this.leftNeighborDirection
253                 + ", rightNeighborRoadId=" + this.rightNeighborRoadId + ", rightNeighborDirection="
254                 + this.rightNeighborDirection + "]";
255     }
256 }