2013-04-18 23:35:02 +02:00
package net.osmand.router ;
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 ;
import net.osmand.binary.RouteDataBorderLinePoint ;
import net.osmand.binary.RouteDataObject ;
import net.osmand.osm.MapRenderingTypes ;
2014-01-17 00:29:41 +01:00
import net.osmand.router.RoutingContext.RoutingSubregionTile ;
2013-04-18 23:35:02 +02:00
import net.osmand.util.MapUtils ;
import org.apache.commons.logging.Log ;
public class BinaryRoutePlanner {
2013-08-03 15:09:27 +02:00
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 ;
2013-04-18 23:35:02 +02:00
protected static final Log log = PlatformUtil . getLog ( BinaryRoutePlanner . class ) ;
private static final int ROUTE_POINTS = 11 ;
private static final boolean TRACE_ROUTING = false ;
public static double squareRootDist ( int x1 , int y1 , int x2 , int y2 ) {
// translate into meters
double dy = MapUtils . convert31YToMeters ( y1 , y2 ) ;
double dx = MapUtils . convert31XToMeters ( x1 , x2 ) ;
return Math . sqrt ( dx * dx + dy * dy ) ;
// return measuredDist(x1, y1, x2, y2);
}
private static class SegmentsComparator implements Comparator < RouteSegment > {
final RoutingContext ctx ;
public SegmentsComparator ( RoutingContext ctx ) {
this . ctx = ctx ;
}
@Override
public int compare ( RouteSegment o1 , RouteSegment o2 ) {
return ctx . roadPriorityComparator ( o1 . distanceFromStart , o1 . distanceToEnd , o2 . distanceFromStart , o2 . distanceToEnd ) ;
}
}
private static class NonHeuristicSegmentsComparator implements Comparator < RouteSegment > {
public NonHeuristicSegmentsComparator ( ) {
}
@Override
public int compare ( RouteSegment o1 , RouteSegment o2 ) {
return roadPriorityComparator ( o1 . distanceFromStart , o1 . distanceToEnd , o2 . distanceFromStart , o2 . distanceToEnd , 0 . 5 ) ;
}
}
/ * *
* Calculate route between start . segmentEnd and end . segmentStart ( using A * algorithm )
* return list of segments
* /
@SuppressWarnings ( " unused " )
FinalRouteSegment searchRouteInternal ( final RoutingContext ctx , RouteSegment start , RouteSegment end ) throws InterruptedException , IOException {
// measure time
ctx . timeToLoad = 0 ;
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 ;
}
}
2014-01-17 00:29:41 +01:00
newSearchRoute ( ctx , start , end ) ;
2013-04-18 23:35:02 +02:00
// Initializing priority queue to visit way segments
Comparator < RouteSegment > nonHeuristicSegmentsComparator = new NonHeuristicSegmentsComparator ( ) ;
PriorityQueue < RouteSegment > graphDirectSegments = new PriorityQueue < RouteSegment > ( 50 , new SegmentsComparator ( ctx ) ) ;
PriorityQueue < RouteSegment > graphReverseSegments = new PriorityQueue < RouteSegment > ( 50 , new SegmentsComparator ( ctx ) ) ;
// Set to not visit one segment twice (stores road.id << X + segmentStart)
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 ) ;
// Extract & analyze segment with min(f(x)) from queue while final segment is not found
boolean inverse = false ;
boolean init = false ;
PriorityQueue < RouteSegment > graphSegments ;
if ( inverse ) {
graphSegments = graphReverseSegments ;
} else {
graphSegments = graphDirectSegments ;
}
ctx . loadBorderPoints ( ) ;
2014-01-09 01:33:46 +01:00
/ * RoutingContext baseCtx = new RoutingContext ( ctx ) ;
baseCtx . newRoutingPoints ( ) ; * /
2013-04-18 23:35:02 +02:00
FinalRouteSegment finalSegment = null ;
while ( ! graphSegments . isEmpty ( ) ) {
RouteSegment segment = graphSegments . poll ( ) ;
// use accumulative approach
ctx . memoryOverhead = ( visitedDirectSegments . size ( ) + visitedOppositeSegments . size ( ) ) * STANDARD_ROAD_VISITED_OVERHEAD +
( graphDirectSegments . size ( ) +
graphReverseSegments . size ( ) ) * STANDARD_ROAD_IN_QUEUE_OVERHEAD ;
if ( TRACE_ROUTING ) {
printRoad ( " > " , segment ) ;
}
if ( segment instanceof FinalRouteSegment ) {
if ( RoutingContext . SHOW_GC_SIZE ) {
log . warn ( " Estimated overhead " + ( ctx . memoryOverhead / ( 1 < < 20 ) ) + " mb " ) ;
printMemoryConsumption ( " Memory occupied after calculation : " ) ;
}
finalSegment = ( FinalRouteSegment ) segment ;
break ;
}
if ( ctx . memoryOverhead > ctx . config . memoryLimitation * 0 . 95 & & RoutingContext . SHOW_GC_SIZE ) {
printMemoryConsumption ( " Memory occupied before exception : " ) ;
}
if ( ctx . memoryOverhead > ctx . config . memoryLimitation * 0 . 95 ) {
throw new IllegalStateException ( " There is no enough memory " + ctx . config . memoryLimitation / ( 1 < < 20 ) + " Mb " ) ;
}
ctx . visitedSegments + + ;
if ( ! inverse ) {
processRouteSegment ( ctx , false , graphDirectSegments , visitedDirectSegments ,
segment , visitedOppositeSegments , true ) ;
processRouteSegment ( ctx , false , graphDirectSegments , visitedDirectSegments ,
segment , visitedOppositeSegments , false ) ;
} else {
processRouteSegment ( ctx , true , graphReverseSegments , visitedOppositeSegments , segment ,
visitedDirectSegments , true ) ;
processRouteSegment ( ctx , true , graphReverseSegments , visitedOppositeSegments , segment ,
visitedDirectSegments , false ) ;
}
updateCalculationProgress ( ctx , graphDirectSegments , graphReverseSegments ) ;
if ( graphReverseSegments . isEmpty ( ) ) {
throw new IllegalArgumentException ( " Route is not found to selected target point. " ) ;
}
if ( 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 ( graphDirectSegments . size ( ) * 1 . 3 > graphReverseSegments . size ( ) ) {
inverse = true ;
} else if ( graphDirectSegments . size ( ) < 1 . 3 * graphReverseSegments . size ( ) ) {
inverse = false ;
}
} else {
// different strategy : use onedirectional graph
inverse = ctx . getPlanRoadDirection ( ) < 0 ;
}
if ( inverse ) {
graphSegments = graphReverseSegments ;
} else {
graphSegments = graphDirectSegments ;
}
// check if interrupted
if ( ctx . calculationProgress ! = null & & ctx . calculationProgress . isCancelled ) {
throw new InterruptedException ( " Route calculation interrupted " ) ;
}
}
printDebugMemoryInformation ( ctx , graphDirectSegments , graphReverseSegments , visitedDirectSegments , visitedOppositeSegments ) ;
return finalSegment ;
}
2014-01-17 00:29:41 +01:00
private void newSearchRoute ( RoutingContext ctx , RouteSegment start , RouteSegment end ) {
int sx31 = start . getRoad ( ) . getPoint31XTile ( start . getSegmentStart ( ) ) ;
int sy31 = start . getRoad ( ) . getPoint31YTile ( start . getSegmentStart ( ) ) ;
int ex31 = end . getRoad ( ) . getPoint31XTile ( end . getSegmentStart ( ) ) ;
int ey31 = end . getRoad ( ) . getPoint31YTile ( end . getSegmentStart ( ) ) ;
int zoom = 11 ;
int sz = 31 - zoom ;
int sx = sx31 > > sz ;
int ex = ex31 > > sz ;
int sy = sy31 > > sz ;
int ey = ey31 > > sz ;
int h = Math . abs ( sy - ey ) ;
int w = Math . abs ( sx - ex ) ;
int dh = ( h + w ) / 6 ;
int dw = ( h + w ) / 6 ;
System . out . println ( " Bbox : ( " + sx + " , " + sy + " - " + ex + " , " + ey + " ) - " +
( w + dw * 2 ) + " x " + ( h + dh * 2 ) ) ;
System . out . println ( " Load tiles " + ( ( w + dw * 2 ) * ( h + dh * 2 ) ) ) ;
int total = 0 ;
for ( int x = - dw ; x < = w + dw ; x + + ) {
for ( int y = - dh ; y < = h + dh ; y + + ) {
int tileX = Math . min ( sx , ex ) + x ;
int tileY = Math . min ( sy , ey ) + y ;
float lat = ( float ) MapUtils . getLatitudeFromTile ( zoom , tileY ) ;
float lon = ( float ) MapUtils . getLongitudeFromTile ( zoom , tileX ) ;
if ( ( x = = - dw & & y = = - dh ) ) {
System . out . println ( " LeftTop : " + lat + " , " + lon ) ;
}
if ( ( x = = w + dw & & y = = h + dh ) ) {
System . out . println ( " RightBottom : " + lat + " , " + lon ) ;
}
List < RoutingSubregionTile > ts = ctx . loadTileHeaders ( sz , tileX , tileY ) ;
if ( ts ! = null ) {
total + = ts . size ( ) ;
}
}
}
System . out . println ( " Total loaded " + total + " " + ctx . subregionTiles . size ( ) ) ;
List < RouteDataObject > ls = new ArrayList < RouteDataObject > ( ) ;
TLongObjectHashMap < RouteDataObject > dup = new TLongObjectHashMap < RouteDataObject > ( ) ;
long time = System . currentTimeMillis ( ) ;
for ( RoutingSubregionTile st : ctx . subregionTiles ) {
ctx . loadSubregionTile ( st , true ) ;
st . loadAllObjects ( ls , ctx , dup ) ;
}
System . out . println ( " Total obj to check " + ls . size ( ) ) ;
System . out . println ( " Time " + ctx . timeToLoadHeaders / 1e6 + " " + ( System . currentTimeMillis ( ) - time ) ) ;
// ctx.loadTileHeaders(zoom, tileX, tileY)
}
2013-04-18 23:35:02 +02:00
private void printMemoryConsumption ( String string ) {
long h1 = RoutingContext . runGCUsedMemory ( ) ;
float mb = ( 1 < < 20 ) ;
log . warn ( string + h1 / mb ) ;
}
private void updateCalculationProgress ( final RoutingContext ctx , PriorityQueue < RouteSegment > graphDirectSegments ,
PriorityQueue < RouteSegment > graphReverseSegments ) {
if ( ctx . calculationProgress ! = null ) {
ctx . calculationProgress . reverseSegmentQueueSize = graphReverseSegments . size ( ) ;
ctx . calculationProgress . directSegmentQueueSize = graphDirectSegments . size ( ) ;
if ( graphDirectSegments . size ( ) > 0 ) {
ctx . calculationProgress . distanceFromBegin =
Math . max ( graphDirectSegments . peek ( ) . distanceFromStart , ctx . calculationProgress . distanceFromBegin ) ;
}
2014-01-17 00:29:41 +01:00
if ( graphReverseSegments . size ( ) > 0 ) {
2013-04-18 23:35:02 +02:00
ctx . calculationProgress . distanceFromEnd =
Math . max ( graphReverseSegments . peek ( ) . distanceFromStart , ctx . calculationProgress . distanceFromBegin ) ;
}
}
}
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 ) {
pr = " pend= " + segment . parentSegmentEnd + " parent= " + segment . parentRoute . road ;
} else {
pr = " " ;
}
println ( prefix + " " + segment . road + " ind= " + segment . getSegmentStart ( ) +
" ds= " + ( ( float ) segment . distanceFromStart ) + " es= " + ( ( float ) segment . distanceToEnd ) + pr ) ;
}
private float estimatedDistance ( final RoutingContext ctx , int targetEndX , int targetEndY ,
int startX , int startY ) {
double distance = squareRootDist ( startX , startY , targetEndX , targetEndY ) ;
return ( float ) ( distance / ctx . getRouter ( ) . getMaxDefaultSpeed ( ) ) ;
}
protected static float h ( RoutingContext ctx , int begX , int begY , int endX , int endY ,
RouteSegment next ) {
double distToFinalPoint = squareRootDist ( begX , begY , endX , endY ) ;
if ( RoutingContext . USE_BORDER_LINES ) {
int begBorder = ctx . searchBorderLineIndex ( begY ) ;
int endBorder = ctx . searchBorderLineIndex ( endY ) ;
if ( begBorder ! = endBorder ) {
double res = 0 ;
boolean plus = begBorder < endBorder ;
boolean beginEqStart = begX = = ctx . startX & & begY = = ctx . startY ;
boolean beginEqTarget = begX = = ctx . targetX & & begY = = ctx . targetY ;
boolean endEqStart = endX = = ctx . startX & & endY = = ctx . startY ;
boolean endEqTarget = endX = = ctx . targetX & & endY = = ctx . targetY ;
if ( endEqStart | | endEqTarget ) {
// we start from intermediate point and end in target or start
if ( begX > ctx . leftBorderBoundary & & begX < ctx . rightBorderBoundary ) {
List < RouteDataBorderLinePoint > pnts = ctx . borderLines [ plus ? begBorder : begBorder - 1 ] . borderPoints ;
for ( RouteDataBorderLinePoint p : pnts ) {
double f = ( endEqTarget ? p . distanceToEndPoint : p . distanceToStartPoint ) + squareRootDist ( p . x , p . y , begX , begY ) ;
if ( res > f | | res < = 0 ) {
res = f ;
}
}
}
} else if ( beginEqStart | | beginEqTarget ) {
if ( endX > ctx . leftBorderBoundary & & endX < ctx . rightBorderBoundary ) {
List < RouteDataBorderLinePoint > pnts = ctx . borderLines [ plus ? endBorder - 1 : endBorder ] . borderPoints ;
for ( RouteDataBorderLinePoint p : pnts ) {
double f = ( beginEqTarget ? p . distanceToEndPoint : p . distanceToStartPoint )
+ squareRootDist ( p . x , p . y , endX , endY ) ;
if ( res > f | | res < = 0 ) {
res = f ;
}
}
}
} else {
throw new IllegalStateException ( ) ;
}
if ( res > 0 ) {
if ( res < distToFinalPoint - 0 . 01 ) {
throw new IllegalStateException ( " Estimated distance " + res + " > " + distToFinalPoint ) ;
}
// if(endEqStart && res - distToFinalPoint > 13000) {
// System.out.println(" Res="+res + " dist=" +distToFinalPoint);
// }
distToFinalPoint = res ;
} else {
// FIXME put penalty
// distToFinalPoint = distToFinalPoint;
}
}
}
double result = distToFinalPoint / ctx . getRouter ( ) . getMaxDefaultSpeed ( ) ;
2014-01-17 00:29:41 +01:00
// result *= 0.8;
2013-04-18 23:35:02 +02:00
return ( float ) result ;
}
private static void println ( String logMsg ) {
// log.info(logMsg);
System . out . println ( logMsg ) ;
}
private static void printInfo ( String logMsg ) {
log . warn ( logMsg ) ;
}
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 ) ;
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 +
" , loaded more than once same tiles "
+ ctx . loadedPrevUnloadedTiles ) ;
printInfo ( " Visited roads " + ctx . visitedSegments + " , relaxed roads " + ctx . relaxedSegments ) ;
if ( graphDirectSegments ! = null & & graphReverseSegments ! = null ) {
printInfo ( " Priority queues sizes : " + graphDirectSegments . size ( ) + " / " + graphReverseSegments . size ( ) ) ;
}
if ( visitedDirectSegments ! = null & & visitedOppositeSegments ! = null ) {
printInfo ( " Visited interval sizes: " + visitedDirectSegments . size ( ) + " / " + visitedOppositeSegments . size ( ) ) ;
}
for ( int k = 0 ; k < ctx . borderLines . length ; k + + ) {
System . out . println ( " Line " + ( ctx . borderLineCoordinates [ k ] > > 17 ) + " points " + ctx . borderLines [ k ] . borderPoints . size ( ) ) ;
}
}
private void processRouteSegment ( final RoutingContext ctx , boolean reverseWaySearch ,
PriorityQueue < RouteSegment > graphSegments , TLongObjectHashMap < RouteSegment > visitedSegments ,
RouteSegment segment , TLongObjectHashMap < RouteSegment > oppositeSegments , boolean direction ) throws IOException {
final RouteDataObject road = segment . road ;
boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment ( ctx , reverseWaySearch , visitedSegments , segment , direction , road ) ;
boolean directionAllowed = initDirectionAllowed ;
// 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 ( ) ;
while ( directionAllowed ) {
int prevInd = segmentEnd ;
if ( direction ) {
segmentEnd + + ;
} else {
segmentEnd - - ;
}
if ( segmentEnd < 0 | | segmentEnd > = 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 ) ;
final int prevx = road . getPoint31XTile ( prevInd ) ;
final int prevy = road . getPoint31YTile ( prevInd ) ;
if ( x = = prevx & & y = = prevy ) {
continue ;
}
if ( RoutingContext . USE_BORDER_LINES ) {
int st = ctx . searchBorderLineIndex ( y ) ;
int tt = ctx . searchBorderLineIndex ( prevy ) ;
if ( st ! = tt ) {
// System.out.print(" " + st + " != " + tt + " " + road.id + " ? ");
for ( int i = Math . min ( st , tt ) ; i < Math . max ( st , tt ) & i < ctx . borderLines . length ; i + + ) {
Iterator < RouteDataBorderLinePoint > pnts = ctx . borderLines [ i ] . borderPoints . iterator ( ) ;
boolean changed = false ;
while ( pnts . hasNext ( ) ) {
RouteDataBorderLinePoint o = pnts . next ( ) ;
if ( o . id = = road . id ) {
// System.out.println("Point removed !");
pnts . remove ( ) ;
changed = true ;
}
}
if ( changed ) {
ctx . updateDistanceForBorderPoints ( ctx . startX , ctx . startY , true ) ;
ctx . updateDistanceForBorderPoints ( ctx . targetX , ctx . targetY , false ) ;
}
}
}
}
// 2. calculate point and try to load neighbor ways if they are not loaded
segmentDist + = squareRootDist ( x , y , prevx , prevy ) ;
// 2.1 calculate possible obstacle plus time
double obstacle = ctx . getRouter ( ) . defineRoutingObstacle ( road , segmentEnd ) ;
if ( obstacle < 0 ) {
directionAllowed = false ;
continue ;
}
obstaclesTime + = obstacle ;
boolean alreadyVisited = checkIfOppositieSegmentWasVisited ( ctx , reverseWaySearch , graphSegments , segment , oppositeSegments , road ,
segmentEnd , direction , intervalId , segmentDist , obstaclesTime ) ;
if ( alreadyVisited ) {
directionAllowed = false ;
continue ;
}
// 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 ) ;
}
}
if ( initDirectionAllowed & & ctx . visitor ! = null ) {
ctx . visitor . visitSegment ( segment , segmentEnd , true ) ;
}
}
private boolean checkIfInitialMovementAllowedOnSegment ( final RoutingContext ctx , boolean reverseWaySearch ,
TLongObjectHashMap < RouteSegment > visitedSegments , RouteSegment segment , boolean direction , 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 ) {
directionAllowed = oneway > = 0 ;
} else {
directionAllowed = oneway < = 0 ;
}
} else {
if ( direction ) {
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 ;
}
}
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 ) ;
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 ;
}
}
return false ;
}
private float calculateTimeWithObstacles ( RoutingContext ctx , RouteDataObject road , float distOnRoadToPass , float obstaclesTime ) {
float priority = ctx . getRouter ( ) . defineSpeedPriority ( road ) ;
float speed = ( ctx . getRouter ( ) . defineSpeed ( road ) * priority ) ;
if ( speed = = 0 ) {
speed = ( ctx . getRouter ( ) . getMinDefaultSpeed ( ) * priority ) ;
}
// speed can not exceed max default speed according to A*
if ( speed > ctx . getRouter ( ) . getMaxDefaultSpeed ( ) ) {
speed = ctx . getRouter ( ) . getMaxDefaultSpeed ( ) ;
}
2013-07-31 20:22:39 +02:00
return obstaclesTime + distOnRoadToPass / speed ;
2013-04-18 23:35:02 +02:00
}
private long calculateRoutePointId ( final RouteDataObject road , int intervalId , boolean positive ) {
return ( road . getId ( ) < < ROUTE_POINTS ) + ( intervalId < < 1 ) + ( positive ? 1 : 0 ) ;
}
private boolean proccessRestrictions ( RoutingContext ctx , RouteDataObject road , RouteSegment inputNext , boolean reverseWay ) {
ctx . segmentsToVisitPrescripted . clear ( ) ;
ctx . segmentsToVisitNotForbidden . clear ( ) ;
boolean exclusiveRestriction = false ;
RouteSegment next = inputNext ;
if ( ! reverseWay & & road . getRestrictionLength ( ) = = 0 ) {
return false ;
}
if ( ! ctx . getRouter ( ) . restrictionsAware ( ) ) {
return false ;
}
while ( next ! = null ) {
int type = - 1 ;
if ( ! reverseWay ) {
for ( int i = 0 ; i < road . getRestrictionLength ( ) ; i + + ) {
if ( road . getRestrictionId ( i ) = = next . road . id ) {
type = road . getRestrictionType ( i ) ;
break ;
}
}
} else {
for ( int i = 0 ; i < next . road . getRestrictionLength ( ) ; i + + ) {
int rt = next . road . getRestrictionType ( i ) ;
long restrictedTo = next . road . getRestrictionId ( i ) ;
if ( restrictedTo = = road . id ) {
type = rt ;
break ;
}
// Check if there is restriction only to the other than current road
if ( rt = = MapRenderingTypes . RESTRICTION_ONLY_RIGHT_TURN | | rt = = MapRenderingTypes . RESTRICTION_ONLY_LEFT_TURN
| | rt = = MapRenderingTypes . RESTRICTION_ONLY_STRAIGHT_ON ) {
// check if that restriction applies to considered junk
RouteSegment foundNext = inputNext ;
while ( foundNext ! = null ) {
if ( foundNext . getRoad ( ) . id = = restrictedTo ) {
break ;
}
foundNext = foundNext . next ;
}
if ( foundNext ! = null ) {
type = REVERSE_WAY_RESTRICTION_ONLY ; // special constant
}
}
}
}
if ( type = = REVERSE_WAY_RESTRICTION_ONLY ) {
// next = next.next; continue;
} else if ( type = = - 1 & & exclusiveRestriction ) {
// next = next.next; continue;
} else if ( type = = MapRenderingTypes . RESTRICTION_NO_LEFT_TURN | | type = = MapRenderingTypes . RESTRICTION_NO_RIGHT_TURN
| | type = = MapRenderingTypes . RESTRICTION_NO_STRAIGHT_ON | | type = = MapRenderingTypes . RESTRICTION_NO_U_TURN ) {
// next = next.next; continue;
} else if ( type = = - 1 ) {
// case no restriction
ctx . segmentsToVisitNotForbidden . add ( next ) ;
} else {
// case exclusive restriction (only_right, only_straight, ...)
// 1. in case we are going backward we should not consider only_restriction
// as exclusive because we have many "in" roads and one "out"
// 2. in case we are going forward we have one "in" and many "out"
if ( ! reverseWay ) {
exclusiveRestriction = true ;
ctx . segmentsToVisitNotForbidden . clear ( ) ;
ctx . segmentsToVisitPrescripted . add ( next ) ;
} else {
ctx . segmentsToVisitNotForbidden . add ( next ) ;
}
}
next = next . next ;
}
ctx . segmentsToVisitPrescripted . addAll ( ctx . segmentsToVisitNotForbidden ) ;
return true ;
}
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 ) ;
Iterator < RouteSegment > nextIterator = null ;
if ( thereAreRestrictions ) {
nextIterator = ctx . segmentsToVisitPrescripted . iterator ( ) ;
if ( TRACE_ROUTING ) {
println ( " >> There are restrictions " ) ;
}
}
// Calculate possible ways to put into priority queue
RouteSegment next = inputNext ;
boolean hasNext = nextIterator = = null | | nextIterator . hasNext ( ) ;
while ( hasNext ) {
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 . 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);
}
}
}
// iterate to next road
if ( nextIterator = = null ) {
next = next . next ;
hasNext = next ! = null ;
} else {
hasNext = nextIterator . hasNext ( ) ;
}
}
}
/*public */ static int roadPriorityComparator ( double o1DistanceFromStart , double o1DistanceToEnd ,
double o2DistanceFromStart , double o2DistanceToEnd , double heuristicCoefficient ) {
// f(x) = g(x) + h(x) --- g(x) - distanceFromStart, h(x) - distanceToEnd (not exact)
return Double . compare ( o1DistanceFromStart + heuristicCoefficient * o1DistanceToEnd ,
o2DistanceFromStart + heuristicCoefficient * o2DistanceToEnd ) ;
}
public interface RouteSegmentVisitor {
public void visitSegment ( RouteSegment segment , int segmentEnd , boolean poll ) ;
}
public static class RouteSegment {
final short segStart ;
final RouteDataObject road ;
// needed to store intersection of routes
RouteSegment next = null ;
// search context (needed for searching route)
// Initially it should be null (!) because it checks was it segment visited before
RouteSegment parentRoute = null ;
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 ;
float distanceToEnd = 0 ;
public RouteSegment ( RouteDataObject road , int segmentStart ) {
this . road = road ;
this . segStart = ( short ) segmentStart ;
}
public byte getDirectionAssigned ( ) {
return directionAssgn ;
}
public RouteSegment getParentRoute ( ) {
return parentRoute ;
}
public void setParentRoute ( RouteSegment parentRoute ) {
this . parentRoute = parentRoute ;
}
public void assignDirection ( byte b ) {
directionAssgn = b ;
}
public byte getAllowedDirection ( ) {
return allowedDirection ;
}
public void setAllowedDirection ( byte allowedDirection ) {
this . allowedDirection = allowedDirection ;
}
public void setParentSegmentEnd ( int parentSegmentEnd ) {
this . parentSegmentEnd = ( short ) parentSegmentEnd ;
}
public int getParentSegmentEnd ( ) {
return parentSegmentEnd ;
}
public RouteSegment getNext ( ) {
return next ;
}
public int getSegmentStart ( ) {
return segStart ;
}
public RouteDataObject getRoad ( ) {
return road ;
}
public String getTestName ( ) {
return MessageFormat . format ( " s{0,number,#.##} e{1,number,#.##} " , ( ( float ) distanceFromStart ) , ( ( float ) distanceToEnd ) ) ;
}
public Iterator < RouteSegment > getIterator ( ) {
return new Iterator < BinaryRoutePlanner . RouteSegment > ( ) {
RouteSegment next = RouteSegment . this ;
@Override
public void remove ( ) {
throw new UnsupportedOperationException ( ) ;
}
@Override
public RouteSegment next ( ) {
RouteSegment c = next ;
if ( next ! = null ) {
next = next . next ;
}
return c ;
}
@Override
public boolean hasNext ( ) {
return next ! = null ;
}
} ;
}
}
static class FinalRouteSegment extends RouteSegment {
boolean reverseWaySearch ;
RouteSegment opposite ;
public FinalRouteSegment ( RouteDataObject road , int segmentStart ) {
super ( road , segmentStart ) ;
}
}
}