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