ORSRouter.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.routing.graphhopper.extensions;

import com.graphhopper.GHRequest;
import com.graphhopper.config.Profile;
import com.graphhopper.routing.*;
import com.graphhopper.routing.ev.EncodedValueLookup;
import com.graphhopper.routing.ev.Subnetwork;
import com.graphhopper.routing.lm.LandmarkStorage;
import com.graphhopper.routing.querygraph.QueryGraph;
import com.graphhopper.routing.util.*;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.RoutingCHGraph;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.util.PMap;
import com.graphhopper.util.TranslationMap;
import com.graphhopper.util.details.PathDetailsBuilderFactory;
import org.heigit.ors.routing.graphhopper.extensions.core.CoreRoutingAlgorithmFactory;
import org.heigit.ors.routing.graphhopper.extensions.core.PrepareCoreLandmarks;

import java.util.Map;

public class ORSRouter extends Router {
    private final GraphHopperStorage ghStorage;
    private final EncodingManager encodingManager;
    private final Map<String, Profile> profilesByName;
    private final RouterConfig routerConfig;
    private final WeightingFactory weightingFactory;
    private Map<String, RoutingCHGraph> coreGraphs;
    private Map<String, PrepareCoreLandmarks> coreLandmarks;

    public ORSRouter(GraphHopperStorage ghStorage, LocationIndex locationIndex, Map<String, Profile> profilesByName, PathDetailsBuilderFactory pathDetailsBuilderFactory, TranslationMap translationMap, RouterConfig routerConfig, WeightingFactory weightingFactory, Map<String, RoutingCHGraph> chGraphs, Map<String, LandmarkStorage> landmarks) {
        super(ghStorage, locationIndex, profilesByName, pathDetailsBuilderFactory, translationMap, routerConfig, weightingFactory, chGraphs, landmarks);
        this.ghStorage = ghStorage;
        this.encodingManager = ghStorage.getEncodingManager();
        this.profilesByName = profilesByName;
        this.routerConfig = routerConfig;
        this.weightingFactory = weightingFactory;
    }

    public void setCoreGraphs(Map<String, RoutingCHGraph> coreGraphs) {
        this.coreGraphs = coreGraphs;
    }

    public void setCoreLandmarks(Map<String, PrepareCoreLandmarks> coreLandmarks) {
        this.coreLandmarks = coreLandmarks;
    }

    private static boolean getDisableCore(PMap hints) {
        return hints.getBool("core.disable", true);
    }

    @Override
    protected Router.Solver createSolver(GHRequest request, EdgeFilterFactory edgeFilterFactory) {
        boolean disableCore = getDisableCore(request.getHints());
        if (!disableCore) {
            return new ORSRouter.CoreSolver(request, this.profilesByName, this.routerConfig, this.encodingManager, this.weightingFactory, this.ghStorage, this.coreGraphs, this.coreLandmarks).setEdgeFilterFactory(edgeFilterFactory);
        } else {
            return super.createSolver(request, edgeFilterFactory);
        }
    }

    private static class CoreSolver extends Router.Solver {
        private final Map<String, RoutingCHGraph> chGraphs;
        private final GraphHopperStorage ghStorage;
        private final WeightingFactory weightingFactory;
        private final Map<String, PrepareCoreLandmarks> landmarks;

        CoreSolver(GHRequest request, Map<String, Profile> profilesByName, RouterConfig routerConfig, EncodedValueLookup lookup, WeightingFactory weightingFactory, GraphHopperStorage ghStorage, Map<String, RoutingCHGraph> chGraphs, Map<String, PrepareCoreLandmarks> landmarks) {
            super(request, profilesByName, routerConfig, lookup);
            this.weightingFactory = weightingFactory;
            this.ghStorage = ghStorage;
            this.chGraphs = chGraphs;
            this.landmarks = landmarks;
        }

        @Override
        protected void checkRequest() {
            super.checkRequest();
            // TODO Refactoring: check request params compatibility with core algo
        }

        protected Weighting createWeighting() {
            return weightingFactory.createWeighting(profile, request.getHints(), false);
        }

        protected PathCalculator createPathCalculator(QueryGraph queryGraph) {
            RoutingCHGraph chGraph = getRoutingCHGraph(this.profile.getName());
            RoutingAlgorithmFactory algorithmFactory = getRoutingAlgorithmFactory(chGraph, queryGraph);
            return new CorePathCalculator(queryGraph, algorithmFactory, weighting, getAlgoOpts());
        }

        AlgorithmOptions getAlgoOpts() {
            AlgorithmOptions algoOpts = new AlgorithmOptions().
                    setAlgorithm(request.getAlgorithm()).
                    setTraversalMode(profile.isTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED).
                    setMaxVisitedNodes(getMaxVisitedNodes(request.getHints())).
                    setHints(request.getHints());

            if (edgeFilterFactory != null)
                algoOpts.setEdgeFilter(edgeFilterFactory.createEdgeFilter(request.getAdditionalHints(), weighting.getFlagEncoder(), ghStorage));

            return algoOpts;
        }

        @Override
        protected EdgeFilter getSnapFilter() {
            EdgeFilter defaultSnapFilter = new DefaultSnapFilter(weighting, lookup.getBooleanEncodedValue(Subnetwork.key(profile.getName())));
            if (edgeFilterFactory != null)
                return edgeFilterFactory.createEdgeFilter(request.getAdditionalHints(), weighting.getFlagEncoder(), ghStorage, defaultSnapFilter);
            return defaultSnapFilter;
        }

        private RoutingCHGraph getRoutingCHGraph(String profileName) {
            RoutingCHGraph chGraph = this.chGraphs.get(profileName);
            if (chGraph == null) {
                throw new IllegalArgumentException("Cannot find core preparation for the requested profile: '" + profileName + "'\nYou can try disabling core using " + "core.disable" + "=true\navailable core profiles: " + this.chGraphs.keySet());
            } else {
                return chGraph;
            }
        }

        private RoutingAlgorithmFactory getRoutingAlgorithmFactory(RoutingCHGraph chGraph, QueryGraph queryGraph) {
            PMap map = request.getHints();
            LandmarkStorage lms = null;
            for (PrepareCoreLandmarks p : landmarks.values()) {
                if (p.getLMConfig().getWeighting().getName().equals(map.getString("weighting_method", "")))
                    if (p.matchesFilter(map)) {
                        lms = p.getLandmarkStorage();
                        break;
                    }
            }

            if (lms == null) {
                return new CoreRoutingAlgorithmFactory(chGraph, queryGraph);
            } else {
                return new CoreRoutingAlgorithmFactory(chGraph, queryGraph, lms);
            }
        }
    }
}