|
|
|
@ -4,10 +4,8 @@ import gnu.trove.map.hash.TLongObjectHashMap;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.text.MessageFormat;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.PriorityQueue;
|
|
|
|
|
|
|
|
|
|
import net.osmand.PlatformUtil;
|
|
|
|
@ -19,6 +17,9 @@ import org.apache.commons.logging.Log;
|
|
|
|
|
|
|
|
|
|
public class BinaryRoutePlanner {
|
|
|
|
|
|
|
|
|
|
private static final int TEST_ID = 235108903;
|
|
|
|
|
private static final boolean TEST_SPECIFIC = false;
|
|
|
|
|
|
|
|
|
|
private static final int REVERSE_WAY_RESTRICTION_ONLY = 1024;
|
|
|
|
|
/*private*/ static final int STANDARD_ROAD_IN_QUEUE_OVERHEAD = 220;
|
|
|
|
|
/*private*/ static final int STANDARD_ROAD_VISITED_OVERHEAD = 150;
|
|
|
|
@ -68,19 +69,6 @@ public class BinaryRoutePlanner {
|
|
|
|
|
ctx.visitedSegments = 0;
|
|
|
|
|
ctx.memoryOverhead = 1000;
|
|
|
|
|
ctx.timeToCalculate = System.nanoTime();
|
|
|
|
|
if(ctx.config.initialDirection != null) {
|
|
|
|
|
// mark here as positive for further check
|
|
|
|
|
ctx.firstRoadId = calculateRoutePointId(start.getRoad(), start.getSegmentStart(), true);
|
|
|
|
|
double plusDir = start.getRoad().directionRoute(start.getSegmentStart(), true);
|
|
|
|
|
double diff = plusDir - ctx.config.initialDirection;
|
|
|
|
|
if(Math.abs(MapUtils.alignAngleDifference(diff)) <= Math.PI / 3) {
|
|
|
|
|
ctx.firstRoadDirection = 1;
|
|
|
|
|
} else if(Math.abs(MapUtils.alignAngleDifference(diff - Math.PI)) <= Math.PI / 3) {
|
|
|
|
|
ctx.firstRoadDirection = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initializing priority queue to visit way segments
|
|
|
|
|
Comparator<RouteSegment> nonHeuristicSegmentsComparator = new NonHeuristicSegmentsComparator();
|
|
|
|
|
PriorityQueue<RouteSegment> graphDirectSegments = new PriorityQueue<RouteSegment>(50, new SegmentsComparator(ctx));
|
|
|
|
@ -90,32 +78,17 @@ public class BinaryRoutePlanner {
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedDirectSegments = new TLongObjectHashMap<RouteSegment>();
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedOppositeSegments = new TLongObjectHashMap<RouteSegment>();
|
|
|
|
|
|
|
|
|
|
RouteSegment recalcEndSegment = smartRecalculationEnabled(ctx, visitedOppositeSegments);
|
|
|
|
|
boolean runRecalculation = false;
|
|
|
|
|
if(recalcEndSegment != null) {
|
|
|
|
|
runRecalculation = true;
|
|
|
|
|
end = recalcEndSegment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for start : f(start) = g(start) + h(start) = 0 + h(start) = h(start)
|
|
|
|
|
float estimatedDistance = (float) estimatedDistance(ctx, ctx.targetX, ctx.targetY, ctx.startX, ctx.startY);
|
|
|
|
|
end.distanceToEnd = start.distanceToEnd = estimatedDistance;
|
|
|
|
|
|
|
|
|
|
graphDirectSegments.add(start);
|
|
|
|
|
graphReverseSegments.add(end);
|
|
|
|
|
initQueuesWithStartEnd(ctx, start, end, graphDirectSegments, graphReverseSegments);
|
|
|
|
|
|
|
|
|
|
// Extract & analyze segment with min(f(x)) from queue while final segment is not found
|
|
|
|
|
boolean inverse = false;
|
|
|
|
|
boolean init = false;
|
|
|
|
|
boolean forwardSearch = true;
|
|
|
|
|
|
|
|
|
|
PriorityQueue<RouteSegment> graphSegments;
|
|
|
|
|
if(inverse) {
|
|
|
|
|
graphSegments = graphReverseSegments;
|
|
|
|
|
} else {
|
|
|
|
|
graphSegments = graphDirectSegments;
|
|
|
|
|
}
|
|
|
|
|
PriorityQueue<RouteSegment> graphSegments = graphDirectSegments;
|
|
|
|
|
|
|
|
|
|
FinalRouteSegment finalSegment = null;
|
|
|
|
|
boolean onlyBackward = ctx.getPlanRoadDirection() < 0;
|
|
|
|
|
boolean onlyForward = ctx.getPlanRoadDirection() > 0;
|
|
|
|
|
while (!graphSegments.isEmpty()) {
|
|
|
|
|
RouteSegment segment = graphSegments.poll();
|
|
|
|
|
// use accumulative approach
|
|
|
|
@ -132,6 +105,9 @@ public class BinaryRoutePlanner {
|
|
|
|
|
printMemoryConsumption("Memory occupied after calculation : ");
|
|
|
|
|
}
|
|
|
|
|
finalSegment = (FinalRouteSegment) segment;
|
|
|
|
|
if(TRACE_ROUTING){
|
|
|
|
|
println("Final segment found");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (ctx.memoryOverhead > ctx.config.memoryLimitation * 0.95 && RoutingContext.SHOW_GC_SIZE) {
|
|
|
|
@ -141,47 +117,45 @@ public class BinaryRoutePlanner {
|
|
|
|
|
throw new IllegalStateException("There is no enough memory " + ctx.config.memoryLimitation/(1<<20) + " Mb");
|
|
|
|
|
}
|
|
|
|
|
ctx.visitedSegments++;
|
|
|
|
|
if (!inverse) {
|
|
|
|
|
if (forwardSearch) {
|
|
|
|
|
boolean doNotAddIntersections = onlyBackward;
|
|
|
|
|
processRouteSegment(ctx, false, graphDirectSegments, visitedDirectSegments,
|
|
|
|
|
segment, visitedOppositeSegments, true);
|
|
|
|
|
processRouteSegment(ctx, false, graphDirectSegments, visitedDirectSegments,
|
|
|
|
|
segment, visitedOppositeSegments, false);
|
|
|
|
|
segment, visitedOppositeSegments, doNotAddIntersections);
|
|
|
|
|
} else {
|
|
|
|
|
boolean doNotAddIntersections = onlyForward;
|
|
|
|
|
processRouteSegment(ctx, true, graphReverseSegments, visitedOppositeSegments, segment,
|
|
|
|
|
visitedDirectSegments, true);
|
|
|
|
|
processRouteSegment(ctx, true, graphReverseSegments, visitedOppositeSegments,segment,
|
|
|
|
|
visitedDirectSegments, false);
|
|
|
|
|
visitedDirectSegments, doNotAddIntersections);
|
|
|
|
|
}
|
|
|
|
|
updateCalculationProgress(ctx, graphDirectSegments, graphReverseSegments);
|
|
|
|
|
if(graphReverseSegments.isEmpty()){
|
|
|
|
|
if(ctx.getPlanRoadDirection() <= 0 && graphReverseSegments.isEmpty()){
|
|
|
|
|
throw new IllegalArgumentException("Route is not found to selected target point.");
|
|
|
|
|
}
|
|
|
|
|
if(graphDirectSegments.isEmpty()){
|
|
|
|
|
if(ctx.getPlanRoadDirection() >= 0 && graphDirectSegments.isEmpty()){
|
|
|
|
|
throw new IllegalArgumentException("Route is not found from selected start point.");
|
|
|
|
|
}
|
|
|
|
|
if(runRecalculation) {
|
|
|
|
|
// nothing to do
|
|
|
|
|
inverse = false;
|
|
|
|
|
} else if (!init) {
|
|
|
|
|
inverse = !inverse;
|
|
|
|
|
init = true;
|
|
|
|
|
} else if (ctx.planRouteIn2Directions()) {
|
|
|
|
|
inverse = nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) > 0;
|
|
|
|
|
if (ctx.planRouteIn2Directions()) {
|
|
|
|
|
forwardSearch = !(nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) > 0);
|
|
|
|
|
if (graphDirectSegments.size() * 1.3 > graphReverseSegments.size()) {
|
|
|
|
|
inverse = true;
|
|
|
|
|
forwardSearch = false;
|
|
|
|
|
} else if (graphDirectSegments.size() < 1.3 * graphReverseSegments.size()) {
|
|
|
|
|
inverse = false;
|
|
|
|
|
forwardSearch = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// different strategy : use onedirectional graph
|
|
|
|
|
inverse = ctx.getPlanRoadDirection() < 0;
|
|
|
|
|
forwardSearch = onlyForward;
|
|
|
|
|
if(onlyBackward && !graphDirectSegments.isEmpty()) {
|
|
|
|
|
forwardSearch = true;
|
|
|
|
|
}
|
|
|
|
|
if(onlyForward && !graphReverseSegments.isEmpty()) {
|
|
|
|
|
forwardSearch = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (inverse) {
|
|
|
|
|
graphSegments = graphReverseSegments;
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (forwardSearch) {
|
|
|
|
|
graphSegments = graphDirectSegments;
|
|
|
|
|
} else {
|
|
|
|
|
graphSegments = graphReverseSegments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if interrupted
|
|
|
|
|
if(ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) {
|
|
|
|
|
throw new InterruptedException("Route calculation interrupted");
|
|
|
|
@ -192,6 +166,47 @@ public class BinaryRoutePlanner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void initQueuesWithStartEnd(final RoutingContext ctx, RouteSegment start, RouteSegment end,
|
|
|
|
|
PriorityQueue<RouteSegment> graphDirectSegments, PriorityQueue<RouteSegment> graphReverseSegments) {
|
|
|
|
|
RouteSegment startPos = start.initRouteSegment(true);
|
|
|
|
|
RouteSegment startNeg = start.initRouteSegment(false);
|
|
|
|
|
RouteSegment endPos = end.initRouteSegment(true);
|
|
|
|
|
RouteSegment endNeg = end.initRouteSegment(false);
|
|
|
|
|
// for start : f(start) = g(start) + h(start) = 0 + h(start) = h(start)
|
|
|
|
|
if(ctx.config.initialDirection != null) {
|
|
|
|
|
// mark here as positive for further check
|
|
|
|
|
double plusDir = start.getRoad().directionRoute(start.getSegmentStart(), true);
|
|
|
|
|
double diff = plusDir - ctx.config.initialDirection;
|
|
|
|
|
if(Math.abs(MapUtils.alignAngleDifference(diff)) <= Math.PI / 3) {
|
|
|
|
|
if(startNeg != null) {
|
|
|
|
|
startNeg.distanceFromStart += 500;
|
|
|
|
|
}
|
|
|
|
|
} else if(Math.abs(MapUtils.alignAngleDifference(diff - Math.PI)) <= Math.PI / 3) {
|
|
|
|
|
if(startPos != null) {
|
|
|
|
|
startPos.distanceFromStart += 500;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
float estimatedDistance = (float) estimatedDistance(ctx, ctx.targetX, ctx.targetY, ctx.startX, ctx.startY);
|
|
|
|
|
if(startPos != null) {
|
|
|
|
|
startPos.distanceToEnd = estimatedDistance;
|
|
|
|
|
graphDirectSegments.add(startPos);
|
|
|
|
|
}
|
|
|
|
|
if(startNeg != null) {
|
|
|
|
|
startNeg.distanceToEnd = estimatedDistance;
|
|
|
|
|
graphDirectSegments.add(startNeg);
|
|
|
|
|
}
|
|
|
|
|
if(endPos != null) {
|
|
|
|
|
endPos.distanceToEnd = estimatedDistance;
|
|
|
|
|
graphReverseSegments.add(endPos);
|
|
|
|
|
}
|
|
|
|
|
if(endNeg != null) {
|
|
|
|
|
endNeg.distanceToEnd = estimatedDistance;
|
|
|
|
|
graphReverseSegments.add(endNeg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void printMemoryConsumption( String string) {
|
|
|
|
|
long h1 = RoutingContext.runGCUsedMemory();
|
|
|
|
|
float mb = (1 << 20);
|
|
|
|
@ -216,42 +231,6 @@ public class BinaryRoutePlanner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private RouteSegment smartRecalculationEnabled(final RoutingContext ctx, TLongObjectHashMap<RouteSegment> visitedOppositeSegments) {
|
|
|
|
|
boolean runRecalculation = ctx.previouslyCalculatedRoute != null && ctx.previouslyCalculatedRoute.size() > 0
|
|
|
|
|
&& ctx.config.recalculateDistance != 0;
|
|
|
|
|
if (runRecalculation) {
|
|
|
|
|
RouteSegment previous = null;
|
|
|
|
|
List<RouteSegmentResult> rlist = new ArrayList<RouteSegmentResult>();
|
|
|
|
|
float distanceThreshold = ctx.config.recalculateDistance;
|
|
|
|
|
float threshold = 0;
|
|
|
|
|
for(RouteSegmentResult rr : ctx.previouslyCalculatedRoute) {
|
|
|
|
|
threshold += rr.getDistance();
|
|
|
|
|
if(threshold > distanceThreshold) {
|
|
|
|
|
rlist.add(rr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
runRecalculation = rlist.size() > 0;
|
|
|
|
|
if (rlist.size() > 0) {
|
|
|
|
|
for (RouteSegmentResult rr : rlist) {
|
|
|
|
|
RouteSegment segment = new RouteSegment(rr.getObject(), rr.getEndPointIndex());
|
|
|
|
|
if (previous != null) {
|
|
|
|
|
previous.setParentRoute(segment);
|
|
|
|
|
previous.setParentSegmentEnd(rr.getStartPointIndex());
|
|
|
|
|
boolean positive = rr.getStartPointIndex() < rr.getEndPointIndex();
|
|
|
|
|
long t = calculateRoutePointId(rr.getObject(), positive ? rr.getEndPointIndex() - 1 : rr.getEndPointIndex(),
|
|
|
|
|
positive);
|
|
|
|
|
visitedOppositeSegments.put(t, segment);
|
|
|
|
|
}
|
|
|
|
|
previous = segment;
|
|
|
|
|
}
|
|
|
|
|
return previous;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void printRoad(String prefix, RouteSegment segment) {
|
|
|
|
|
String pr;
|
|
|
|
|
if(segment.parentRoute != null){
|
|
|
|
@ -259,7 +238,7 @@ public class BinaryRoutePlanner {
|
|
|
|
|
} else {
|
|
|
|
|
pr = "";
|
|
|
|
|
}
|
|
|
|
|
println(prefix +"" + segment.road + " ind=" + segment.getSegmentStart() +
|
|
|
|
|
println(prefix +"" + segment.road + " dir="+segment.getDirectionAssigned()+" ind=" + segment.getSegmentStart() +
|
|
|
|
|
" ds=" + ((float)segment.distanceFromStart) + " es="+((float)segment.distanceToEnd) + pr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -269,16 +248,15 @@ public class BinaryRoutePlanner {
|
|
|
|
|
return (float) (distance / ctx.getRouter().getMaxDefaultSpeed());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static float h(RoutingContext ctx, int begX, int begY, int endX, int endY,
|
|
|
|
|
RouteSegment next) {
|
|
|
|
|
protected static float h(RoutingContext ctx, int begX, int begY, int endX, int endY) {
|
|
|
|
|
double distToFinalPoint = squareRootDist(begX, begY, endX, endY);
|
|
|
|
|
double result = distToFinalPoint / ctx.getRouter().getMaxDefaultSpeed();
|
|
|
|
|
if(ctx.precalculatedRouteDirection != null){
|
|
|
|
|
float te = ctx.precalculatedRouteDirection.timeEstimate(begX, begY, endX, endY);
|
|
|
|
|
if(te > 0) {
|
|
|
|
|
return te;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
double result = distToFinalPoint / ctx.getRouter().getMaxDefaultSpeed();
|
|
|
|
|
return (float) result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -295,7 +273,9 @@ public class BinaryRoutePlanner {
|
|
|
|
|
|
|
|
|
|
public void printDebugMemoryInformation(RoutingContext ctx, PriorityQueue<RouteSegment> graphDirectSegments, PriorityQueue<RouteSegment> graphReverseSegments,
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedDirectSegments,TLongObjectHashMap<RouteSegment> visitedOppositeSegments) {
|
|
|
|
|
printInfo("Time to calculate : " + (System.nanoTime() - ctx.timeToCalculate) / 1e6 + ", time to load : " + ctx.timeToLoad / 1e6 + ", time to load headers : " + ctx.timeToLoadHeaders / 1e6);
|
|
|
|
|
printInfo("Time to calculate : " + (System.nanoTime() - ctx.timeToCalculate) / 1e6 +
|
|
|
|
|
", time to load : " + ctx.timeToLoad / 1e6 + ", time to load headers : " + ctx.timeToLoadHeaders / 1e6 +
|
|
|
|
|
", time to calc dev : " + ctx.timeNanoToCalcDeviation/ 1e6);
|
|
|
|
|
int maxLoadedTiles = Math.max(ctx.maxLoadedTiles, ctx.getCurrentlyLoadedTiles());
|
|
|
|
|
printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles " + maxLoadedTiles);
|
|
|
|
|
printInfo("Loaded tiles " + ctx.loadedTiles + " (distinct "+ctx.distinctLoadedTiles+ "), unloaded tiles " + ctx.unloadedTiles +
|
|
|
|
@ -305,51 +285,57 @@ public class BinaryRoutePlanner {
|
|
|
|
|
if (graphDirectSegments != null && graphReverseSegments != null) {
|
|
|
|
|
printInfo("Priority queues sizes : " + graphDirectSegments.size() + "/" + graphReverseSegments.size());
|
|
|
|
|
}
|
|
|
|
|
printInfo("Already visited " + alreadyVisited);
|
|
|
|
|
alreadyVisited = 0;
|
|
|
|
|
if (visitedDirectSegments != null && visitedOppositeSegments != null) {
|
|
|
|
|
printInfo("Visited interval sizes: " + visitedDirectSegments.size() + "/" + visitedOppositeSegments.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
private static int alreadyVisited = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
|
private void processRouteSegment(final RoutingContext ctx, boolean reverseWaySearch,
|
|
|
|
|
PriorityQueue<RouteSegment> graphSegments, TLongObjectHashMap<RouteSegment> visitedSegments,
|
|
|
|
|
RouteSegment segment, TLongObjectHashMap<RouteSegment> oppositeSegments, boolean direction) throws IOException {
|
|
|
|
|
RouteSegment segment, TLongObjectHashMap<RouteSegment> oppositeSegments, boolean doNotAddIntersections) throws IOException {
|
|
|
|
|
final RouteDataObject road = segment.road;
|
|
|
|
|
boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment(ctx, reverseWaySearch, visitedSegments, segment, direction, road);
|
|
|
|
|
boolean directionAllowed = initDirectionAllowed;
|
|
|
|
|
boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment(ctx, reverseWaySearch, visitedSegments, segment, road);
|
|
|
|
|
if(TEST_SPECIFIC && road.getId() == TEST_ID ) {
|
|
|
|
|
printRoad(" ! " + +segment.distanceFromStart + " ", segment);
|
|
|
|
|
}
|
|
|
|
|
boolean directionAllowed = initDirectionAllowed;
|
|
|
|
|
if(!directionAllowed) {
|
|
|
|
|
alreadyVisited ++;
|
|
|
|
|
if(TRACE_ROUTING) {
|
|
|
|
|
println(" >> Already visited");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Go through all point of the way and find ways to continue
|
|
|
|
|
// ! Actually there is small bug when there is restriction to move forward on the way (it doesn't take into account)
|
|
|
|
|
float obstaclesTime = 0;
|
|
|
|
|
if(segment.getParentRoute() != null && directionAllowed) {
|
|
|
|
|
obstaclesTime = (float) ctx.getRouter().calculateTurnTime(segment, direction? segment.getRoad().getPointsLength() - 1 : 0,
|
|
|
|
|
segment.getParentRoute(), segment.getParentSegmentEnd());
|
|
|
|
|
}
|
|
|
|
|
if(ctx.firstRoadId == calculateRoutePointId(road, segment.getSegmentStart(), true) ) {
|
|
|
|
|
if(direction && ctx.firstRoadDirection < 0) {
|
|
|
|
|
obstaclesTime += 500;
|
|
|
|
|
} else if(!direction && ctx.firstRoadDirection > 0) {
|
|
|
|
|
obstaclesTime += 500;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
float segmentDist = 0;
|
|
|
|
|
// +/- diff from middle point
|
|
|
|
|
int segmentEnd = segment.getSegmentStart();
|
|
|
|
|
short segmentPoint = segment.getSegmentStart();
|
|
|
|
|
boolean dir = segment.isPositive();
|
|
|
|
|
while (directionAllowed) {
|
|
|
|
|
int prevInd = segmentEnd;
|
|
|
|
|
if(direction) {
|
|
|
|
|
segmentEnd ++;
|
|
|
|
|
// mark previous interval as visited and move to next intersection
|
|
|
|
|
short prevInd = segmentPoint;
|
|
|
|
|
if(dir) {
|
|
|
|
|
segmentPoint ++;
|
|
|
|
|
} else {
|
|
|
|
|
segmentEnd --;
|
|
|
|
|
segmentPoint --;
|
|
|
|
|
}
|
|
|
|
|
if (segmentEnd < 0 || segmentEnd >= road.getPointsLength()) {
|
|
|
|
|
if (segmentPoint < 0 || segmentPoint >= road.getPointsLength()) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
final int intervalId = direction ? segmentEnd - 1 : segmentEnd;
|
|
|
|
|
visitedSegments.put(calculateRoutePointId(road, intervalId, direction), segment);
|
|
|
|
|
final int x = road.getPoint31XTile(segmentEnd);
|
|
|
|
|
final int y = road.getPoint31YTile(segmentEnd);
|
|
|
|
|
// store <segment> in order to not have unique <segment, direction> in visitedSegments
|
|
|
|
|
visitedSegments.put(calculateRoutePointId(segment.getRoad(), segment.isPositive() ? segmentPoint - 1 : segmentPoint,
|
|
|
|
|
segment.isPositive()), segment);
|
|
|
|
|
final int x = road.getPoint31XTile(segmentPoint);
|
|
|
|
|
final int y = road.getPoint31YTile(segmentPoint);
|
|
|
|
|
final int prevx = road.getPoint31XTile(prevInd);
|
|
|
|
|
final int prevy = road.getPoint31YTile(prevInd);
|
|
|
|
|
if(x == prevx && y == prevy) {
|
|
|
|
@ -360,102 +346,93 @@ public class BinaryRoutePlanner {
|
|
|
|
|
segmentDist += squareRootDist(x, y, prevx, prevy);
|
|
|
|
|
|
|
|
|
|
// 2.1 calculate possible obstacle plus time
|
|
|
|
|
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentEnd);
|
|
|
|
|
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentPoint);
|
|
|
|
|
if (obstacle < 0) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
obstaclesTime += obstacle;
|
|
|
|
|
|
|
|
|
|
boolean alreadyVisited = checkIfOppositieSegmentWasVisited(ctx, reverseWaySearch, graphSegments, segment, oppositeSegments, road,
|
|
|
|
|
segmentEnd, direction, intervalId, segmentDist, obstaclesTime);
|
|
|
|
|
boolean alreadyVisited = checkIfOppositieSegmentWasVisited(ctx, reverseWaySearch, graphSegments, segment, oppositeSegments,
|
|
|
|
|
segmentPoint, segmentDist, obstaclesTime);
|
|
|
|
|
if (alreadyVisited) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// correct way of handling precalculatedRouteDirection
|
|
|
|
|
if(ctx.precalculatedRouteDirection != null) {
|
|
|
|
|
// long nt = System.nanoTime();
|
|
|
|
|
// float devDistance = ctx.precalculatedRouteDirection.getDeviationDistance(x, y);
|
|
|
|
|
// // 1. linear method
|
|
|
|
|
// // segmentDist = segmentDist * (1 + ctx.precalculatedRouteDirection.getDeviationDistance(x, y) / ctx.config.DEVIATION_RADIUS);
|
|
|
|
|
// // 2. exponential method
|
|
|
|
|
// segmentDist = segmentDist * (float) Math.pow(1.5, devDistance / 500);
|
|
|
|
|
// ctx.timeNanoToCalcDeviation += (System.nanoTime() - nt);
|
|
|
|
|
}
|
|
|
|
|
// could be expensive calculation
|
|
|
|
|
// 3. get intersected ways
|
|
|
|
|
final RouteSegment roadNext = ctx.loadRouteSegment(x, y, ctx.config.memoryLimitation - ctx.memoryOverhead);
|
|
|
|
|
if(roadNext != null &&
|
|
|
|
|
!((roadNext == segment || roadNext.road.id == road.id) && roadNext.next == null)) {
|
|
|
|
|
// check if there are outgoing connections in that case we need to stop processing
|
|
|
|
|
boolean outgoingConnections = false;
|
|
|
|
|
RouteSegment r = roadNext;
|
|
|
|
|
while(r != null && !outgoingConnections) {
|
|
|
|
|
if(r.road.id != road.id || r.getSegmentStart() != 0 || r.road.getOneway() != 1){
|
|
|
|
|
outgoingConnections = true;
|
|
|
|
|
}
|
|
|
|
|
r = r.next;
|
|
|
|
|
}
|
|
|
|
|
if (outgoingConnections) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float distStartObstacles = segment.distanceFromStart + calculateTimeWithObstacles(ctx, road, segmentDist , obstaclesTime);
|
|
|
|
|
processIntersections(ctx, graphSegments, visitedSegments,
|
|
|
|
|
distStartObstacles, segment, segmentEnd,
|
|
|
|
|
roadNext, reverseWaySearch, outgoingConnections);
|
|
|
|
|
float distStartObstacles = segment.distanceFromStart + calculateTimeWithObstacles(ctx, road, segmentDist , obstaclesTime);
|
|
|
|
|
|
|
|
|
|
// We don't check if there are outgoing connections
|
|
|
|
|
boolean processFurther = processIntersections(ctx, graphSegments, visitedSegments, distStartObstacles,
|
|
|
|
|
segment, segmentPoint, roadNext, reverseWaySearch, doNotAddIntersections);
|
|
|
|
|
if (!processFurther) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(initDirectionAllowed && ctx.visitor != null){
|
|
|
|
|
ctx.visitor.visitSegment(segment, segmentEnd, true);
|
|
|
|
|
ctx.visitor.visitSegment(segment, segmentPoint, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean checkIfInitialMovementAllowedOnSegment(final RoutingContext ctx, boolean reverseWaySearch,
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedSegments, RouteSegment segment, boolean direction, final RouteDataObject road
|
|
|
|
|
) {
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedSegments, RouteSegment segment, final RouteDataObject road) {
|
|
|
|
|
boolean directionAllowed;
|
|
|
|
|
final int middle = segment.getSegmentStart();
|
|
|
|
|
int oneway = ctx.getRouter().isOneWay(road);
|
|
|
|
|
// use positive direction as agreed
|
|
|
|
|
if (!reverseWaySearch) {
|
|
|
|
|
if(direction){
|
|
|
|
|
if(segment.isPositive()){
|
|
|
|
|
directionAllowed = oneway >= 0;
|
|
|
|
|
} else {
|
|
|
|
|
directionAllowed = oneway <= 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if(direction){
|
|
|
|
|
if(segment.isPositive()){
|
|
|
|
|
directionAllowed = oneway <= 0;
|
|
|
|
|
} else {
|
|
|
|
|
directionAllowed = oneway >= 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(direction) {
|
|
|
|
|
if(middle == road.getPointsLength() - 1 ||
|
|
|
|
|
visitedSegments.containsKey(calculateRoutePointId(road, middle, true)) ||
|
|
|
|
|
segment.getAllowedDirection() == -1) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if(middle == 0 || visitedSegments.containsKey(calculateRoutePointId(road, middle - 1, false)) ||
|
|
|
|
|
segment.getAllowedDirection() == 1) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(directionAllowed && visitedSegments.containsKey(calculateRoutePointId(segment, segment.isPositive()))) {
|
|
|
|
|
directionAllowed = false;
|
|
|
|
|
}
|
|
|
|
|
return directionAllowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean checkIfOppositieSegmentWasVisited(final RoutingContext ctx, boolean reverseWaySearch,
|
|
|
|
|
PriorityQueue<RouteSegment> graphSegments, RouteSegment segment, TLongObjectHashMap<RouteSegment> oppositeSegments,
|
|
|
|
|
final RouteDataObject road, int segmentEnd, boolean positive, int intervalId, float segmentDist, float obstaclesTime) {
|
|
|
|
|
long opp = calculateRoutePointId(road, intervalId, !positive);
|
|
|
|
|
int segmentPoint, float segmentDist, float obstaclesTime) {
|
|
|
|
|
RouteDataObject road = segment.getRoad();
|
|
|
|
|
long opp = calculateRoutePointId(road, segment.isPositive() ? segmentPoint - 1 : segmentPoint, !segment.isPositive());
|
|
|
|
|
if (oppositeSegments.containsKey(opp)) {
|
|
|
|
|
RouteSegment opposite = oppositeSegments.get(opp);
|
|
|
|
|
if (opposite.getSegmentStart() == segmentEnd) {
|
|
|
|
|
FinalRouteSegment frs = new FinalRouteSegment(road, segment.getSegmentStart());
|
|
|
|
|
float distStartObstacles = segment.distanceFromStart + calculateTimeWithObstacles(ctx, road, segmentDist , obstaclesTime);
|
|
|
|
|
frs.setParentRoute(segment.getParentRoute());
|
|
|
|
|
frs.setParentSegmentEnd(segment.getParentSegmentEnd());
|
|
|
|
|
frs.reverseWaySearch = reverseWaySearch;
|
|
|
|
|
frs.distanceFromStart = opposite.distanceFromStart + distStartObstacles;
|
|
|
|
|
frs.distanceToEnd = 0;
|
|
|
|
|
frs.opposite = opposite;
|
|
|
|
|
graphSegments.add(frs);
|
|
|
|
|
return true;
|
|
|
|
|
FinalRouteSegment frs = new FinalRouteSegment(road, segmentPoint);
|
|
|
|
|
float distStartObstacles = segment.distanceFromStart
|
|
|
|
|
+ calculateTimeWithObstacles(ctx, road, segmentDist, obstaclesTime);
|
|
|
|
|
frs.setParentRoute(segment);
|
|
|
|
|
frs.setParentSegmentEnd(segmentPoint);
|
|
|
|
|
frs.reverseWaySearch = reverseWaySearch;
|
|
|
|
|
frs.distanceFromStart = opposite.distanceFromStart + distStartObstacles;
|
|
|
|
|
frs.distanceToEnd = 0;
|
|
|
|
|
frs.opposite = opposite;
|
|
|
|
|
graphSegments.add(frs);
|
|
|
|
|
if(TRACE_ROUTING){
|
|
|
|
|
printRoad(" >> Final segment : ", frs);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -475,8 +452,23 @@ public class BinaryRoutePlanner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long calculateRoutePointId(final RouteDataObject road, int intervalId, boolean positive) {
|
|
|
|
|
if(intervalId < 0) {
|
|
|
|
|
// should be assert
|
|
|
|
|
throw new IllegalStateException("Assert failed");
|
|
|
|
|
}
|
|
|
|
|
return (road.getId() << ROUTE_POINTS) + (intervalId << 1) + (positive ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long calculateRoutePointId(RouteSegment segm, boolean direction) {
|
|
|
|
|
if(segm.getSegmentStart() == 0 && !direction) {
|
|
|
|
|
throw new IllegalStateException("Assert failed");
|
|
|
|
|
}
|
|
|
|
|
if(segm.getSegmentStart() == segm.getRoad().getPointsLength() - 1 && direction) {
|
|
|
|
|
throw new IllegalStateException("Assert failed");
|
|
|
|
|
}
|
|
|
|
|
return calculateRoutePointId(segm.getRoad(),
|
|
|
|
|
direction ? segm.getSegmentStart() : segm.getSegmentStart() - 1, direction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean proccessRestrictions(RoutingContext ctx, RouteDataObject road, RouteSegment inputNext, boolean reverseWay) {
|
|
|
|
@ -557,20 +549,27 @@ public class BinaryRoutePlanner {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void processIntersections(RoutingContext ctx, PriorityQueue<RouteSegment> graphSegments,
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedSegments, float distFromStart,
|
|
|
|
|
RouteSegment segment, int segmentEnd,
|
|
|
|
|
RouteSegment inputNext, boolean reverseWaySearch,
|
|
|
|
|
boolean addSameRoadFutureDirection) {
|
|
|
|
|
byte searchDirection = reverseWaySearch ? (byte)-1 : (byte)1;
|
|
|
|
|
boolean thereAreRestrictions = proccessRestrictions(ctx, segment.road, inputNext, reverseWaySearch);
|
|
|
|
|
private boolean processIntersections(RoutingContext ctx, PriorityQueue<RouteSegment> graphSegments,
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedSegments, float distFromStart, RouteSegment segment,
|
|
|
|
|
short segmentPoint, RouteSegment inputNext, boolean reverseWaySearch, boolean doNotAddIntersections) {
|
|
|
|
|
boolean thereAreRestrictions ;
|
|
|
|
|
boolean processFurther = true;
|
|
|
|
|
Iterator<RouteSegment> nextIterator = null;
|
|
|
|
|
if (thereAreRestrictions) {
|
|
|
|
|
nextIterator = ctx.segmentsToVisitPrescripted.iterator();
|
|
|
|
|
if(TRACE_ROUTING){
|
|
|
|
|
println(" >> There are restrictions");
|
|
|
|
|
if(inputNext != null && inputNext.getRoad().getId() == segment.getRoad().getId() && inputNext.next == null) {
|
|
|
|
|
thereAreRestrictions = false;
|
|
|
|
|
} else {
|
|
|
|
|
thereAreRestrictions = proccessRestrictions(ctx, segment.road, inputNext, reverseWaySearch);
|
|
|
|
|
if (thereAreRestrictions) {
|
|
|
|
|
nextIterator = ctx.segmentsToVisitPrescripted.iterator();
|
|
|
|
|
if (TRACE_ROUTING) {
|
|
|
|
|
println(" >> There are restrictions");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int targetEndX = reverseWaySearch ? ctx.startX : ctx.targetX;
|
|
|
|
|
int targetEndY = reverseWaySearch ? ctx.startY : ctx.targetY;
|
|
|
|
|
float distanceToEnd = h(ctx, segment.getRoad().getPoint31XTile(segmentPoint), segment.getRoad()
|
|
|
|
|
.getPoint31YTile(segmentPoint), targetEndX, targetEndY);
|
|
|
|
|
// Calculate possible ways to put into priority queue
|
|
|
|
|
RouteSegment next = inputNext;
|
|
|
|
|
boolean hasNext = nextIterator == null || nextIterator.hasNext();
|
|
|
|
@ -578,69 +577,33 @@ public class BinaryRoutePlanner {
|
|
|
|
|
if (nextIterator != null) {
|
|
|
|
|
next = nextIterator.next();
|
|
|
|
|
}
|
|
|
|
|
boolean nextPlusNotAllowed = (next.getSegmentStart() == next.road.getPointsLength() - 1) ||
|
|
|
|
|
visitedSegments.containsKey(calculateRoutePointId(next.road, next.getSegmentStart(), true));
|
|
|
|
|
boolean nextMinusNotAllowed = (next.getSegmentStart() == 0) ||
|
|
|
|
|
visitedSegments.containsKey(calculateRoutePointId(next.road, next.getSegmentStart() - 1, false));
|
|
|
|
|
boolean sameRoadFutureDirection = next.road.id == segment.road.id && next.getSegmentStart() == segmentEnd;
|
|
|
|
|
// road.id could be equal on roundabout, but we should accept them
|
|
|
|
|
boolean alreadyVisited = nextPlusNotAllowed && nextMinusNotAllowed;
|
|
|
|
|
boolean skipRoad = sameRoadFutureDirection && !addSameRoadFutureDirection;
|
|
|
|
|
if (!alreadyVisited && !skipRoad) {
|
|
|
|
|
int targetEndX = reverseWaySearch? ctx.startX : ctx.targetX;
|
|
|
|
|
int targetEndY = reverseWaySearch? ctx.startY : ctx.targetY;
|
|
|
|
|
float distanceToEnd = h(ctx, segment.getRoad().getPoint31XTile(segmentEnd),
|
|
|
|
|
segment.getRoad().getPoint31YTile(segmentEnd), targetEndX, targetEndY, next);
|
|
|
|
|
// assigned to wrong direction
|
|
|
|
|
if(next.getDirectionAssigned() == -searchDirection){
|
|
|
|
|
next = new RouteSegment(next.getRoad(), next.getSegmentStart());
|
|
|
|
|
if (next.getSegmentStart() == segmentPoint && next.getRoad().getId() == segment.getRoad().id) {
|
|
|
|
|
// find segment itself
|
|
|
|
|
// (and process it as other with small exception that we don't add to graph segments and process immediately)
|
|
|
|
|
RouteSegment itself = next.initRouteSegment(segment.isPositive());
|
|
|
|
|
if(itself == null) {
|
|
|
|
|
// do nothing
|
|
|
|
|
} else if (itself.getParentRoute() == null
|
|
|
|
|
|| ctx.roadPriorityComparator(itself.distanceFromStart, itself.distanceToEnd, distFromStart,
|
|
|
|
|
distanceToEnd) > 0) {
|
|
|
|
|
itself.distanceFromStart = distFromStart;
|
|
|
|
|
itself.distanceToEnd = distanceToEnd;
|
|
|
|
|
itself.setParentRoute(segment);
|
|
|
|
|
itself.setParentSegmentEnd(segmentPoint);
|
|
|
|
|
} else {
|
|
|
|
|
// we already processed that segment earlier or it is in graph segments
|
|
|
|
|
// and we had better results (so we shouldn't process)
|
|
|
|
|
processFurther = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next.getParentRoute() == null
|
|
|
|
|
|| ctx.roadPriorityComparator(next.distanceFromStart, next.distanceToEnd, distFromStart, distanceToEnd) > 0) {
|
|
|
|
|
if (next.getParentRoute() != null) {
|
|
|
|
|
if (!graphSegments.remove(next)) {
|
|
|
|
|
throw new IllegalStateException("Should be handled by direction flag");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
next.assignDirection(searchDirection);
|
|
|
|
|
next.distanceFromStart = distFromStart;
|
|
|
|
|
next.distanceToEnd = distanceToEnd;
|
|
|
|
|
if(sameRoadFutureDirection) {
|
|
|
|
|
next.setAllowedDirection((byte) (segment.getSegmentStart() < next.getSegmentStart() ? 1 : - 1));
|
|
|
|
|
}
|
|
|
|
|
if(TRACE_ROUTING) {
|
|
|
|
|
printRoad(" >>", next);
|
|
|
|
|
}
|
|
|
|
|
// put additional information to recover whole route after
|
|
|
|
|
next.setParentRoute(segment);
|
|
|
|
|
next.setParentSegmentEnd(segmentEnd);
|
|
|
|
|
|
|
|
|
|
graphSegments.add(next);
|
|
|
|
|
}
|
|
|
|
|
if (ctx.visitor != null) {
|
|
|
|
|
// ctx.visitor.visitSegment(next, false);
|
|
|
|
|
}
|
|
|
|
|
} else if(!sameRoadFutureDirection){
|
|
|
|
|
// the segment was already visited! We need to follow better route if it exists
|
|
|
|
|
// that is very strange situation and almost exception (it can happen when we underestimate distnceToEnd)
|
|
|
|
|
if (next.getDirectionAssigned() == searchDirection &&
|
|
|
|
|
distFromStart < next.distanceFromStart && next.road.id != segment.road.id) {
|
|
|
|
|
if(ctx.config.heuristicCoefficient <= 1) {
|
|
|
|
|
throw new IllegalStateException("distance from start " + distFromStart + " < " +next.distanceFromStart);
|
|
|
|
|
}
|
|
|
|
|
// That code is incorrect (when segment is processed itself,
|
|
|
|
|
// then it tries to make wrong u-turn) -
|
|
|
|
|
// this situation should be very carefully checked in future (seems to be fixed)
|
|
|
|
|
next.distanceFromStart = distFromStart;
|
|
|
|
|
next.setParentRoute(segment);
|
|
|
|
|
next.setParentSegmentEnd(segmentEnd);
|
|
|
|
|
if (ctx.visitor != null) {
|
|
|
|
|
// ctx.visitor.visitSegment(next, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if(!doNotAddIntersections) {
|
|
|
|
|
RouteSegment nextPos = next.initRouteSegment(true);
|
|
|
|
|
RouteSegment nextNeg = next.initRouteSegment(false);
|
|
|
|
|
processOneRoadIntersection(ctx, graphSegments, visitedSegments, distFromStart, distanceToEnd, segment, segmentPoint,
|
|
|
|
|
nextPos);
|
|
|
|
|
processOneRoadIntersection(ctx, graphSegments, visitedSegments, distFromStart, distanceToEnd, segment, segmentPoint,
|
|
|
|
|
nextNeg);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// iterate to next road
|
|
|
|
|
if (nextIterator == null) {
|
|
|
|
|
next = next.next;
|
|
|
|
@ -649,6 +612,61 @@ public class BinaryRoutePlanner {
|
|
|
|
|
hasNext = nextIterator.hasNext();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return processFurther;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
|
private void processOneRoadIntersection(RoutingContext ctx, PriorityQueue<RouteSegment> graphSegments,
|
|
|
|
|
TLongObjectHashMap<RouteSegment> visitedSegments, float distFromStart, float distanceToEnd, RouteSegment segment,
|
|
|
|
|
int segmentPoint, RouteSegment next) {
|
|
|
|
|
if (next != null) {
|
|
|
|
|
float obstaclesTime = (float) ctx.getRouter().calculateTurnTime(next, next.isPositive()?
|
|
|
|
|
next.getRoad().getPointsLength() - 1 : 0,
|
|
|
|
|
segment, segmentPoint);
|
|
|
|
|
distFromStart += obstaclesTime;
|
|
|
|
|
if(TEST_SPECIFIC && next.road.getId() == TEST_ID) {
|
|
|
|
|
printRoad(" !? distFromStart=" + +distFromStart + " from " + segment.getRoad().getId() +
|
|
|
|
|
" dir=" + segment.getDirectionAssigned() +
|
|
|
|
|
" distToEnd=" + distanceToEnd +
|
|
|
|
|
" segmentPoint="+ segmentPoint + " -- ", next);
|
|
|
|
|
}
|
|
|
|
|
if (!visitedSegments.containsKey(calculateRoutePointId(next, next.isPositive()))) {
|
|
|
|
|
if (next.getParentRoute() == null
|
|
|
|
|
|| ctx.roadPriorityComparator(next.distanceFromStart, next.distanceToEnd,
|
|
|
|
|
distFromStart, distanceToEnd) > 0) {
|
|
|
|
|
next.distanceFromStart = distFromStart;
|
|
|
|
|
next.distanceToEnd = distanceToEnd;
|
|
|
|
|
if (TRACE_ROUTING) {
|
|
|
|
|
printRoad(" >>", next);
|
|
|
|
|
}
|
|
|
|
|
// put additional information to recover whole route after
|
|
|
|
|
next.setParentRoute(segment);
|
|
|
|
|
next.setParentSegmentEnd(segmentPoint);
|
|
|
|
|
graphSegments.add(next);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// the segment was already visited! We need to follow better route if it exists
|
|
|
|
|
// that is very exceptional situation and almost exception, it can happen
|
|
|
|
|
// 1. when we underestimate distnceToEnd - wrong h()
|
|
|
|
|
// 2. because we process not small segments but the whole road, it could be that
|
|
|
|
|
// deviation from the road is faster than following the whole road itself!
|
|
|
|
|
if (distFromStart < next.distanceFromStart) {
|
|
|
|
|
if (ctx.config.heuristicCoefficient <= 1) {
|
|
|
|
|
System.err.println("! Alert distance from start " + distFromStart + " < "
|
|
|
|
|
+ next.distanceFromStart + " id="+next.road.id);
|
|
|
|
|
}
|
|
|
|
|
// A: we can't change parent route just here, because we need to update visitedSegments
|
|
|
|
|
// presumably we can do visitedSegments.put(calculateRoutePointId(next), next);
|
|
|
|
|
// next.distanceFromStart = distFromStart;
|
|
|
|
|
// next.setParentRoute(segment);
|
|
|
|
|
// next.setParentSegmentEnd(segmentPoint);
|
|
|
|
|
if (ctx.visitor != null) {
|
|
|
|
|
// ctx.visitor.visitSegment(next, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -670,6 +688,7 @@ public class BinaryRoutePlanner {
|
|
|
|
|
final RouteDataObject road;
|
|
|
|
|
// needed to store intersection of routes
|
|
|
|
|
RouteSegment next = null;
|
|
|
|
|
RouteSegment oppositeDirection = null;
|
|
|
|
|
|
|
|
|
|
// search context (needed for searching route)
|
|
|
|
|
// Initially it should be null (!) because it checks was it segment visited before
|
|
|
|
@ -677,8 +696,6 @@ public class BinaryRoutePlanner {
|
|
|
|
|
short parentSegmentEnd = 0;
|
|
|
|
|
// 1 - positive , -1 - negative, 0 not assigned
|
|
|
|
|
byte directionAssgn = 0;
|
|
|
|
|
// 1 - only positive allowed, -1 - only negative allowed
|
|
|
|
|
byte allowedDirection = 0;
|
|
|
|
|
|
|
|
|
|
// distance measured in time (seconds)
|
|
|
|
|
float distanceFromStart = 0;
|
|
|
|
@ -689,6 +706,39 @@ public class BinaryRoutePlanner {
|
|
|
|
|
this.segStart = (short) segmentStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RouteSegment initRouteSegment(boolean positiveDirection) {
|
|
|
|
|
if(segStart == 0 && !positiveDirection) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if(segStart == road.getPointsLength() - 1 && positiveDirection) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
RouteSegment rs = this;
|
|
|
|
|
if(directionAssgn == 0) {
|
|
|
|
|
rs.directionAssgn = (byte) (positiveDirection ? 1 : -1);
|
|
|
|
|
} else {
|
|
|
|
|
if(positiveDirection != (directionAssgn == 1)) {
|
|
|
|
|
if(oppositeDirection == null) {
|
|
|
|
|
oppositeDirection = new RouteSegment(road, segStart);
|
|
|
|
|
oppositeDirection.directionAssgn = (byte) (positiveDirection ? 1 : -1);
|
|
|
|
|
}
|
|
|
|
|
if ((oppositeDirection.directionAssgn == 1) != positiveDirection) {
|
|
|
|
|
throw new IllegalStateException();
|
|
|
|
|
}
|
|
|
|
|
rs = oppositeDirection;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getSegmentIntervalInd() {
|
|
|
|
|
boolean positive = directionAssgn >= 0;
|
|
|
|
|
if(!positive) {
|
|
|
|
|
return segStart - 1;
|
|
|
|
|
}
|
|
|
|
|
return segStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte getDirectionAssigned(){
|
|
|
|
|
return directionAssgn;
|
|
|
|
|
}
|
|
|
|
@ -697,6 +747,10 @@ public class BinaryRoutePlanner {
|
|
|
|
|
return parentRoute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isPositive() {
|
|
|
|
|
return directionAssgn == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setParentRoute(RouteSegment parentRoute) {
|
|
|
|
|
this.parentRoute = parentRoute;
|
|
|
|
|
}
|
|
|
|
@ -705,15 +759,6 @@ public class BinaryRoutePlanner {
|
|
|
|
|
directionAssgn = b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte getAllowedDirection() {
|
|
|
|
|
return allowedDirection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setAllowedDirection(byte allowedDirection) {
|
|
|
|
|
this.allowedDirection = allowedDirection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setParentSegmentEnd(int parentSegmentEnd) {
|
|
|
|
|
this.parentSegmentEnd = (short) parentSegmentEnd;
|
|
|
|
|
}
|
|
|
|
@ -726,7 +771,7 @@ public class BinaryRoutePlanner {
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getSegmentStart() {
|
|
|
|
|
public short getSegmentStart() {
|
|
|
|
|
return segStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|