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