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

com.telenav.mesakit.navigation.routing.bidijkstra.BiDijkstraRouter Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// © 2011-2021 Telenav, Inc.
//
// Licensed 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
//
// https://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.telenav.mesakit.navigation.routing.bidijkstra;

import com.telenav.kivakit.core.value.count.MutableCount;
import com.telenav.kivakit.core.logging.Logger;
import com.telenav.kivakit.core.logging.LoggerFactory;
import com.telenav.kivakit.core.messaging.Debug;
import com.telenav.mesakit.navigation.routing.BaseRouter;
import com.telenav.mesakit.navigation.routing.LevelPromoter;
import com.telenav.mesakit.navigation.routing.RoutingRequest;
import com.telenav.mesakit.navigation.routing.RoutingResponse;
import com.telenav.mesakit.navigation.routing.cost.CostFunction;
import com.telenav.mesakit.navigation.routing.cost.EdgePermissionFunction;
import com.telenav.mesakit.navigation.routing.cost.RoutePermissionFunction;
import com.telenav.mesakit.navigation.routing.dijkstra.DijkstraRouter;
import com.telenav.mesakit.navigation.routing.dijkstra.DijkstraRoutingRequest;
import com.telenav.mesakit.navigation.routing.dijkstra.Direction;
import com.telenav.mesakit.navigation.routing.dijkstra.Meet;

public class BiDijkstraRouter extends BaseRouter
{
    private static final Logger LOGGER = LoggerFactory.newLogger();

    public static final Debug DEBUG = new Debug(LOGGER);

    /** The cost function for routing */
    private final CostFunction costFunction;

    /** Any route permission function */
    private RoutePermissionFunction routePermissionFunction = RoutePermissionFunction.NULL;

    /** Any edge permission function */
    private EdgePermissionFunction edgePermissionFunction = EdgePermissionFunction.NULL;

    /** Any level promoter */
    private LevelPromoter levelPromoter = LevelPromoter.NULL;

    public BiDijkstraRouter(CostFunction costFunction)
    {
        this.costFunction = costFunction;
    }

    private BiDijkstraRouter(BiDijkstraRouter that)
    {
        costFunction = that.costFunction;
        routePermissionFunction = that.routePermissionFunction;
        edgePermissionFunction = that.edgePermissionFunction;
        levelPromoter = that.levelPromoter;
    }

    @Override
    public RoutingResponse onFindRoute(RoutingRequest request)
    {
        // Get request as BiDijkstraRoutingRequest
        var birequest = (BiDijkstraRoutingRequest) request;

        // Construct forward and backwards routing requests
        var forward = new DijkstraRoutingRequest(request.start(), request.end())
                .withDirection(Direction.FORWARD)
                .withDebugger(request.debugger())
                .withLimiter(request.limiter());

        var backward = new DijkstraRoutingRequest(request.start(), request.end())
                .withDirection(Direction.BACKWARD)
                .withDebugger(request.debugger())
                .withLimiter(request.limiter());

        // Create router
        var forwardRouter = router().withHeuristicCostFunction(birequest.forwardHeuristicCostFunction());
        var backwardRouter = router().withHeuristicCostFunction(birequest.backwardHeuristicCostFunction());

        // Start routing
        forward.onStartRouting();
        backward.onStartRouting();

        // The best meet we've found
        Meet best = null;

        // The number of meets we've found
        var meets = new MutableCount();

        // Flip route direction back and forth forever
        for (var direction = Direction.FORWARD; ; direction = direction.reversed())
        {
            // execute the next step in either the forward or backward request
            RoutingResponse result;
            if (direction.isForward())
            {
                result = forwardRouter.execute(forward, 1);
            }
            else
            {
                result = backwardRouter.execute(backward, 1);
            }

            // If the forward request has just met the backward request
            var meet = direction.isForward() ? forward.meet(backward) : backward.meet(forward);
            if (meet != null)
            {
                // If we found the same route twice in a row
                var sameRoute = meet.equals(best);

                // If we have no best meet yet or this meet is cheaper than the best
                if (best == null || meet.isCheaperThan(best))
                {
                    // then we have a new best meet
                    best = meet;
                }

                // If (1) we've found the same route or (2) the number of meets so far is greater
                // than the maximum allowed or (3) the meet cost is greater than the first meet's
                // cost times the maximum first meet cost multiple
                meets.increment();
                if (sameRoute || meets.count().isGreaterThan(birequest.meets())
                        || meet.cost().isGreaterThan(best.cost().times(birequest.maximumFirstMeetCostMultiple())))
                {
                    // then we stop because we're not likely to get a better meet
                    return forward.done(best.route());
                }
            }

            // If we didn't meet because of this step forward or backward and we've never met at all
            // and our step failed
            if (meet == null && meets.isZero() && result.failed())
            {
                // then it's time to give up because there's no way for us to succeed
                return forward.failed();
            }
        }
    }

    public BiDijkstraRouter withEdgePermissionFunction(EdgePermissionFunction edgePermissionFunction)
    {
        var router = new BiDijkstraRouter(this);
        router.edgePermissionFunction = edgePermissionFunction;
        return router;
    }

    public BiDijkstraRouter withLevelPromoter(LevelPromoter levelPromoter)
    {
        var router = new BiDijkstraRouter(this);
        router.levelPromoter = levelPromoter;
        return router;
    }

    public BiDijkstraRouter withRoutePermissionFunction(RoutePermissionFunction routePermissionFunction)
    {
        var router = new BiDijkstraRouter(this);
        router.routePermissionFunction = routePermissionFunction;
        return router;
    }

    private DijkstraRouter router()
    {
        return new DijkstraRouter(costFunction)
                .withLevelPromoter(levelPromoter)
                .withRoutePermissionFunction(routePermissionFunction)
                .withEdgePermissionFunction(edgePermissionFunction);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy