FastIsochroneAlgorithm.java

  1. /*  This file is part of Openrouteservice.
  2.  *
  3.  *  Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
  4.  *  GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
  5.  *  of the License, or (at your option) any later version.

  6.  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  7.  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8.  *  See the GNU Lesser General Public License for more details.

  9.  *  You should have received a copy of the GNU Lesser General Public License along with this library;
  10.  *  if not, see <https://www.gnu.org/licenses/>.
  11.  */
  12. package org.heigit.ors.fastisochrones;

  13. import com.carrotsearch.hppc.IntObjectMap;
  14. import com.carrotsearch.hppc.cursors.IntObjectCursor;
  15. import com.graphhopper.coll.GHIntObjectHashMap;
  16. import com.graphhopper.routing.SPTEntry;
  17. import com.graphhopper.routing.util.EdgeFilter;
  18. import com.graphhopper.routing.util.TraversalMode;
  19. import com.graphhopper.routing.weighting.Weighting;
  20. import com.graphhopper.storage.Graph;
  21. import org.heigit.ors.fastisochrones.partitioning.storage.CellStorage;
  22. import org.heigit.ors.fastisochrones.partitioning.storage.IsochroneNodeStorage;
  23. import org.heigit.ors.fastisochrones.storage.BorderNodeDistanceStorage;
  24. import org.heigit.ors.fastisochrones.storage.EccentricityStorage;
  25. import org.heigit.ors.routing.graphhopper.extensions.edgefilters.EdgeFilterSequence;

  26. import java.util.*;

  27. /**
  28.  * Implementation of Fast Isochrones
  29.  * <p>
  30.  *
  31.  * @author Hendrik Leuschner
  32.  */
  33. public class FastIsochroneAlgorithm extends AbstractIsochroneAlgorithm {
  34.     private static final String NAME = "FastIsochrone";
  35.     protected IntObjectMap<SPTEntry> startCellMap;
  36.     protected Set<Integer> activeBorderNodes;
  37.     protected Set<Integer> inactiveBorderNodes;
  38.     protected Set<Integer> fullyReachableCells;
  39.     protected Map<Integer, Map<Integer, Double>> upAndCoreGraphDistMap;
  40.     protected Map<Integer, IntObjectMap<SPTEntry>> activeCellMaps;
  41.     int from;
  42.     int fromNonVirtual;

  43.     public FastIsochroneAlgorithm(Graph graph,
  44.                                   Weighting weighting,
  45.                                   TraversalMode tMode,
  46.                                   CellStorage cellStorage,
  47.                                   IsochroneNodeStorage isochroneNodeStorage,
  48.                                   EccentricityStorage eccentricityStorage,
  49.                                   BorderNodeDistanceStorage borderNodeDistanceStorage,
  50.                                   EdgeFilter additionalEdgeFilter) {
  51.         super(graph, weighting, tMode, cellStorage, isochroneNodeStorage, eccentricityStorage, borderNodeDistanceStorage, additionalEdgeFilter);
  52.     }

  53.     @Override
  54.     protected void initCollections(int size) {
  55.         startCellMap = new GHIntObjectHashMap<>(size);
  56.     }

  57.     @Override
  58.     public void init(int from, int fromNonVirtual, double isochroneLimit) {
  59.         this.from = from;
  60.         this.fromNonVirtual = fromNonVirtual;
  61.         this.isochroneLimit = isochroneLimit;
  62.         activeBorderNodes = new HashSet<>();
  63.         inactiveBorderNodes = new HashSet<>();
  64.         fullyReachableCells = new HashSet<>();
  65.         upAndCoreGraphDistMap = new HashMap<>();
  66.     }

  67.     @Override
  68.     void runStartCellPhase() {
  69.         int startCell = isochroneNodeStorage.getCellId(fromNonVirtual);
  70.         CoreRangeDijkstra coreRangeDijkstra = new CoreRangeDijkstra(graph, weighting, isochroneNodeStorage, borderNodeDistanceStorage);
  71.         EdgeFilterSequence edgeFilterSequence = new EdgeFilterSequence();
  72.         if (additionalEdgeFilter != null)
  73.             edgeFilterSequence.add(additionalEdgeFilter);
  74.         edgeFilterSequence.add(
  75.                 new CellAndBorderNodeFilter(isochroneNodeStorage,
  76.                         startCell,
  77.                         graph.getNodes())
  78.         );
  79.         coreRangeDijkstra.setEdgeFilter(edgeFilterSequence);
  80.         coreRangeDijkstra.setIsochroneLimit(isochroneLimit);
  81.         coreRangeDijkstra.initFrom(from);
  82.         coreRangeDijkstra.runAlgo();
  83.         startCellMap = coreRangeDijkstra.getFromMap();
  84.         findFullyReachableCells(startCellMap);

  85.         for (int inactiveBorderNode : inactiveBorderNodes) {
  86.             startCellMap.remove(inactiveBorderNode);
  87.             activeBorderNodes.remove(inactiveBorderNode);
  88.         }

  89.         for (int sweepEndNode : activeBorderNodes) {
  90.             double dist = coreRangeDijkstra.fromMap.get(sweepEndNode).getWeightOfVisitedPath();
  91.             int cell = isochroneNodeStorage.getCellId(sweepEndNode);
  92.             if (cell == startCell)
  93.                 continue;
  94.             if (!upAndCoreGraphDistMap.containsKey(cell))
  95.                 upAndCoreGraphDistMap.put(cell, new HashMap<>());
  96.             upAndCoreGraphDistMap.get(cell).put(sweepEndNode, dist);
  97.             startCellMap.remove(sweepEndNode);
  98.         }
  99.     }

  100.     @Override
  101.     public boolean finishedStartCellPhase() {
  102.         return true;
  103.     }

  104.     /**
  105.      * Run the ALT algo in the core
  106.      */
  107.     @Override
  108.     void runBorderNodePhase() {
  109.         //This implementation combines running in the start cell and running on the border nodes in one phase
  110.     }

  111.     @Override
  112.     public boolean finishedBorderNodePhase() {
  113.         //This implementation combines running in the start cell and running on the border nodes in one phase
  114.         return true;
  115.     }

  116.     @Override
  117.     void runActiveCellPhase() {
  118.         activeCellMaps = new HashMap<>(upAndCoreGraphDistMap.entrySet().size());
  119.         activeCellMaps.put(isochroneNodeStorage.getCellId(fromNonVirtual), startCellMap);
  120.         for (Map.Entry<Integer, Map<Integer, Double>> entry : upAndCoreGraphDistMap.entrySet()) {
  121.             ActiveCellDijkstra activeCellDijkstra = new ActiveCellDijkstra(graph, weighting, isochroneNodeStorage, entry.getKey());
  122.             activeCellDijkstra.setIsochroneLimit(isochroneLimit);
  123.             //Add all the start points with their respective already visited weight
  124.             for (int nodeId : entry.getValue().keySet()) {
  125.                 activeCellDijkstra.addInitialBordernode(nodeId, entry.getValue().get(nodeId));
  126.             }
  127.             activeCellDijkstra.init();
  128.             activeCellDijkstra.runAlgo();
  129.             activeCellMaps.put(entry.getKey(), activeCellDijkstra.getFromMap());
  130.         }
  131.     }

  132.     @Override
  133.     public boolean finishedActiveCellPhase() {
  134.         return false;
  135.     }

  136.     private void findFullyReachableCells(IntObjectMap<SPTEntry> entryMap) {
  137.         for (IntObjectCursor<SPTEntry> entry : entryMap) {
  138.             int baseNode = entry.key;
  139.             if (!isochroneNodeStorage.getBorderness(baseNode))
  140.                 continue;
  141.             SPTEntry sptEntry = entry.value;
  142.             int baseCell = isochroneNodeStorage.getCellId(baseNode);
  143.             int eccentricity = eccentricityStorage.getEccentricity(baseNode);
  144.             if (isWithinLimit(sptEntry, eccentricity)
  145.                     && eccentricityStorage.getFullyReachable(baseNode)) {
  146.                 addFullyReachableCell(baseCell);
  147.                 addInactiveBorderNode(baseNode);
  148.             } else {
  149.                 if (!getFullyReachableCells().contains(baseCell)) {
  150.                     addActiveBorderNode(baseNode);
  151.                 }
  152.             }
  153.         }
  154.     }

  155.     /**
  156.      * Consider all active cells that have a percentage of *approximation* of their nodes visited to be fully reachable.
  157.      *
  158.      * @param approximation factor of approximation. 1 means all nodes must be found, 0 means no nodes have to be found.
  159.      */
  160.     public void approximateActiveCells(double approximation) {
  161.         Iterator<Map.Entry<Integer, IntObjectMap<SPTEntry>>> activeCellIterator = getActiveCellMaps().entrySet().iterator();
  162.         while (activeCellIterator.hasNext()) {
  163.             Map.Entry<Integer, IntObjectMap<SPTEntry>> activeCell = activeCellIterator.next();
  164.             if (activeCell.getValue().size() / (double) cellStorage.getNodesOfCell(activeCell.getKey()).size() > approximation) {
  165.                 activeCellIterator.remove();
  166.                 getFullyReachableCells().add(activeCell.getKey());
  167.             }
  168.         }
  169.     }

  170.     private boolean isWithinLimit(SPTEntry sptEntry, int eccentricity) {
  171.         return sptEntry.getWeightOfVisitedPath() + eccentricity <= isochroneLimit;
  172.     }

  173.     private void addFullyReachableCell(int cellId) {
  174.         fullyReachableCells.add(cellId);
  175.     }

  176.     protected void addActiveBorderNode(int nodeId) {
  177.         activeBorderNodes.add(nodeId);
  178.     }

  179.     protected void addInactiveBorderNode(int nodeId) {
  180.         inactiveBorderNodes.add(nodeId);
  181.     }

  182.     public Set<Integer> getFullyReachableCells() {
  183.         return fullyReachableCells;
  184.     }

  185.     public IntObjectMap<SPTEntry> getStartCellMap() {
  186.         return startCellMap;
  187.     }

  188.     public String getName() {
  189.         return NAME;
  190.     }

  191.     public Map<Integer, IntObjectMap<SPTEntry>> getActiveCellMaps() {
  192.         return activeCellMaps;
  193.     }
  194. }