GraphLaneUtil.java

  1. package org.opentrafficsim.draw.graphs.road;

  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.Comparator;
  5. import java.util.Iterator;
  6. import java.util.LinkedHashMap;
  7. import java.util.LinkedHashSet;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Set;

  11. import org.djunits.value.vdouble.scalar.Length;
  12. import org.djunits.value.vdouble.scalar.Speed;
  13. import org.djutils.exceptions.Throw;
  14. import org.djutils.immutablecollections.ImmutableSet;
  15. import org.opentrafficsim.core.network.Link;
  16. import org.opentrafficsim.core.network.LinkPosition;
  17. import org.opentrafficsim.core.network.NetworkException;
  18. import org.opentrafficsim.draw.graphs.GraphCrossSection;
  19. import org.opentrafficsim.draw.graphs.GraphPath;
  20. import org.opentrafficsim.draw.graphs.GraphPath.Section;
  21. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  22. import org.opentrafficsim.road.network.lane.Lane;
  23. import org.opentrafficsim.road.network.lane.LanePosition;
  24. import org.opentrafficsim.road.network.sampling.LaneDataRoad;

  25. /**
  26.  * Utilities to create {@code GraphPath}s and {@code GraphCrossSection}s for graphs, based on lanes.
  27.  * <p>
  28.  * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  29.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  30.  * </p>
  31.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  32.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  33.  * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  34.  */
  35. public final class GraphLaneUtil
  36. {

  37.     /**
  38.      * Constructor.
  39.      */
  40.     private GraphLaneUtil()
  41.     {
  42.         //
  43.     }

  44.     /**
  45.      * Creates a path starting at the provided lane and moving downstream until a dead-end, split, or loop.
  46.      * @param name String; path name
  47.      * @param first Lane; first lane
  48.      * @return GraphPath&lt;LaneDataRoad&gt; path
  49.      * @throws NetworkException when the lane does not have any set speed limit
  50.      */
  51.     public static GraphPath<LaneDataRoad> createPath(final String name, final Lane first) throws NetworkException
  52.     {
  53.         Throw.whenNull(name, "Name may not be null.");
  54.         Throw.whenNull(first, "First may not be null.");
  55.         List<Section<LaneDataRoad>> sections = new ArrayList<>();
  56.         Set<Lane> set = new LinkedHashSet<>();
  57.         Lane lane = first;
  58.         while (lane != null && !set.contains(lane))
  59.         {
  60.             LaneDataRoad laneData = new LaneDataRoad(lane);
  61.             List<LaneDataRoad> list = new ArrayList<>();
  62.             list.add(laneData);
  63.             Speed speed = lane.getLowestSpeedLimit();
  64.             Length length = lane.getLength();
  65.             sections.add(new Section<>(length, speed, list));
  66.             set.add(lane);
  67.             Set<Lane> nextLaneSet = lane.nextLanes(null);
  68.             if (nextLaneSet.size() == 1)
  69.             {
  70.                 lane = nextLaneSet.iterator().next();
  71.             }
  72.         }
  73.         return new GraphPath<>(name, sections);
  74.     }

  75.     /**
  76.      * Creates a path starting at the provided lanes and moving downstream for as long as no lane finds a loop (on to any of the
  77.      * lanes) and there's a unique link all lanes have downstream. The length and speed limit are taken from the first lane.
  78.      * @param names List&lt;String&gt;; lane names
  79.      * @param first List&lt;Lane&gt;; first lanes
  80.      * @return GraphPath&lt;LaneDataRoad&gt; path
  81.      * @throws NetworkException when a lane does not have any set speed limit
  82.      */
  83.     public static GraphPath<LaneDataRoad> createPath(final List<String> names, final List<Lane> first) throws NetworkException
  84.     {
  85.         Throw.whenNull(names, "Names may not be null.");
  86.         Throw.whenNull(first, "First may not be null.");
  87.         Throw.when(names.size() != first.size(), IllegalArgumentException.class, "Size of 'names' and 'first' must be equal.");
  88.         List<Section<LaneDataRoad>> sections = new ArrayList<>();
  89.         Set<Lane> set = new LinkedHashSet<>();
  90.         List<Lane> lanes = first;
  91.         while (lanes != null && Collections.disjoint(set, lanes))
  92.         {
  93.             List<LaneDataRoad> list = new ArrayList<>();
  94.             Speed speed = null;
  95.             for (Lane lane : lanes)
  96.             {
  97.                 if (lane == null)
  98.                 {
  99.                     list.add(null);
  100.                     continue;
  101.                 }
  102.                 speed = speed == null ? lane.getLowestSpeedLimit() : Speed.min(speed, lane.getLowestSpeedLimit());
  103.                 list.add(new LaneDataRoad(lane));
  104.             }
  105.             Speed finalSpeed = speed;
  106.             Lane firstNextLane = null;
  107.             for (Lane lane : lanes)
  108.             {
  109.                 if (lane != null)
  110.                 {
  111.                     firstNextLane = lane;
  112.                     continue;
  113.                 }
  114.             }
  115.             Length length = firstNextLane.getLength();
  116.             sections.add(new Section<>(length, finalSpeed, list));
  117.             set.addAll(lanes);
  118.             // per link and then per lane, find the downstream lane
  119.             Map<Link, List<Lane>> linkMap = new LinkedHashMap<>();
  120.             Link link = firstNextLane.getParentLink();
  121.             ImmutableSet<Link> links = link.getEndNode().getLinks();
  122.             for (Link nextLink : links)
  123.             {
  124.                 if (!link.equals(nextLink))
  125.                 {
  126.                     List<Lane> nextLanes = new ArrayList<>();
  127.                     for (Lane nextLane : lanes)
  128.                     {
  129.                         Set<Lane> nextLaneSet = nextLane.nextLanes(null);
  130.                         int n = 0;
  131.                         for (Lane nl : nextLaneSet)
  132.                         {
  133.                             if (nl.getParentLink().equals(nextLink))
  134.                             {
  135.                                 n++;
  136.                                 nextLanes.add(nl);
  137.                             }
  138.                         }
  139.                         if (n > 1)
  140.                         {
  141.                             // multiple downstream lanes of this lane go to this link, this is not allowed
  142.                             nextLanes.clear();
  143.                             break;
  144.                         }
  145.                         else if (n == 0)
  146.                         {
  147.                             nextLanes.add(null);
  148.                         }
  149.                     }
  150.                     if (nextLanes.size() == lanes.size())
  151.                     {
  152.                         linkMap.put(nextLink, nextLanes);
  153.                     }
  154.                 }
  155.             }
  156.             // in case there are multiple downstream links, remove all links for which some lanes had no downstream lane
  157.             if (linkMap.size() > 1)
  158.             {
  159.                 Iterator<List<Lane>> it = linkMap.values().iterator();
  160.                 while (it.hasNext())
  161.                 {
  162.                     if (it.next().contains(null))
  163.                     {
  164.                         it.remove();
  165.                     }
  166.                 }
  167.             }
  168.             if (linkMap.size() == 1)
  169.             {
  170.                 lanes = linkMap.values().iterator().next();
  171.             }
  172.             else
  173.             {
  174.                 lanes = null;
  175.             }
  176.         }
  177.         return new GraphPath<>(names, sections);
  178.     }

  179.     /**
  180.      * Creates a single-lane path.
  181.      * @param name String; name
  182.      * @param lane Lane; lane
  183.      * @return GraphPath&lt;LaneDataRoad&gt; path
  184.      * @throws NetworkException when a lane does not have any set speed limit
  185.      */
  186.     public static GraphPath<LaneDataRoad> createSingleLanePath(final String name, final Lane lane) throws NetworkException
  187.     {
  188.         List<LaneDataRoad> lanes = new ArrayList<>();
  189.         lanes.add(new LaneDataRoad(lane));
  190.         List<Section<LaneDataRoad>> sections = new ArrayList<>();
  191.         Speed speed = lane.getLowestSpeedLimit();
  192.         sections.add(new Section<>(lane.getLength(), speed, lanes));
  193.         return new GraphPath<>(name, sections);
  194.     }

  195.     /**
  196.      * Creates a cross section at the provided lane and position.
  197.      * @param name String; name
  198.      * @param lanePosition LanePosition; lane position
  199.      * @return GraphCrossSection&lt;LaneDataRoad&gt; cross section
  200.      * @throws NetworkException when the lane does not have any set speed limit
  201.      */
  202.     public static GraphCrossSection<LaneDataRoad> createCrossSection(final String name, final LanePosition lanePosition)
  203.             throws NetworkException
  204.     {
  205.         Throw.whenNull(name, "Name may not be null.");
  206.         Throw.whenNull(lanePosition, "Lane position may not be null.");
  207.         List<LaneDataRoad> list = new ArrayList<>();
  208.         List<String> names = new ArrayList<>();
  209.         List<Length> positions = new ArrayList<>();
  210.         names.add(name);
  211.         positions.add(lanePosition.getPosition());
  212.         list.add(new LaneDataRoad(lanePosition.getLane()));
  213.         Speed speed = lanePosition.getLane().getLowestSpeedLimit();
  214.         return createCrossSection(names, list, positions, speed);
  215.     }

  216.     /**
  217.      * Creates a cross section at the provided link and position.
  218.      * @param names List&lt;String&gt;; lane names
  219.      * @param linkPosition LinkPosition; link position
  220.      * @return GraphCrossSection&lt;LaneDataRoad&gt; cross section
  221.      * @throws NetworkException when a lane does not have any set speed limit
  222.      */
  223.     public static GraphCrossSection<LaneDataRoad> createCrossSection(final List<String> names, final LinkPosition linkPosition)
  224.             throws NetworkException
  225.     {
  226.         Throw.whenNull(names, "Names may not be null.");
  227.         Throw.whenNull(linkPosition, "Link position may not be null.");
  228.         Throw.when(!(linkPosition.getLink() instanceof CrossSectionLink), IllegalArgumentException.class,
  229.                 "The link is not a CrossEctionLink.");
  230.         List<Lane> lanes = ((CrossSectionLink) linkPosition.getLink()).getLanes();
  231.         Throw.when(names.size() != lanes.size(), IllegalArgumentException.class,
  232.                 "Size of 'names' not equal to the number of lanes.");
  233.         Collections.sort(lanes, new Comparator<Lane>()
  234.         {
  235.             /** {@ingeritDoc} */
  236.             @Override
  237.             public int compare(final Lane o1, final Lane o2)
  238.             {
  239.                 return o1.getDesignLineOffsetAtBegin().compareTo(o2.getDesignLineOffsetAtEnd());
  240.             }

  241.         });
  242.         List<LaneDataRoad> list = new ArrayList<>();
  243.         List<Length> positions = new ArrayList<>();
  244.         Speed speed = null;
  245.         for (Lane lane : lanes)
  246.         {
  247.             speed = speed == null ? lane.getLowestSpeedLimit() : Speed.min(speed, lane.getLowestSpeedLimit());
  248.             list.add(new LaneDataRoad(lane));
  249.             positions.add(lane.getLength().times(linkPosition.getFractionalLongitudinalPosition()));
  250.         }
  251.         return createCrossSection(names, list, positions, speed);
  252.     }

  253.     /**
  254.      * Creates a cross section.
  255.      * @param names List&lt;String&gt;;; names
  256.      * @param lanes List&lt;LaneDataRoad&gt;;; lanes
  257.      * @param positions List&lt;Length&gt;; positions
  258.      * @param speed Speed; speed
  259.      * @return GraphCrossSection&lt;LaneDataRoad&gt;; cross section
  260.      */
  261.     public static GraphCrossSection<LaneDataRoad> createCrossSection(final List<String> names, final List<LaneDataRoad> lanes,
  262.             final List<Length> positions, final Speed speed)
  263.     {
  264.         Section<LaneDataRoad> section = new Section<>(lanes.get(0).getLength(), speed, lanes);
  265.         return new GraphCrossSection<>(names, section, positions);
  266.     }

  267. }