PathMetricsExtractor.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.SPTEntry;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
import org.heigit.ors.common.DistanceUnit;
import org.heigit.ors.exceptions.StatusCodeException;
import org.heigit.ors.util.DistanceUnitUtil;

public class PathMetricsExtractor {
    private final int metrics;
    private final Graph graph;
    private final Weighting weighting;
    private final DistanceUnit distUnits;
    private final GHLongObjectHashMap<MetricsItem> edgeMetrics = new GHLongObjectHashMap<>();

    private static class MetricsItem {
        protected double distance;
        protected double time;
        protected double weight;
    }

    public PathMetricsExtractor(int metrics, Graph graph, Weighting weighting, DistanceUnit units) {
        this.metrics = metrics;
        this.graph = graph;
        this.weighting = weighting;
        this.distUnits = units;
    }

    public void setEmptyValues(int sourceIndex, MatrixLocations dstData, float[] times, float[] distances, float[] weights) {
        int offset = sourceIndex * dstData.size();
        for (int i = 0; i < dstData.getNodeIds().length; i++) {
            if (times != null)
                times[offset + i] = -1;
            if (distances != null)
                distances[offset + i] = -1;
            if (weights != null)
                weights[offset + i] = -1;
        }
    }

    public void calcValues(int sourceIndex, SPTEntry[] targets, MatrixLocations dstData, float[] times, float[] distances, float[] weights) throws IllegalStateException, StatusCodeException {
        if (targets == null)
            throw new IllegalStateException("Target destinations not set");

        int index = sourceIndex * dstData.size();
        double pathTime;
        double pathDistance;
        double pathWeight;
        boolean calcTime = MatrixMetricsType.isSet(metrics, MatrixMetricsType.DURATION);
        boolean calcDistance = MatrixMetricsType.isSet(metrics, MatrixMetricsType.DISTANCE);
        boolean calcWeight = MatrixMetricsType.isSet(metrics, MatrixMetricsType.WEIGHT);

        for (int i = 0; i < targets.length; ++i) {
            SPTEntry goalEdge = targets[i];

            if (goalEdge != null) {
                pathTime = 0.0;
                pathDistance = 0.0;
                pathWeight = 0.0;

                while (EdgeIterator.Edge.isValid(goalEdge.edge)) {
                    EdgeIteratorState iter = graph.getEdgeIteratorState(goalEdge.edge, goalEdge.adjNode);
                    long edgeKey = iter.getEdgeKey();
                    MetricsItem edgeMetricsItem = edgeMetrics.get(edgeKey);

                    if (edgeMetricsItem == null) {
                        edgeMetricsItem = new MetricsItem();
                        if (calcDistance)
                            edgeMetricsItem.distance = (distUnits == DistanceUnit.METERS) ? iter.getDistance() : DistanceUnitUtil.convert(iter.getDistance(), DistanceUnit.METERS, distUnits);
                        if (calcTime)
                            edgeMetricsItem.time = weighting.calcEdgeMillis(iter, false, EdgeIterator.NO_EDGE) / 1000.0;
                        if (calcWeight)
                            edgeMetricsItem.weight = weighting.calcEdgeWeight(iter, false, EdgeIterator.NO_EDGE);
                        edgeMetrics.put(edgeKey, edgeMetricsItem);
                    }

                    pathDistance += edgeMetricsItem.distance;
                    pathTime += edgeMetricsItem.time;
                    pathWeight += edgeMetricsItem.weight;

                    goalEdge = goalEdge.parent;

                    if (goalEdge == null)
                        break;
                }
            } else {
                pathTime = -1;
                pathDistance = -1;
                pathWeight = -1;
            }

            if (calcTime)
                times[index] = (float) pathTime;

            if (calcDistance)
                distances[index] = (float) pathDistance;

            if (calcWeight)
                weights[index] = (float) pathWeight;

            index++;
        }
    }
}