MultiTreeMetricsExtractor.java
/* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.matrix;
import com.graphhopper.coll.GHLongObjectHashMap;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.weighting.FastestWeighting;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.RoutingCHEdgeIteratorState;
import com.graphhopper.storage.RoutingCHGraph;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
import org.heigit.ors.common.DistanceUnit;
import org.heigit.ors.routing.graphhopper.extensions.storages.MultiTreeSPEntry;
import org.heigit.ors.routing.graphhopper.extensions.storages.MultiTreeSPEntryItem;
import org.heigit.ors.util.DistanceUnitUtil;
public class MultiTreeMetricsExtractor {
private final int metrics;
private final Graph graph;
private final Weighting weighting;
private final Weighting timeWeighting;
private final DistanceUnit distUnits;
private final GHLongObjectHashMap<MetricsItem> edgeMetrics;
private final long maxEdgeId;
private final RoutingCHGraph chGraph;
private double edgeDistance;
private double edgeWeight;
private double edgeTime;
private boolean reverseOrder = true;
private boolean swap;
public MultiTreeMetricsExtractor(int metrics, RoutingCHGraph chGraph, FlagEncoder encoder, Weighting weighting,
DistanceUnit units) {
this.metrics = metrics;
this.graph = chGraph.getBaseGraph();
this.weighting = weighting;
this.chGraph = chGraph;
assert this.chGraph != null;
maxEdgeId = this.chGraph.getEdges();
timeWeighting = new FastestWeighting(encoder);
distUnits = units;
edgeMetrics = new GHLongObjectHashMap<>();
}
public void setSwap(boolean swap) {
this.swap = swap;
}
public void setEmptyValues(int sourceIndex, MatrixLocations dstData, float[] times, float[] distances, float[] weights) {
int i = sourceIndex * dstData.size();
int[] targetNodes = dstData.getNodeIds();
for (int c = 0; c < targetNodes.length; c++) {
if (times != null)
times[i] = -1;
if (distances != null)
distances[i] = -1;
if (weights != null)
weights[i] = -1;
i++;
}
}
public void calcValues(MultiTreeSPEntry[] targets, MatrixLocations srcData, MatrixLocations dstData, float[] times,
float[] distances, float[] weights) throws Exception {
if (targets == null)
throw new IllegalStateException("Target destinations not set");
int index;
double pathTime;
double pathDistance;
double pathWeight;
long entryHash = 0;
boolean calcTime = MatrixMetricsType.isSet(metrics, MatrixMetricsType.DURATION);
boolean calcDistance = MatrixMetricsType.isSet(metrics, MatrixMetricsType.DISTANCE);
boolean calcWeight = MatrixMetricsType.isSet(metrics, MatrixMetricsType.WEIGHT);
MetricsItem edgeMetricsItem;
MultiTreeSPEntryItem sptItem;
for (int i = 0; i < targets.length; ++i) {
int srcNode = 0;
for (int j = 0; j < srcData.size(); ++j) {
pathTime = -1;
pathDistance = -1;
pathWeight = -1;
index = j * dstData.size() + i;
if (srcData.getNodeId(j) != -1) {
MultiTreeSPEntry targetEntry = targets[i];
if (targetEntry != null) {
//Only set values to 0 if target and start node are the same
sptItem = targetEntry.getItem(srcNode);
if (srcData.getNodeId(j) == targetEntry.getAdjNode() || sptItem.getParent() != null) {
pathTime = 0.0;
pathDistance = 0.0;
pathWeight = 0.0;
}
if (sptItem.getParent() != null) {
while (EdgeIterator.Edge.isValid(sptItem.getEdge())) {
edgeMetricsItem = null;
if (edgeMetrics != null) {
entryHash = getMultiTreeSPEntryHash(targetEntry, srcNode);
edgeMetricsItem = edgeMetrics.get(entryHash);
}
if (edgeMetricsItem == null) {
if (chGraph != null) {
RoutingCHEdgeIteratorState iterState = chGraph.getEdgeIteratorState(sptItem.getEdge(), targetEntry.getAdjNode());
if (iterState.isShortcut()) {
if (chGraph.getLevel(iterState.getBaseNode()) >= chGraph.getLevel(iterState.getAdjNode())) {
reverseOrder = true;
extractEdgeValues(iterState, swap);
} else {
reverseOrder = false;
extractEdgeValues(iterState, !swap);
}
} else {
extractEdgeValues(iterState, swap);
}
edgeDistance = (distUnits == DistanceUnit.METERS) ? edgeDistance
: DistanceUnitUtil.convert(edgeDistance, DistanceUnit.METERS, distUnits);
} else {
EdgeIteratorState iter = graph.getEdgeIteratorState(sptItem.getEdge(),
targetEntry.getAdjNode());
if (calcDistance)
edgeDistance = (distUnits == DistanceUnit.METERS) ? iter.getDistance()
: DistanceUnitUtil.convert(iter.getDistance(), DistanceUnit.METERS,
distUnits);
if (calcTime)
edgeTime = timeWeighting.calcEdgeMillis(iter, false, EdgeIterator.NO_EDGE)
/ 1000.0;
if (calcWeight)
edgeWeight = weighting.calcEdgeWeight(iter, false, EdgeIterator.NO_EDGE);
}
if (edgeMetrics != null) {
edgeMetricsItem = new MetricsItem();
edgeMetricsItem.distance = edgeDistance;
edgeMetricsItem.time = edgeTime;
edgeMetricsItem.weight = edgeWeight;
edgeMetrics.put(entryHash, edgeMetricsItem);
}
pathDistance += edgeDistance;
pathTime += edgeTime;
pathWeight += edgeWeight;
} else {
if (calcDistance)
pathDistance += edgeMetricsItem.distance;
if (calcTime)
pathTime += edgeMetricsItem.time;
if (calcWeight)
pathWeight += edgeMetricsItem.weight;
}
targetEntry = sptItem.getParent();
if (targetEntry == null)
break;
sptItem = targetEntry.getItem(srcNode);
}
}
}
srcNode++;
}
if (calcTime)
times[index] = (float) pathTime;
if (calcDistance)
distances[index] = (float) pathDistance;
if (calcWeight)
weights[index] = (float) pathWeight;
}
}
}
private long getMultiTreeSPEntryHash(MultiTreeSPEntry entry, int sptEntry) {
return entry.getAdjNode() * maxEdgeId + entry.getItem(sptEntry).getEdge();
}
private void extractEdgeValues(RoutingCHEdgeIteratorState iterState, boolean reverse) {
if (iterState.isShortcut()) {
edgeDistance = 0.0;
edgeTime = 0.0;
edgeWeight = 0.0;
if ((chGraph.getLevel(iterState.getBaseNode()) < chGraph.getLevel(iterState.getAdjNode())))
reverse = !reverse;
expandEdge(iterState, reverse);
} else {
EdgeIteratorState baseIterator = chGraph.getBaseGraph().getEdgeIteratorState(iterState.getOrigEdge(), iterState.getAdjNode());
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DISTANCE))
edgeDistance = baseIterator.getDistance();
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DURATION))
edgeTime = weighting.calcEdgeMillis(baseIterator, reverse, EdgeIterator.NO_EDGE) / 1000.0;
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.WEIGHT))
edgeWeight = weighting.calcEdgeWeight(baseIterator, reverse, EdgeIterator.NO_EDGE);
}
}
private void expandEdge(RoutingCHEdgeIteratorState iterState, boolean reverse) {
if (!iterState.isShortcut()) {
EdgeIteratorState baseIterator = chGraph.getBaseGraph().getEdgeIteratorState(iterState.getOrigEdge(), iterState.getAdjNode());
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DISTANCE))
edgeDistance += baseIterator.getDistance();
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DURATION))
edgeTime += weighting.calcEdgeMillis(baseIterator, reverse, EdgeIterator.NO_EDGE) / 1000.0;
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.WEIGHT))
edgeWeight += weighting.calcEdgeWeight(baseIterator, reverse, EdgeIterator.NO_EDGE);
return;
}
int skippedEdge1 = iterState.getSkippedEdge1();
int skippedEdge2 = iterState.getSkippedEdge2();
int from = iterState.getBaseNode();
int to = iterState.getAdjNode();
// get properties like speed of the edge in the correct direction
if (reverse) {
int tmp = from;
from = to;
to = tmp;
}
// getEdgeProps could possibly return an empty edge if the shortcut is
// available for both directions
if (reverseOrder) {
RoutingCHEdgeIteratorState edgeState = chGraph.getEdgeIteratorState(skippedEdge1, to);
boolean empty = edgeState == null;
if (empty)
edgeState = chGraph.getEdgeIteratorState(skippedEdge2, to);
expandEdge(edgeState, false);
if (empty)
edgeState = chGraph.getEdgeIteratorState(skippedEdge1, from);
else
edgeState = chGraph.getEdgeIteratorState(skippedEdge2, from);
expandEdge(edgeState, true);
} else {
RoutingCHEdgeIteratorState edgeState = chGraph.getEdgeIteratorState(skippedEdge1, from);
boolean empty = edgeState == null;
if (empty)
edgeState = chGraph.getEdgeIteratorState(skippedEdge2, from);
expandEdge(edgeState, true);
if (empty)
edgeState = chGraph.getEdgeIteratorState(skippedEdge1, to);
else
edgeState = chGraph.getEdgeIteratorState(skippedEdge2, to);
expandEdge(edgeState, false);
}
}
private static class MetricsItem {
private double time;
private double distance;
private double weight;
public double getTime() {
return time;
}
public void setTime(double time) {
this.time = time;
}
public double getDistance() {
return distance;
}
public void setDistance(double distance) {
this.distance = distance;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
}