View Javadoc
1   package org.opentrafficsim.road.network.factory.osm;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.Objects;
6   
7   import org.opentrafficsim.road.network.factory.osm.events.WarningEvent;
8   import org.opentrafficsim.road.network.factory.osm.events.WarningListener;
9   
10  /**
11   * OpenStreetMap Link.
12   * <p>
13   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * <p>
16   * $LastChangedDate: 2015-07-26 01:01:13 +0200 (Sun, 26 Jul 2015) $, @version $Revision: 1155 $, by $Author: averbraeck $,
17   * initial version 31 dec. 2014 <br>
18   * @author <a>Moritz Bergmann</a>
19   */
20  public class OSMLink
21  {
22      /** The Link ID. It is generated out of the Start ID and the End ID and (if present) the name tag. */
23      private final String id;
24  
25      /** The start Node of the OSMLink. */
26      private final OSMNode start;
27  
28      /** The end Node of the OSMLink. */
29      private final OSMNode end;
30  
31      /** The List of nodes that are used only to define shape (for mapping purposes). */
32      private List<OSMNode> intermediateNodes;
33  
34      /** The length of the OSMLink. */
35      private final double length;
36  
37      /** The tags that this OSMLink inherits from it's way. */
38      private List<OSMTag> tags;
39  
40      /** The number of lanes on this OSMLink. */
41      private byte lanes;
42  
43      /** The number of lanes going forward (the <i>design</i> direction). */
44      private byte forwardLanes;
45  
46      /** Is this OSMLink one way? */
47      private boolean oneway;
48  
49      /**
50       * Construct a new OSMLink.
51       * @param fromNode OSMNode; the OSMNode where this OSMLinks begins
52       * @param toNode OSMNode; the OSMNode where this OSMLink ends
53       * @param tags List&lt;OSMTab&gt;; the OSMTags (inherited from the OSMWay that causes this OSMLink to be constructed)
54       * @param length double; the length of the new OSMLink
55       * @param warningListener WarningListener; the warning listener that will receive warning events
56       */
57      public OSMLink(final OSMNode fromNode, final OSMNode toNode, final List<OSMTag> tags, final double length,
58          final WarningListener warningListener)
59      {
60          if (fromNode == toNode)
61          {
62              throw new Error("Start and end of link are the same Node: " + fromNode);
63          }
64          String name = "";
65          for (OSMTag tag : tags)
66          {
67              if (tag.getKey().equals("name"))
68              {
69                  name = ": " + tag.getValue();
70              }
71          }
72          this.id = Objects.toString(fromNode.getId()) + Objects.toString(toNode.getId()) + name;
73          this.start = fromNode;
74          this.end = toNode;
75          this.length = length;
76          this.lanes = 1;
77          this.forwardLanes = 1;
78          boolean forwardDefined = false;
79  
80          List<OSMTag> linkTags = new ArrayList<OSMTag>(tags);
81          for (OSMTag tag : tags)
82          {
83              if (tag.getKey().equals("oneway") && tag.getValue().equals("yes"))
84              {
85                  this.setOneway(true);
86                  linkTags.remove(tag);
87                  this.forwardLanes = this.lanes;
88              }
89              if (tag.getKey().equals("highway") && tag.getValue().equals("motorway_link"))
90              {
91                  this.setOneway(true);
92                  this.lanes = 1;
93                  this.forwardLanes = this.lanes;
94              }
95              if (tag.getKey().equals("highway") && tag.getValue().equals("motorway"))
96              {
97                  this.setOneway(true);
98                  this.forwardLanes = this.lanes;
99              }
100             if (tag.getKey().equals("highway")
101                 && (tag.getValue().equals("cycleway") || tag.getValue().equals("footway")
102                     || tag.getValue().equals("pedestrian") || tag.getValue().equals("steps")))
103             {
104                 this.lanes = 1;
105             }
106         }
107 
108         for (OSMTag tag2 : new ArrayList<OSMTag>(linkTags))
109         {
110             if (tag2.getKey().equals("lanes"))
111             {
112                 if (isByte(tag2.getValue()))
113                 {
114                     this.lanes = Byte.parseByte(tag2.getValue());
115                     linkTags.remove(tag2);
116                     if (this.oneway)
117                     {
118                         this.forwardLanes = this.lanes;
119                         forwardDefined = true;
120                     }
121                 }
122                 else
123                 {
124                     String warning = "Illegal value for the tag 'lanes' at link " + this.id;
125                     warningListener.warning(new WarningEvent(this, warning));
126                 }
127             }
128             if (tag2.getKey().equals("lanes:forward"))
129             {
130                 if (isByte(tag2.getValue()))
131                 {
132                     this.forwardLanes = Byte.parseByte(tag2.getValue());
133                     linkTags.remove(tag2);
134                     forwardDefined = true;
135                 }
136                 else
137                 {
138                     String warning = "Illegal value for the tag 'lanes:forward' at link " + this.id;
139                     warningListener.warning(new WarningEvent(this, warning));
140                 }
141             }
142         }
143         this.tags = linkTags;
144         if (!forwardDefined && this.lanes > 1)
145         {
146             this.forwardLanes = (byte) (this.lanes / 2);
147             String warning = "No forward lanes defined at link " + this.id;
148             warningListener.warning(new WarningEvent(this, warning));
149         }
150         this.intermediateNodes = new ArrayList<OSMNode>();
151     }
152 
153     /**
154      * Construct a new OSMLink with specified number of lanes and forward lanes.
155      * @param startNode OSMNode; the start OSMNode of the new OSMLink
156      * @param endNode OSMNode; the end OSMNode of the new OSMLink
157      * @param tags List of Tags inherited from way
158      * @param length length of link
159      * @param lanes byte; the total number of lanes
160      * @param flanes byte; the number of forward lanes
161      */
162     public OSMLink(final OSMNode startNode, final OSMNode endNode, final List<OSMTag> tags, final double length,
163         final byte lanes, final byte flanes)
164     {
165         if (startNode == endNode)
166         {
167             throw new Error("start and end of link are the same Node: " + startNode);
168         }
169         this.id = Objects.toString(startNode.getId()) + Objects.toString(endNode.getId());
170         this.start = startNode;
171         this.end = endNode;
172         this.tags = tags;
173         this.length = length;
174         this.lanes = lanes;
175         this.forwardLanes = flanes;
176         this.intermediateNodes = new ArrayList<OSMNode>();
177     }
178 
179     /**
180      * @return id
181      */
182     public final String getId()
183     {
184         return this.id;
185     }
186 
187     /**
188      * @return start.
189      */
190     public final OSMNode getStart()
191     {
192         return this.start;
193     }
194 
195     /**
196      * @return end.
197      */
198     public final OSMNode getEnd()
199     {
200         return this.end;
201     }
202 
203     /**
204      * Retrieve the tags of this OSMLink.
205      * @return List&lt;OSMTab&gt;; the returned object is a copy; modifications of the returned object do not affect this
206      *         OSMLink
207      */
208     public final List<OSMTag> getTags()
209     {
210         return new ArrayList<OSMTag>(this.tags); // Create and return a copy (the tags themselves are immutable).
211     }
212 
213     /**
214      * Indicate if this OSMLink is one way.
215      * @return boolean; true if this OSMLink is one way; false if this OSMLink is not one way
216      */
217     public final boolean isOneway()
218     {
219         return this.oneway;
220     }
221 
222     /**
223      * Set the one way status of this OSMLink.
224      * @param isOneWay the new value for the one way status of this OSMLink
225      */
226     public final void setOneway(final boolean isOneWay)
227     {
228         this.oneway = isOneWay;
229     }
230 
231     /**
232      * Retrieve the total number of lanes on this OSMLink.
233      * @return byte; the total number of lanes on this OSMLink
234      */
235     public final byte getLanes()
236     {
237         return this.lanes;
238     }
239 
240     /**
241      * Retrieve the total number of forward lanes on this OSMLink; forward lanes are lanes that may only be traveled from
242      * startNode towards endNode.
243      * @return byte; the number of forward lanes on this OSMLink
244      */
245     public final byte getForwardLanes()
246     {
247         return this.forwardLanes;
248     }
249 
250     /**
251      * Add an OSMTag to this Link.
252      * @param tag Tag; the OSMTag that must be added
253      */
254     public final void addTag(final OSMTag tag)
255     {
256         this.tags.add(tag);
257     }
258 
259     /**
260      * Retrieve the length of this OSMLink.
261      * @return double; the length of this OSMLink in meters
262      */
263     public final double getLength()
264     {
265         return this.length;
266     }
267 
268     /**
269      * Retrieve the list of OSMNodes that define the shape of this OSMLink.
270      * @return List&lt;OSMNode&gt;; the list of OSMNodes that define the shape of this OSMLink
271      */
272     public final List<OSMNode> getSplineList()
273     {
274         return this.intermediateNodes;
275     }
276 
277     /**
278      * Append a Node to the list of OSMNodes of this OSMLink that define the shape of this OSMLink.
279      * @param shapeNode OSMNode; the OSMNode to add to the list of OSMNodes that define the shape of this OSMLink
280      */
281     public final void addSpline(final OSMNode shapeNode)
282     {
283         this.intermediateNodes.add(shapeNode);
284     }
285 
286     /**
287      * Returns true if the link has an OSMTag with the specified key.
288      * @param key String; the key of the sought OSMTag
289      * @return boolean; true if this OSMLink has (one or more) OSMTag(s) with the specified key
290      */
291     public final boolean hasTag(final String key)
292     {
293         for (OSMTag t : this.tags)
294         {
295             if (t.getKey().equals(key))
296             {
297                 return true;
298             }
299         }
300         return false;
301     }
302 
303     /**
304      * Determine if a string represents a number that can stored in a byte.
305      * @param s String; the string
306      * @return is the given String a byte.
307      */
308     private boolean isByte(final String s)
309     {
310         try
311         {
312             Byte.parseByte(s);
313         }
314         catch (NumberFormatException e)
315         {
316             return false;
317         }
318         return true;
319     }
320 
321     /** {@inheritDoc} */
322     public final String toString()
323     {
324         return String.format("Link %s from %d to %d", getId(), getStart().getId(), getEnd().getId());
325     }
326 
327     /**
328      * Report if this OSMLink has all tags in a supplied set.
329      * @param tagsToCheck List&lt;OSMTag&gt;; the supplied set of tags
330      * @return boolean; true if this Link has all the supplied tags; false otherwise
331      */
332     public final boolean containsAllTags(final List<OSMTag> tagsToCheck)
333     {
334         return this.tags.containsAll(tagsToCheck);
335     }
336 
337 }