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

com.graphhopper.routing.DijkstraOneToMany 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.IntArrayList;
import com.graphhopper.apache.commons.collections.IntFloatBinaryHeap;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.*;

import java.util.Arrays;

/**
 * A simple dijkstra tuned to perform multiple one to many queries with the same source and different target nodes
 * more efficiently than {@link Dijkstra}. Old data structures are cached between requests and potentially reused and
 * the shortest path tree is stored in (large as the graph) arrays instead of hash maps.
 * 

* * @author Peter Karich */ public class DijkstraOneToMany extends AbstractRoutingAlgorithm { private static final int EMPTY_PARENT = -1; private static final int NOT_FOUND = -1; private final IntArrayListWithCap changedNodes; protected double[] weights; private int[] parents; private int[] edgeIds; private IntFloatBinaryHeap heap; private int visitedNodes; private boolean doClear = true; private int endNode; private int currNode, fromNode, to; private double weightLimit = Double.MAX_VALUE; public DijkstraOneToMany(Graph graph, Weighting weighting, TraversalMode tMode) { super(graph, weighting, tMode); parents = new int[graph.getNodes()]; Arrays.fill(parents, EMPTY_PARENT); edgeIds = new int[graph.getNodes()]; Arrays.fill(edgeIds, EdgeIterator.NO_EDGE); weights = new double[graph.getNodes()]; Arrays.fill(weights, Double.MAX_VALUE); heap = new IntFloatBinaryHeap(1000); changedNodes = new IntArrayListWithCap(); } @Override public Path calcPath(int from, int to) { setupFinishTime(); fromNode = from; endNode = findEndNode(from, to); if (endNode < 0 || isWeightLimitExceeded()) { Path path = createEmptyPath(); path.setFromNode(fromNode); path.setEndNode(endNode); return path; } Path path = new Path(graph); int node = endNode; while (true) { int edge = edgeIds[node]; if (!EdgeIterator.Edge.isValid(edge)) { break; } EdgeIteratorState edgeState = graph.getEdgeIteratorState(edge, node); path.addDistance(edgeState.getDistance()); // todo: we do not yet account for turn times here! path.addTime(weighting.calcEdgeMillis(edgeState, false)); path.addEdge(edge); node = parents[node]; } ArrayUtil.reverse(path.getEdges()); path.setFromNode(fromNode); path.setEndNode(endNode); path.setFound(true); path.setWeight(weights[endNode]); return path; } /** * Call clear if you have a different start node and need to clear the cache. */ public DijkstraOneToMany clear() { doClear = true; return this; } public double getWeight(int endNode) { return weights[endNode]; } public int findEndNode(int from, int to) { if (weights.length < 2) return NOT_FOUND; this.to = to; if (doClear) { doClear = false; int vn = changedNodes.size(); for (int i = 0; i < vn; i++) { int n = changedNodes.get(i); weights[n] = Double.MAX_VALUE; parents[n] = EMPTY_PARENT; edgeIds[n] = EdgeIterator.NO_EDGE; } heap.clear(); // changedNodes.clear(); changedNodes.elementsCount = 0; currNode = from; if (!traversalMode.isEdgeBased()) { weights[currNode] = 0; changedNodes.add(currNode); } } else { // Cached! Re-use existing data structures int parentNode = parents[to]; if (parentNode != EMPTY_PARENT && weights[to] <= weights[currNode]) return to; if (heap.isEmpty() || isMaxVisitedNodesExceeded() || isTimeoutExceeded()) return NOT_FOUND; currNode = heap.poll(); } visitedNodes = 0; // we call 'finished' before heap.peekElement but this would add unnecessary overhead for this special case so we do it outside of the loop if (finished()) { // then we need a small workaround for special cases see #707 if (heap.isEmpty()) doClear = true; return currNode; } while (true) { visitedNodes++; EdgeIterator iter = edgeExplorer.setBaseNode(currNode); while (iter.next()) { int adjNode = iter.getAdjNode(); int prevEdgeId = edgeIds[adjNode]; if (!accept(iter, prevEdgeId)) continue; double tmpWeight = GHUtility.calcWeightWithTurnWeight(weighting, iter, false, prevEdgeId) + weights[currNode]; if (Double.isInfinite(tmpWeight)) continue; double w = weights[adjNode]; if (w == Double.MAX_VALUE) { parents[adjNode] = currNode; weights[adjNode] = tmpWeight; heap.insert(tmpWeight, adjNode); changedNodes.add(adjNode); edgeIds[adjNode] = iter.getEdge(); } else if (w > tmpWeight) { parents[adjNode] = currNode; weights[adjNode] = tmpWeight; heap.update(tmpWeight, adjNode); changedNodes.add(adjNode); edgeIds[adjNode] = iter.getEdge(); } } if (heap.isEmpty() || isMaxVisitedNodesExceeded() || isWeightLimitExceeded() || isTimeoutExceeded()) return NOT_FOUND; // calling just peek and not poll is important if the next query is cached currNode = heap.peekElement(); if (finished()) return currNode; heap.poll(); } } private boolean finished() { return currNode == to; } public void setWeightLimit(double weightLimit) { this.weightLimit = weightLimit; } protected boolean isWeightLimitExceeded() { return weights[currNode] > weightLimit; } public void close() { weights = null; parents = null; edgeIds = null; heap = null; } @Override public int getVisitedNodes() { return visitedNodes; } @Override public String getName() { return Parameters.Algorithms.DIJKSTRA_ONE_TO_MANY; } /** * List currently used memory in MB (approximately) */ public String getMemoryUsageAsString() { long len = weights.length; return ((8L + 4L + 4L) * len + changedNodes.getCapacity() * 4L + heap.getCapacity() * (4L + 4L)) / Helper.MB + "MB"; } private static class IntArrayListWithCap extends IntArrayList { public IntArrayListWithCap() { } public int getCapacity() { return buffer.length; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy