All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.routing.AbstractBidirCHAlgo Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.0
Show newest version
/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.graphhopper.routing;

import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.routing.ch.NodeBasedCHBidirPathExtractor;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.storage.*;
import com.graphhopper.util.GHUtility;

import java.util.PriorityQueue;
import java.util.function.Supplier;

import static com.graphhopper.util.EdgeIterator.ANY_EDGE;

/**
 * Common subclass for bidirectional CH algorithms.
 * 

* * @author Peter Karich * @author easbar * @see AbstractNonCHBidirAlgo for non-CH bidirectional algorithms */ public abstract class AbstractBidirCHAlgo extends AbstractBidirAlgo implements EdgeToEdgeRoutingAlgorithm { protected final RoutingCHGraph graph; protected final NodeAccess nodeAccess; protected RoutingCHEdgeExplorer inEdgeExplorer; protected RoutingCHEdgeExplorer outEdgeExplorer; protected CHEdgeFilter levelEdgeFilter; private Supplier pathExtractorSupplier; public AbstractBidirCHAlgo(RoutingCHGraph graph, TraversalMode tMode) { super(tMode); this.graph = graph; if (graph.hasTurnCosts() && !tMode.isEdgeBased()) throw new IllegalStateException("Weightings supporting turn costs cannot be used with node-based traversal mode"); this.nodeAccess = graph.getBaseGraph().getNodeAccess(); outEdgeExplorer = graph.createOutEdgeExplorer(); inEdgeExplorer = graph.createInEdgeExplorer(); levelEdgeFilter = new CHLevelEdgeFilter(graph); pathExtractorSupplier = () -> new NodeBasedCHBidirPathExtractor(graph); int size = Math.min(Math.max(200, graph.getNodes() / 10), 150_000); initCollections(size); } @Override protected void initCollections(int size) { super.initCollections(Math.min(size, 2000)); } /** * Creates a new entry of the shortest path tree (a {@link SPTEntry} or one of its subclasses) during a dijkstra * expansion. * * @param edge the id of the edge that is currently processed for the expansion * @param adjNode the adjacent node of the edge * @param incEdge the id of the edge that is incoming to the node the edge is pointed at. usually this is the same as * edge, but for edge-based CH and in case edge corresponds to a shortcut incEdge is the original edge * that is incoming to the node * @param weight the weight the shortest path three entry should carry * @param parent the parent entry of in the shortest path tree * @param reverse true if we are currently looking at the backward search, false otherwise */ protected abstract SPTEntry createEntry(int edge, int adjNode, int incEdge, double weight, SPTEntry parent, boolean reverse); @Override protected void postInitFrom() { if (fromOutEdge == ANY_EDGE) { fillEdgesFromUsingFilter(levelEdgeFilter); } else { // need to use a local reference here, because levelEdgeFilter is modified when calling fillEdgesFromUsingFilter final CHEdgeFilter tmpFilter = levelEdgeFilter; fillEdgesFromUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyFirst()) == fromOutEdge); } } @Override protected void postInitTo() { if (toInEdge == ANY_EDGE) { fillEdgesToUsingFilter(levelEdgeFilter); } else { final CHEdgeFilter tmpFilter = levelEdgeFilter; fillEdgesToUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyLast()) == toInEdge); } } /** * @param edgeFilter edge filter used to fill edges. the {@link #levelEdgeFilter} reference will be set to * edgeFilter by this method, so make sure edgeFilter does not use it directly. */ protected void fillEdgesFromUsingFilter(CHEdgeFilter edgeFilter) { // we temporarily ignore the additionalEdgeFilter CHEdgeFilter tmpFilter = levelEdgeFilter; levelEdgeFilter = edgeFilter; finishedFrom = !fillEdgesFrom(); levelEdgeFilter = tmpFilter; } /** * @see #fillEdgesFromUsingFilter(CHEdgeFilter) */ protected void fillEdgesToUsingFilter(CHEdgeFilter edgeFilter) { // we temporarily ignore the additionalEdgeFilter CHEdgeFilter tmpFilter = levelEdgeFilter; levelEdgeFilter = edgeFilter; finishedTo = !fillEdgesTo(); levelEdgeFilter = tmpFilter; } @Override public boolean finished() { // we need to finish BOTH searches for CH! if (finishedFrom && finishedTo) return true; // changed also the final finish condition for CH return currFrom.weight >= bestWeight && currTo.weight >= bestWeight; } @Override boolean fillEdgesFrom() { while (true) { if (pqOpenSetFrom.isEmpty()) return false; currFrom = pqOpenSetFrom.poll(); if (!currFrom.isDeleted()) break; } visitedCountFrom++; if (fromEntryCanBeSkipped()) { return true; } if (fwdSearchCanBeStopped()) { return false; } bestWeightMapOther = bestWeightMapTo; fillEdges(currFrom, pqOpenSetFrom, bestWeightMapFrom, outEdgeExplorer, false); return true; } @Override boolean fillEdgesTo() { while (true) { if (pqOpenSetTo.isEmpty()) return false; currTo = pqOpenSetTo.poll(); if (!currTo.isDeleted()) break; } visitedCountTo++; if (toEntryCanBeSkipped()) { return true; } if (bwdSearchCanBeStopped()) { return false; } bestWeightMapOther = bestWeightMapFrom; fillEdges(currTo, pqOpenSetTo, bestWeightMapTo, inEdgeExplorer, true); return true; } private void fillEdges(SPTEntry currEdge, PriorityQueue prioQueue, IntObjectMap bestWeightMap, RoutingCHEdgeExplorer explorer, boolean reverse) { RoutingCHEdgeIterator iter = explorer.setBaseNode(currEdge.adjNode); while (iter.next()) { if (!accept(iter, currEdge, reverse)) continue; final double weight = calcWeight(iter, currEdge, reverse); if (Double.isInfinite(weight)) { continue; } final int origEdgeId = GHUtility.getEdgeFromEdgeKey(reverse ? iter.getOrigEdgeKeyFirst() : iter.getOrigEdgeKeyLast()); final int traversalId = traversalMode.createTraversalId(iter, reverse); SPTEntry entry = bestWeightMap.get(traversalId); if (entry == null) { entry = createEntry(iter.getEdge(), iter.getAdjNode(), origEdgeId, weight, currEdge, reverse); bestWeightMap.put(traversalId, entry); prioQueue.add(entry); } else if (entry.getWeightOfVisitedPath() > weight) { // flagging this entry, so it will be ignored when it is polled the next time // this is faster than removing the entry from the queue and adding again, but for CH it does not really // make a difference overall. entry.setDeleted(); boolean isBestEntry = reverse ? (entry == bestBwdEntry) : (entry == bestFwdEntry); entry = createEntry(iter.getEdge(), iter.getAdjNode(), origEdgeId, weight, currEdge, reverse); bestWeightMap.put(traversalId, entry); prioQueue.add(entry); // if this is the best entry we need to update the best reference as well if (isBestEntry) if (reverse) bestBwdEntry = entry; else bestFwdEntry = entry; } else continue; if (updateBestPath) { // use dummy value for edge weight as it is used for neither node- nor edge-based CH updateBestPath(Double.POSITIVE_INFINITY, entry, origEdgeId, traversalId, reverse); } } } protected double calcWeight(RoutingCHEdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { double edgeWeight = edgeState.getWeight(reverse); final int origEdgeId = GHUtility.getEdgeFromEdgeKey(reverse ? edgeState.getOrigEdgeKeyLast() : edgeState.getOrigEdgeKeyFirst()); double turnCosts = reverse ? graph.getTurnWeight(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId) : graph.getTurnWeight(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId); return edgeWeight + turnCosts; } protected void updateEntry(SPTEntry entry, int edge, int adjNode, int incEdge, double weight, SPTEntry parent, boolean reverse) { entry.edge = edge; entry.weight = weight; entry.parent = parent; } protected boolean accept(RoutingCHEdgeIteratorState edge, SPTEntry currEdge, boolean reverse) { // for edge-based traversal we leave it for TurnWeighting to decide whether or not a u-turn is acceptable, // but for node-based traversal we exclude such a turn for performance reasons already here if (!traversalMode.isEdgeBased() && edge.getEdge() == getIncomingEdge(currEdge)) return false; return levelEdgeFilter == null || levelEdgeFilter.accept(edge); } protected double calcWeight(RoutingCHEdgeIteratorState iter, SPTEntry currEdge, boolean reverse) { return calcWeight(iter, reverse, getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath(); } @Override protected double getInEdgeWeight(SPTEntry entry) { return graph.getEdgeIteratorState(getIncomingEdge(entry), entry.adjNode).getWeight(false); } @Override protected Path extractPath() { if (finished()) return createPathExtractor().extract(bestFwdEntry, bestBwdEntry, bestWeight); return createEmptyPath(); } public void setPathExtractorSupplier(Supplier pathExtractorSupplier) { this.pathExtractorSupplier = pathExtractorSupplier; } BidirPathExtractor createPathExtractor() { return pathExtractorSupplier.get(); } protected Path createEmptyPath() { return new Path(graph.getBaseGraph()); } @Override public String toString() { return getName() + "|" + graph.getWeighting(); } private static class CHLevelEdgeFilter implements CHEdgeFilter { private final RoutingCHGraph graph; private final int maxNodes; public CHLevelEdgeFilter(RoutingCHGraph graph) { this.graph = graph; maxNodes = graph.getBaseGraph().getBaseGraph().getNodes(); } @Override public boolean accept(RoutingCHEdgeIteratorState edgeState) { int base = edgeState.getBaseNode(); int adj = edgeState.getAdjNode(); // always accept virtual edges, see #288 if (base >= maxNodes || adj >= maxNodes) return true; // minor performance improvement: shortcuts in wrong direction are disconnected, so no need to exclude them if (edgeState.isShortcut()) return true; return graph.getLevel(base) <= graph.getLevel(adj); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy