1   package org.opentrafficsim.draw.graphs.road;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Comparator;
6   import java.util.Iterator;
7   import java.util.LinkedHashMap;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import org.djunits.value.vdouble.scalar.Length;
14  import org.djunits.value.vdouble.scalar.Speed;
15  import org.djutils.exceptions.Throw;
16  import org.djutils.immutablecollections.ImmutableMap;
17  import org.djutils.immutablecollections.ImmutableSet;
18  import org.opentrafficsim.core.gtu.GTUDirectionality;
19  import org.opentrafficsim.core.network.DirectedLinkPosition;
20  import org.opentrafficsim.core.network.Link;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.draw.graphs.GraphCrossSection;
23  import org.opentrafficsim.draw.graphs.GraphPath;
24  import org.opentrafficsim.draw.graphs.GraphPath.Section;
25  import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
26  import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
27  import org.opentrafficsim.road.network.lane.CrossSectionLink;
28  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
29  import org.opentrafficsim.road.network.lane.Lane;
30  import org.opentrafficsim.road.network.lane.LaneDirection;
31  import org.opentrafficsim.road.network.sampling.LaneData;
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  public final class GraphLaneUtil
45  {
46  
47      
48  
49  
50      private GraphLaneUtil()
51      {
52          
53      }
54  
55      
56  
57  
58  
59  
60  
61  
62      public static GraphPath<KpiLaneDirection> createPath(final String name, final LaneDirection first) throws NetworkException
63      {
64          Throw.whenNull(name, "Name may not be null.");
65          Throw.whenNull(first, "First may not be null.");
66          List<Section<KpiLaneDirection>> sections = new ArrayList<>();
67          Set<LaneDirection> set = new LinkedHashSet<>();
68          LaneDirection lane = first;
69          while (lane != null && !set.contains(lane))
70          {
71              KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane.getLane()),
72                      lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
73              List<KpiLaneDirection> list = new ArrayList<>();
74              list.add(kpiLaneDirection);
75              Speed speed = lane.getLane().getLowestSpeedLimit();
76              Length length = lane.getLength();
77              sections.add(new Section<KpiLaneDirection>()
78              {
79                  
80                  @Override
81                  public Iterator<KpiLaneDirection> iterator()
82                  {
83                      return list.iterator();
84                  }
85  
86                  
87                  @Override
88                  public Length getLength()
89                  {
90                      return length;
91                  }
92  
93                  
94                  @Override
95                  public Speed getSpeedLimit()
96                  {
97                      return speed;
98                  }
99  
100                 
101                 @Override
102                 public KpiLaneDirection getSource(final int series)
103                 {
104                     return kpiLaneDirection;
105                 }
106             });
107             set.add(lane);
108             ImmutableMap<Lane, GTUDirectionality> map = lane.getLane().downstreamLanes(lane.getDirection(), null);
109             if (map.size() == 1)
110             {
111                 ImmutableMap.ImmutableEntry<Lane, GTUDirectionality> entry = map.entrySet().iterator().next();
112                 lane = new LaneDirection(entry.getKey(), entry.getValue());
113             }
114         }
115         return new GraphPath<>(name, sections);
116     }
117 
118     
119 
120 
121 
122 
123 
124 
125 
126     public static GraphPath<KpiLaneDirection> createPath(final List<String> names, final List<LaneDirection> first)
127             throws NetworkException
128     {
129         Throw.whenNull(names, "Names may not be null.");
130         Throw.whenNull(first, "First may not be null.");
131         Throw.when(names.size() != first.size(), IllegalArgumentException.class, "Size of 'names' and 'first' must be equal.");
132         List<Section<KpiLaneDirection>> sections = new ArrayList<>();
133         Set<LaneDirection> set = new LinkedHashSet<>();
134         List<LaneDirection> lanes = first;
135         while (lanes != null && Collections.disjoint(set, lanes))
136         {
137             List<KpiLaneDirection> list = new ArrayList<>();
138             Speed speed = null;
139             for (LaneDirection lane : lanes)
140             {
141                 speed = speed == null ? lane.getLane().getLowestSpeedLimit()
142                         : Speed.min(speed, lane.getLane().getLowestSpeedLimit());
143                 KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane.getLane()),
144                         lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
145                 list.add(kpiLaneDirection);
146             }
147             Speed finalSpeed = speed;
148             Length length = lanes.get(0).getLane().getLength();
149             sections.add(new Section<KpiLaneDirection>()
150             {
151                 
152                 @Override
153                 public Iterator<KpiLaneDirection> iterator()
154                 {
155                     return list.iterator();
156                 }
157 
158                 
159                 @Override
160                 public Length getLength()
161                 {
162                     return length;
163                 }
164 
165                 
166                 @Override
167                 public Speed getSpeedLimit()
168                 {
169                     return finalSpeed;
170                 }
171 
172                 
173                 @Override
174                 public KpiLaneDirection getSource(final int series)
175                 {
176                     return list.get(series);
177                 }
178             });
179             set.addAll(lanes);
180             
181             Map<Link, List<LaneDirection>> linkMap = new LinkedHashMap<>();
182             Link link = lanes.get(0).getLane().getParentLink();
183             ImmutableSet<Link> links =
184                     (lanes.get(0).getDirection().isPlus() ? link.getEndNode() : link.getStartNode()).getLinks();
185             for (Link nextLink : links)
186             {
187                 if (!link.equals(nextLink))
188                 {
189                     List<LaneDirection> nextLanes = new ArrayList<>();
190                     for (LaneDirection laneDir : lanes)
191                     {
192                         ImmutableMap<Lane, GTUDirectionality> map =
193                                 laneDir.getLane().downstreamLanes(laneDir.getDirection(), null);
194                         int n = 0;
195                         for (ImmutableMap.ImmutableEntry<Lane, GTUDirectionality> entry : map.entrySet())
196                         {
197                             if (entry.getKey().getParentLink().equals(nextLink))
198                             {
199                                 n++;
200                                 nextLanes.add(new LaneDirection(entry.getKey(), entry.getValue()));
201                             }
202                         }
203                         if (n > 1)
204                         {
205                             
206                             nextLanes.clear();
207                             break;
208                         }
209                         else if (n == 0)
210                         {
211                             nextLanes.addAll(null);
212                         }
213                     }
214                     if (nextLanes.size() == lanes.size())
215                     {
216                         linkMap.put(nextLink, nextLanes);
217                     }
218                 }
219             }
220             
221             if (linkMap.size() > 1)
222             {
223                 Iterator<List<LaneDirection>> it = linkMap.values().iterator();
224                 while (it.hasNext())
225                 {
226                     if (it.next().contains(null))
227                     {
228                         it.remove();
229                     }
230                 }
231             }
232             if (linkMap.size() == 1)
233             {
234                 lanes = linkMap.values().iterator().next();
235             }
236             else
237             {
238                 lanes = null;
239             }
240         }
241         return new GraphPath<>(names, sections);
242     }
243 
244     
245 
246 
247 
248 
249 
250 
251     public static GraphPath<KpiLaneDirection> createSingleLanePath(final String name, final LaneDirection lane)
252             throws NetworkException
253     {
254         List<KpiLaneDirection> lanes = new ArrayList<>();
255         lanes.add(new KpiLaneDirection(new LaneData(lane.getLane()),
256                 lane.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
257         List<Section<KpiLaneDirection>> sections = new ArrayList<>();
258         Speed speed = lane.getLane().getLowestSpeedLimit();
259         sections.add(new Section<KpiLaneDirection>()
260         {
261 
262             
263             @Override
264             public Iterator<KpiLaneDirection> iterator()
265             {
266                 return lanes.iterator();
267             }
268 
269             
270             @Override
271             public Length getLength()
272             {
273                 return lane.getLength();
274             }
275 
276             
277             @Override
278             public Speed getSpeedLimit()
279             {
280                 return speed;
281             }
282 
283             
284             @Override
285             public KpiLaneDirection getSource(final int series)
286             {
287                 return lanes.get(0);
288             }
289 
290         });
291         return new GraphPath<>(name, sections);
292     }
293 
294     
295 
296 
297 
298 
299 
300 
301     public static GraphCrossSection<KpiLaneDirection> createCrossSection(final String name,
302             final DirectedLanePosition lanePosition) throws NetworkException
303     {
304         Throw.whenNull(name, "Name may not be null.");
305         Throw.whenNull(lanePosition, "Lane position may not be null.");
306         List<KpiLaneDirection> list = new ArrayList<>();
307         List<String> names = new ArrayList<>();
308         List<Length> positions = new ArrayList<>();
309         names.add(name);
310         positions.add(lanePosition.getPosition());
311         list.add(new KpiLaneDirection(new LaneData(lanePosition.getLane()),
312                 lanePosition.getGtuDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
313         Speed speed = lanePosition.getLane().getLowestSpeedLimit();
314         return createCrossSection(names, list, positions, speed);
315     }
316 
317     
318 
319 
320 
321 
322 
323 
324     public static GraphCrossSection<KpiLaneDirection> createCrossSection(final List<String> names,
325             final DirectedLinkPosition linkPosition) throws NetworkException
326     {
327         Throw.whenNull(names, "Names may not be null.");
328         Throw.whenNull(linkPosition, "Link position may not be null.");
329         Throw.when(!(linkPosition.getLink() instanceof CrossSectionLink), IllegalArgumentException.class,
330                 "The link is not a CrossEctionLink.");
331         List<Lane> lanes = ((CrossSectionLink) linkPosition.getLink()).getLanes();
332         Throw.when(names.size() != lanes.size(), IllegalArgumentException.class,
333                 "Size of 'names' not equal to the number of lanes.");
334         Collections.sort(lanes, new Comparator<Lane>()
335         {
336             
337             @Override
338             public int compare(final Lane o1, final Lane o2)
339             {
340                 int comp = o1.getDesignLineOffsetAtBegin().compareTo(o2.getDesignLineOffsetAtEnd());
341                 return linkPosition.getDirection().isPlus() ? comp : -comp;
342             }
343 
344         });
345         List<KpiLaneDirection> list = new ArrayList<>();
346         List<Length> positions = new ArrayList<>();
347         Speed speed = null;
348         for (Lane lane : lanes)
349         {
350             speed = speed == null ? lane.getLowestSpeedLimit() : Speed.min(speed, lane.getLowestSpeedLimit());
351             list.add(new KpiLaneDirection(new LaneData(lane),
352                     linkPosition.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS));
353             positions.add(lane.getLength().times(linkPosition.getFractionalLongitudinalPosition()));
354         }
355         return createCrossSection(names, list, positions, speed);
356     }
357 
358     
359 
360 
361 
362 
363 
364 
365 
366     public static GraphCrossSection<KpiLaneDirection> createCrossSection(final List<String> names,
367             final List<KpiLaneDirection> lanes, final List<Length> positions, final Speed speed)
368     {
369         Section<KpiLaneDirection> section = new Section<KpiLaneDirection>()
370         {
371             
372             @Override
373             public Iterator<KpiLaneDirection> iterator()
374             {
375                 return lanes.iterator();
376             }
377 
378             
379             @Override
380             public Length getLength()
381             {
382                 return lanes.get(0).getLaneData().getLength();
383             }
384 
385             
386             @Override
387             public Speed getSpeedLimit()
388             {
389                 return speed;
390             }
391 
392             
393             @Override
394             public KpiLaneDirection getSource(final int series)
395             {
396                 return lanes.get(series);
397             }
398         };
399         return new GraphCrossSection<>(names, section, positions);
400     }
401 
402 }