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

com.graphhopper.routing.template.RoundTripRoutingTemplate Maven / Gradle / Ivy

Go to download

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

The 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.template;

import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.PathWrapper;
import com.graphhopper.routing.*;
import com.graphhopper.routing.util.AvoidEdgesWeighting;
import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.util.tour.MultiPointTour;
import com.graphhopper.routing.util.tour.TourStrategy;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.Helper;
import com.graphhopper.util.Parameters;
import com.graphhopper.util.Parameters.Algorithms;
import com.graphhopper.util.Parameters.Algorithms.RoundTrip;
import com.graphhopper.util.PathMerger;
import com.graphhopper.util.Translation;
import com.graphhopper.util.shapes.GHPoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * Implementation of calculating a route with one or more round trip (route with identical start and
 * end).
 *
 * @author Peter Karich
 */
public class RoundTripRoutingTemplate extends AbstractRoutingTemplate implements RoutingTemplate
{
    private final int maxRetries;
    private final GHRequest ghRequest;
    private final GHResponse ghResponse;
    private PathWrapper altResponse;
    private final LocationIndex locationIndex;
    // result from route
    private List pathList;

    public RoundTripRoutingTemplate( GHRequest request, GHResponse ghRsp, LocationIndex locationIndex, int maxRetries )
    {
        this.ghRequest = request;
        this.ghResponse = ghRsp;
        this.locationIndex = locationIndex;
        this.maxRetries = maxRetries;
    }

    @Override
    public List lookup( List points, FlagEncoder encoder )
    {
        if (points.isEmpty())
            throw new IllegalStateException("For round trip calculation one point is required");
        final double distanceInMeter = ghRequest.getHints().getDouble(RoundTrip.DISTANCE, 10000);
        final long seed = ghRequest.getHints().getLong(RoundTrip.SEED, 0L);
        final double initialHeading = ghRequest.getHints().getDouble(RoundTrip.HEADING, Double.NaN);
        final int roundTripPointCount = Math.min(20, ghRequest.getHints().getInt(Algorithms.ROUND_TRIP + ".points", 2 + (int) (distanceInMeter / 50000)));
        final GHPoint start = ghRequest.getPoints().get(0);

        TourStrategy strategy = new MultiPointTour(new Random(seed), distanceInMeter, roundTripPointCount, initialHeading);
        queryResults = new ArrayList<>(2 + strategy.getNumberOfGeneratedPoints());
        EdgeFilter edgeFilter = new DefaultEdgeFilter(encoder);
        QueryResult startQR = locationIndex.findClosest(start.lat, start.lon, edgeFilter);
        if (!startQR.isValid())
            throw new IllegalArgumentException("Cannot find point 0: " + start);

        queryResults.add(startQR);

        GHPoint last = points.get(0);
        for (int i = 0; i < strategy.getNumberOfGeneratedPoints(); i++)
        {
            double heading = strategy.getHeadingForIteration(i);
            QueryResult result = generateValidPoint(last, strategy.getDistanceForIteration(i), heading, edgeFilter);
            if (result == null)
            {
                ghResponse.addError(new IllegalStateException("Could not find a valid point after " + maxRetries + " tries, for the point:" + last));
                return Collections.emptyList();
            }
            last = result.getSnappedPoint();
            queryResults.add(result);
        }

        queryResults.add(startQR);
        return queryResults;
    }

    void setQueryResults( List queryResults )
    {
        this.queryResults = queryResults;
    }

    @Override
    public List calcPaths( QueryGraph queryGraph, RoutingAlgorithmFactory algoFactory, AlgorithmOptions algoOpts )
    {
        pathList = new ArrayList<>(queryResults.size() - 1);

        AvoidEdgesWeighting avoidPathWeighting = new AvoidEdgesWeighting(algoOpts.getWeighting());
        avoidPathWeighting.setEdgePenaltyFactor(5);
        algoOpts = AlgorithmOptions.start(algoOpts).
                algorithm(Parameters.Algorithms.ASTAR_BI).
                weighting(avoidPathWeighting).build();
        algoOpts.getHints().put(Algorithms.ASTAR_BI + ".epsilon", 2);

        long visitedNodesSum = 0L;
        QueryResult start = queryResults.get(0);
        for (int qrIndex = 1; qrIndex < queryResults.size(); qrIndex++)
        {
            RoutingAlgorithm algo = algoFactory.createAlgo(queryGraph, algoOpts);
            // instead getClosestNode (which might be a virtual one and introducing unnecessary tails of the route)
            // use next tower node -> getBaseNode or getAdjNode
            // Later: remove potential route tail
            QueryResult startQR = queryResults.get(qrIndex - 1);
            int startNode = (startQR == start) ? startQR.getClosestNode() : startQR.getClosestEdge().getBaseNode();
            QueryResult endQR = queryResults.get(qrIndex);
            int endNode = (endQR == start) ? endQR.getClosestNode() : endQR.getClosestEdge().getBaseNode();

            Path path = algo.calcPath(startNode, endNode);
            visitedNodesSum += algo.getVisitedNodes();

            pathList.add(path);

            // it is important to avoid previously visited nodes for future paths
            avoidPathWeighting.addEdges(path.calcEdges());
        }

        ghResponse.getHints().put("visited_nodes.sum", visitedNodesSum);
        ghResponse.getHints().put("visited_nodes.average", (float) visitedNodesSum / (queryResults.size() - 1));

        return pathList;
    }

    public void setPaths( List pathList )
    {
        this.pathList = pathList;
    }

    @Override
    public boolean isReady( PathMerger pathMerger, Translation tr )
    {
        altResponse = new PathWrapper();
        altResponse.setWaypoints(getWaypoints());
        ghResponse.add(altResponse);
        pathMerger.doWork(altResponse, pathList, tr);
        // with potentially retrying, including generating new route points, for now disabled
        return true;
    }

    private QueryResult generateValidPoint( GHPoint from, double distanceInMeters, double heading,
                                            EdgeFilter edgeFilter )
    {
        int tryCount = 0;
        while (true)
        {
            GHPoint generatedPoint = Helper.DIST_EARTH.projectCoordinate(from.getLat(), from.getLon(), distanceInMeters, heading);
            QueryResult qr = locationIndex.findClosest(generatedPoint.getLat(), generatedPoint.getLon(), edgeFilter);
            if (qr.isValid())
                return qr;

            tryCount++;
            distanceInMeters *= 0.95;

            if (tryCount >= maxRetries)
                return null;
        }
    }

    @Override
    public int getMaxRetries()
    {
        // with potentially retrying, including generating new route points, for now disabled
        return 1;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy