2013-04-18 23:35:02 +02:00
package net.osmand.router ;
2014-11-23 19:45:31 +01:00
import gnu.trove.list.array.TIntArrayList ;
import gnu.trove.set.hash.TIntHashSet ;
2013-04-18 23:35:02 +02:00
import java.io.IOException ;
import java.text.MessageFormat ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collections ;
import java.util.Iterator ;
import java.util.List ;
2014-09-24 22:32:37 +02:00
import net.osmand.PlatformUtil ;
2016-04-26 22:59:47 +02:00
import net.osmand.binary.BinaryInspector ;
2013-04-18 23:35:02 +02:00
import net.osmand.binary.BinaryMapIndexReader ;
2014-03-01 12:30:25 +01:00
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule ;
2013-04-18 23:35:02 +02:00
import net.osmand.binary.RouteDataObject ;
import net.osmand.data.LatLon ;
import net.osmand.router.BinaryRoutePlanner.FinalRouteSegment ;
import net.osmand.router.BinaryRoutePlanner.RouteSegment ;
2014-01-19 23:58:17 +01:00
import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode ;
2016-04-27 01:05:42 +02:00
import net.osmand.util.Algorithms ;
2013-04-18 23:35:02 +02:00
import net.osmand.util.MapUtils ;
2014-11-23 19:45:31 +01:00
import org.apache.commons.logging.Log ;
2013-04-18 23:35:02 +02:00
public class RouteResultPreparation {
2014-02-01 02:06:36 +01:00
public static boolean PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = false ;
2015-09-13 10:58:27 +02:00
private static final float TURN_DEGREE_MIN = 45 ;
2014-09-24 22:32:37 +02:00
private Log log = PlatformUtil . getLog ( RouteResultPreparation . class ) ;
2013-04-18 23:35:02 +02:00
/ * *
* Helper method to prepare final result
* /
2014-01-19 21:29:11 +01:00
List < RouteSegmentResult > prepareResult ( RoutingContext ctx , FinalRouteSegment finalSegment ) throws IOException {
2013-04-18 23:35:02 +02:00
List < RouteSegmentResult > result = convertFinalSegmentToResults ( ctx , finalSegment ) ;
2014-01-19 21:29:11 +01:00
prepareResult ( ctx , result ) ;
2013-04-18 23:35:02 +02:00
return result ;
}
2014-01-19 21:29:11 +01:00
List < RouteSegmentResult > prepareResult ( RoutingContext ctx , List < RouteSegmentResult > result ) throws IOException {
2013-04-18 23:35:02 +02:00
validateAllPointsConnected ( result ) ;
splitRoadsAndAttachRoadSegments ( ctx , result ) ;
calculateTimeSpeed ( ctx , result ) ;
2015-01-13 10:25:56 +01:00
2015-01-15 02:42:33 +01:00
for ( int i = 0 ; i < result . size ( ) ; i + + ) {
TurnType turnType = getTurnInfo ( result , i , ctx . leftSideNavigation ) ;
result . get ( i ) . setTurnType ( turnType ) ;
}
2015-01-13 10:25:56 +01:00
determineTurnsToMerge ( ctx . leftSideNavigation , result ) ;
2015-01-15 02:42:33 +01:00
justifyUTurns ( ctx . leftSideNavigation , result ) ;
2015-01-13 10:25:56 +01:00
addTurnInfoDescriptions ( result ) ;
2013-04-18 23:35:02 +02:00
return result ;
}
2015-01-15 02:42:33 +01:00
private void justifyUTurns ( boolean leftSide , List < RouteSegmentResult > result ) {
int next ;
for ( int i = 0 ; i < result . size ( ) - 1 ; i = next ) {
next = i + 1 ;
TurnType t = result . get ( i ) . getTurnType ( ) ;
// justify turn
if ( t ! = null ) {
TurnType jt = justifyUTurn ( leftSide , result , i , t ) ;
if ( jt ! = null ) {
result . get ( i ) . setTurnType ( jt ) ;
next = i + 2 ;
}
}
}
}
2013-04-18 23:35:02 +02:00
private void calculateTimeSpeed ( RoutingContext ctx , List < RouteSegmentResult > result ) throws IOException {
for ( int i = 0 ; i < result . size ( ) ; i + + ) {
RouteSegmentResult rr = result . get ( i ) ;
RouteDataObject road = rr . getObject ( ) ;
double distOnRoadToPass = 0 ;
2014-02-02 14:06:09 +01:00
double speed = ctx . getRouter ( ) . defineVehicleSpeed ( road ) ;
2013-04-18 23:35:02 +02:00
if ( speed = = 0 ) {
speed = ctx . getRouter ( ) . getMinDefaultSpeed ( ) ;
2013-11-06 23:04:55 +01:00
} else {
if ( speed > 15 ) {
// decrease speed proportionally from 15ms=50kmh -
// reference speed 30ms=108kmh - 7kmh
speed = speed - ( ( speed - 15f ) / ( 30f - 15f ) * 2f ) ;
}
2013-04-18 23:35:02 +02:00
}
boolean plus = rr . getStartPointIndex ( ) < rr . getEndPointIndex ( ) ;
int next ;
double distance = 0 ;
for ( int j = rr . getStartPointIndex ( ) ; j ! = rr . getEndPointIndex ( ) ; j = next ) {
next = plus ? j + 1 : j - 1 ;
double d = measuredDist ( road . getPoint31XTile ( j ) , road . getPoint31YTile ( j ) , road . getPoint31XTile ( next ) ,
road . getPoint31YTile ( next ) ) ;
distance + = d ;
double obstacle = ctx . getRouter ( ) . defineObstacle ( road , j ) ;
if ( obstacle < 0 ) {
obstacle = 0 ;
}
distOnRoadToPass + = d / speed + obstacle ;
}
// last point turn time can be added
// if(i + 1 < result.size()) { distOnRoadToPass += ctx.getRouter().calculateTurnTime(); }
rr . setSegmentTime ( ( float ) distOnRoadToPass ) ;
rr . setSegmentSpeed ( ( float ) speed ) ;
rr . setDistance ( ( float ) distance ) ;
}
}
private void splitRoadsAndAttachRoadSegments ( RoutingContext ctx , List < RouteSegmentResult > result ) throws IOException {
for ( int i = 0 ; i < result . size ( ) ; i + + ) {
if ( ctx . checkIfMemoryLimitCritical ( ctx . config . memoryLimitation ) ) {
ctx . unloadUnusedTiles ( ctx . config . memoryLimitation ) ;
}
RouteSegmentResult rr = result . get ( i ) ;
RouteDataObject road = rr . getObject ( ) ;
checkAndInitRouteRegion ( ctx , road ) ;
boolean plus = rr . getStartPointIndex ( ) < rr . getEndPointIndex ( ) ;
int next ;
for ( int j = rr . getStartPointIndex ( ) ; j ! = rr . getEndPointIndex ( ) ; j = next ) {
next = plus ? j + 1 : j - 1 ;
if ( j = = rr . getStartPointIndex ( ) ) {
attachRoadSegments ( ctx , result , i , j , plus ) ;
}
if ( next ! = rr . getEndPointIndex ( ) ) {
attachRoadSegments ( ctx , result , i , next , plus ) ;
}
List < RouteSegmentResult > attachedRoutes = rr . getAttachedRoutes ( next ) ;
2013-11-26 16:23:05 +01:00
boolean tryToSplit = next ! = rr . getEndPointIndex ( ) & & ! rr . getObject ( ) . roundabout ( ) & & attachedRoutes ! = null ;
if ( rr . getDistance ( next , plus ) = = 0 ) {
// same point will be processed next step
tryToSplit = false ;
}
if ( tryToSplit ) {
// avoid small zigzags
2013-04-18 23:35:02 +02:00
float before = rr . getBearing ( next , ! plus ) ;
float after = rr . getBearing ( next , plus ) ;
2013-11-26 16:23:05 +01:00
if ( rr . getDistance ( next , plus ) < 5 ) {
after = before + 180 ;
} else if ( rr . getDistance ( next , ! plus ) < 5 ) {
before = after - 180 ;
}
2013-04-18 23:35:02 +02:00
boolean straight = Math . abs ( MapUtils . degreesDiff ( before + 180 , after ) ) < TURN_DEGREE_MIN ;
boolean isSplit = false ;
// split if needed
for ( RouteSegmentResult rs : attachedRoutes ) {
double diff = MapUtils . degreesDiff ( before + 180 , rs . getBearingBegin ( ) ) ;
if ( Math . abs ( diff ) < = TURN_DEGREE_MIN ) {
isSplit = true ;
} else if ( ! straight & & Math . abs ( diff ) < 100 ) {
isSplit = true ;
}
}
if ( isSplit ) {
int endPointIndex = rr . getEndPointIndex ( ) ;
RouteSegmentResult split = new RouteSegmentResult ( rr . getObject ( ) , next , endPointIndex ) ;
split . copyPreattachedRoutes ( rr , Math . abs ( next - rr . getStartPointIndex ( ) ) ) ;
rr . setEndPointIndex ( next ) ;
result . add ( i + 1 , split ) ;
i + + ;
// switch current segment to the splitted
rr = split ;
}
}
}
}
}
private void checkAndInitRouteRegion ( RoutingContext ctx , RouteDataObject road ) throws IOException {
BinaryMapIndexReader reader = ctx . reverseMap . get ( road . region ) ;
if ( reader ! = null ) {
reader . initRouteRegion ( road . region ) ;
}
}
private void validateAllPointsConnected ( List < RouteSegmentResult > result ) {
for ( int i = 1 ; i < result . size ( ) ; i + + ) {
RouteSegmentResult rr = result . get ( i ) ;
RouteSegmentResult pr = result . get ( i - 1 ) ;
double d = MapUtils . getDistance ( pr . getPoint ( pr . getEndPointIndex ( ) ) , rr . getPoint ( rr . getStartPointIndex ( ) ) ) ;
if ( d > 0 ) {
System . err . println ( " Points are not connected : " + pr . getObject ( ) + " ( " + pr . getEndPointIndex ( ) + " ) -> " + rr . getObject ( )
+ " ( " + rr . getStartPointIndex ( ) + " ) " + d + " meters " ) ;
}
}
}
private List < RouteSegmentResult > convertFinalSegmentToResults ( RoutingContext ctx , FinalRouteSegment finalSegment ) {
List < RouteSegmentResult > result = new ArrayList < RouteSegmentResult > ( ) ;
if ( finalSegment ! = null ) {
ctx . routingTime = finalSegment . distanceFromStart ;
println ( " Routing calculated time distance " + finalSegment . distanceFromStart ) ;
// Get results from opposite direction roads
2014-01-31 01:43:52 +01:00
RouteSegment segment = finalSegment . reverseWaySearch ? finalSegment :
finalSegment . opposite . getParentRoute ( ) ;
int parentSegmentStart = finalSegment . reverseWaySearch ? finalSegment . opposite . getSegmentStart ( ) :
finalSegment . opposite . getParentSegmentEnd ( ) ;
2014-01-28 21:03:25 +01:00
float parentRoutingTime = - 1 ;
2013-04-18 23:35:02 +02:00
while ( segment ! = null ) {
RouteSegmentResult res = new RouteSegmentResult ( segment . road , parentSegmentStart , segment . getSegmentStart ( ) ) ;
2014-01-28 21:03:25 +01:00
parentRoutingTime = calcRoutingTime ( parentRoutingTime , finalSegment , segment , res ) ;
2013-04-18 23:35:02 +02:00
parentSegmentStart = segment . getParentSegmentEnd ( ) ;
segment = segment . getParentRoute ( ) ;
2014-01-19 23:58:17 +01:00
addRouteSegmentToResult ( ctx , result , res , false ) ;
2013-04-18 23:35:02 +02:00
}
// reverse it just to attach good direction roads
Collections . reverse ( result ) ;
segment = finalSegment . reverseWaySearch ? finalSegment . opposite . getParentRoute ( ) : finalSegment ;
int parentSegmentEnd = finalSegment . reverseWaySearch ? finalSegment . opposite . getParentSegmentEnd ( ) : finalSegment . opposite . getSegmentStart ( ) ;
2014-01-28 21:03:25 +01:00
parentRoutingTime = - 1 ;
2013-04-18 23:35:02 +02:00
while ( segment ! = null ) {
RouteSegmentResult res = new RouteSegmentResult ( segment . road , segment . getSegmentStart ( ) , parentSegmentEnd ) ;
2014-01-28 21:03:25 +01:00
parentRoutingTime = calcRoutingTime ( parentRoutingTime , finalSegment , segment , res ) ;
2013-04-18 23:35:02 +02:00
parentSegmentEnd = segment . getParentSegmentEnd ( ) ;
segment = segment . getParentRoute ( ) ;
// happens in smart recalculation
2014-01-19 23:58:17 +01:00
addRouteSegmentToResult ( ctx , result , res , true ) ;
2013-04-18 23:35:02 +02:00
}
Collections . reverse ( result ) ;
2014-01-28 21:03:25 +01:00
// checkTotalRoutingTime(result);
2013-04-18 23:35:02 +02:00
}
return result ;
}
2014-01-28 21:03:25 +01:00
protected void checkTotalRoutingTime ( List < RouteSegmentResult > result ) {
float totalRoutingTime = 0 ;
for ( RouteSegmentResult r : result ) {
totalRoutingTime + = r . getRoutingTime ( ) ;
}
println ( " Total routing time ! " + totalRoutingTime ) ;
}
private float calcRoutingTime ( float parentRoutingTime , RouteSegment finalSegment , RouteSegment segment ,
RouteSegmentResult res ) {
if ( segment ! = finalSegment ) {
if ( parentRoutingTime ! = - 1 ) {
res . setRoutingTime ( parentRoutingTime - segment . distanceFromStart ) ;
}
parentRoutingTime = segment . distanceFromStart ;
}
return parentRoutingTime ;
}
2013-04-18 23:35:02 +02:00
2014-01-19 23:58:17 +01:00
private void addRouteSegmentToResult ( RoutingContext ctx , List < RouteSegmentResult > result , RouteSegmentResult res , boolean reverse ) {
2013-04-18 23:35:02 +02:00
if ( res . getStartPointIndex ( ) ! = res . getEndPointIndex ( ) ) {
if ( result . size ( ) > 0 ) {
RouteSegmentResult last = result . get ( result . size ( ) - 1 ) ;
2014-01-19 23:58:17 +01:00
if ( last . getObject ( ) . id = = res . getObject ( ) . id & & ctx . calculationMode ! = RouteCalculationMode . BASE ) {
2013-04-18 23:35:02 +02:00
if ( combineTwoSegmentResult ( res , last , reverse ) ) {
return ;
}
}
}
result . add ( res ) ;
}
}
private boolean combineTwoSegmentResult ( RouteSegmentResult toAdd , RouteSegmentResult previous ,
boolean reverse ) {
boolean ld = previous . getEndPointIndex ( ) > previous . getStartPointIndex ( ) ;
boolean rd = toAdd . getEndPointIndex ( ) > toAdd . getStartPointIndex ( ) ;
if ( rd = = ld ) {
if ( toAdd . getStartPointIndex ( ) = = previous . getEndPointIndex ( ) & & ! reverse ) {
previous . setEndPointIndex ( toAdd . getEndPointIndex ( ) ) ;
2014-01-28 21:03:25 +01:00
previous . setRoutingTime ( previous . getRoutingTime ( ) + toAdd . getRoutingTime ( ) ) ;
2013-04-18 23:35:02 +02:00
return true ;
} else if ( toAdd . getEndPointIndex ( ) = = previous . getStartPointIndex ( ) & & reverse ) {
previous . setStartPointIndex ( toAdd . getStartPointIndex ( ) ) ;
2014-01-28 21:03:25 +01:00
previous . setRoutingTime ( previous . getRoutingTime ( ) + toAdd . getRoutingTime ( ) ) ;
2013-04-18 23:35:02 +02:00
return true ;
}
}
return false ;
}
void printResults ( RoutingContext ctx , LatLon start , LatLon end , List < RouteSegmentResult > result ) {
float completeTime = 0 ;
float completeDistance = 0 ;
for ( RouteSegmentResult r : result ) {
completeTime + = r . getSegmentTime ( ) ;
completeDistance + = r . getDistance ( ) ;
}
println ( " ROUTE : " ) ;
double startLat = start . getLatitude ( ) ;
double startLon = start . getLongitude ( ) ;
double endLat = end . getLatitude ( ) ;
double endLon = end . getLongitude ( ) ;
2014-09-24 22:32:37 +02:00
String msg = MessageFormat . format ( " <test regions= \" \" description= \" \" best_percent= \" \" vehicle= \" {4} \" \ n "
2014-01-09 01:33:46 +01:00
+ " start_lat= \" {0} \" start_lon= \" {1} \" target_lat= \" {2} \" target_lon= \" {3} \" {5} > " ,
startLat + " " , startLon + " " , endLat + " " , endLon + " " , ctx . config . routerName ,
" loadedTiles = \" " + ctx . loadedTiles + " \" " + " visitedSegments = \" " + ctx . visitedSegments + " \" " +
" complete_distance = \" " + completeDistance + " \" " + " complete_time = \" " + completeTime + " \" " +
2014-09-24 22:32:37 +02:00
" routing_time = \" " + ctx . routingTime + " \" " ) ;
log . info ( msg ) ;
println ( msg ) ;
2013-04-18 23:35:02 +02:00
if ( PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST ) {
for ( RouteSegmentResult res : result ) {
String name = res . getObject ( ) . getName ( ) ;
String ref = res . getObject ( ) . getRef ( ) ;
if ( name = = null ) {
name = " " ;
}
if ( ref ! = null ) {
name + = " ( " + ref + " ) " ;
}
StringBuilder additional = new StringBuilder ( ) ;
additional . append ( " time = \" " ) . append ( res . getSegmentTime ( ) ) . append ( " \" " ) ;
2014-01-28 21:03:25 +01:00
additional . append ( " rtime = \" " ) . append ( res . getRoutingTime ( ) ) . append ( " \" " ) ;
2013-04-18 23:35:02 +02:00
additional . append ( " name = \" " ) . append ( name ) . append ( " \" " ) ;
// float ms = res.getSegmentSpeed();
2015-08-31 00:11:48 +02:00
float ms = res . getObject ( ) . getMaximumSpeed ( res . isForwardDirection ( ) ) ;
2013-04-18 23:35:02 +02:00
if ( ms > 0 ) {
additional . append ( " maxspeed = \" " ) . append ( ms * 3 . 6f ) . append ( " \" " ) . append ( res . getObject ( ) . getHighway ( ) ) . append ( " " ) ;
}
additional . append ( " distance = \" " ) . append ( res . getDistance ( ) ) . append ( " \" " ) ;
if ( res . getTurnType ( ) ! = null ) {
additional . append ( " turn = \" " ) . append ( res . getTurnType ( ) ) . append ( " \" " ) ;
additional . append ( " turn_angle = \" " ) . append ( res . getTurnType ( ) . getTurnAngle ( ) ) . append ( " \" " ) ;
if ( res . getTurnType ( ) . getLanes ( ) ! = null ) {
additional . append ( " lanes = \" " ) . append ( Arrays . toString ( res . getTurnType ( ) . getLanes ( ) ) ) . append ( " \" " ) ;
}
}
additional . append ( " start_bearing = \" " ) . append ( res . getBearingBegin ( ) ) . append ( " \" " ) ;
additional . append ( " end_bearing = \" " ) . append ( res . getBearingEnd ( ) ) . append ( " \" " ) ;
additional . append ( " description = \" " ) . append ( res . getDescription ( ) ) . append ( " \" " ) ;
2016-04-26 22:59:47 +02:00
println ( MessageFormat . format ( " \ t<segment id= \" {0} \" oid= \" {1} \" start= \" {2} \" end= \" {3} \" {4}/> " ,
( res . getObject ( ) . getId ( ) > > ( BinaryInspector . SHIFT_ID ) ) + " " , res . getObject ( ) . getId ( ) + " " ,
2013-04-18 23:35:02 +02:00
res . getStartPointIndex ( ) + " " , res . getEndPointIndex ( ) + " " , additional . toString ( ) ) ) ;
2014-03-01 12:30:25 +01:00
printAdditionalPointInfo ( res ) ;
2013-04-18 23:35:02 +02:00
}
}
println ( " </test> " ) ;
}
2014-03-01 12:30:25 +01:00
private void printAdditionalPointInfo ( RouteSegmentResult res ) {
boolean plus = res . getStartPointIndex ( ) < res . getEndPointIndex ( ) ;
for ( int k = res . getStartPointIndex ( ) ; k ! = res . getEndPointIndex ( ) ; ) {
int [ ] tp = res . getObject ( ) . getPointTypes ( k ) ;
2015-07-06 07:08:10 +02:00
String [ ] pointNames = res . getObject ( ) . getPointNames ( k ) ;
int [ ] pointNameTypes = res . getObject ( ) . getPointNameTypes ( k ) ;
if ( tp ! = null | | pointNameTypes ! = null ) {
StringBuilder bld = new StringBuilder ( ) ;
bld . append ( " <point " ) ;
if ( tp ! = null ) {
for ( int t = 0 ; t < tp . length ; t + + ) {
RouteTypeRule rr = res . getObject ( ) . region . quickGetEncodingRule ( tp [ t ] ) ;
bld . append ( " " + rr . getTag ( ) + " = \" " + rr . getValue ( ) + " \" " ) ;
}
}
if ( pointNameTypes ! = null ) {
for ( int t = 0 ; t < pointNameTypes . length ; t + + ) {
RouteTypeRule rr = res . getObject ( ) . region . quickGetEncodingRule ( pointNameTypes [ t ] ) ;
bld . append ( " " + rr . getTag ( ) + " = \" " + pointNames [ t ] + " \" " ) ;
}
2014-03-01 12:30:25 +01:00
}
2015-07-06 07:08:10 +02:00
bld . append ( " /> " ) ;
println ( " \ t " + bld . toString ( ) ) ;
2014-03-01 12:30:25 +01:00
}
if ( plus ) {
k + + ;
} else {
k - - ;
}
}
}
2013-04-18 23:35:02 +02:00
2015-01-13 10:25:56 +01:00
protected void addTurnInfoDescriptions ( List < RouteSegmentResult > result ) {
int prevSegment = - 1 ;
float dist = 0 ;
for ( int i = 0 ; i < = result . size ( ) ; i + + ) {
if ( i = = result . size ( ) | | result . get ( i ) . getTurnType ( ) ! = null ) {
2013-04-18 23:35:02 +02:00
if ( prevSegment > = 0 ) {
String turn = result . get ( prevSegment ) . getTurnType ( ) . toString ( ) ;
2015-01-15 02:42:33 +01:00
final int [ ] lns = result . get ( prevSegment ) . getTurnType ( ) . getLanes ( ) ;
if ( lns ! = null ) {
String s = " [ " ;
for ( int h = 0 ; h < lns . length ; h + + ) {
if ( h > 0 ) {
2016-04-09 14:11:09 +02:00
s + = " | " ;
2015-01-15 02:42:33 +01:00
}
if ( lns [ h ] % 2 = = 1 ) {
s + = " + " ;
}
int pt = TurnType . getPrimaryTurn ( lns [ h ] ) ;
if ( pt = = 0 ) {
pt = 1 ;
}
s + = TurnType . valueOf ( pt , false ) . toXmlString ( ) ;
int st = TurnType . getSecondaryTurn ( lns [ h ] ) ;
if ( st ! = 0 ) {
2016-04-09 14:11:09 +02:00
s + = " , " + TurnType . valueOf ( st , false ) . toXmlString ( ) ;
2015-01-15 02:42:33 +01:00
}
2016-03-30 14:45:24 +02:00
int tt = TurnType . getTertiaryTurn ( lns [ h ] ) ;
if ( tt ! = 0 ) {
2016-04-09 14:11:09 +02:00
s + = " , " + TurnType . valueOf ( tt , false ) . toXmlString ( ) ;
2016-03-30 14:45:24 +02:00
}
2015-01-15 02:42:33 +01:00
}
s + = " ] " ;
turn + = s ;
2013-04-18 23:35:02 +02:00
}
2015-01-13 10:25:56 +01:00
result . get ( prevSegment ) . setDescription (
turn + MessageFormat . format ( " and go {0,number,#.##} meters " , dist ) ) ;
if ( result . get ( prevSegment ) . getTurnType ( ) . isSkipToSpeak ( ) ) {
result . get ( prevSegment ) . setDescription ( " -* " + result . get ( prevSegment ) . getDescription ( ) ) ;
2013-04-18 23:35:02 +02:00
}
}
prevSegment = i ;
dist = 0 ;
}
if ( i < result . size ( ) ) {
dist + = result . get ( i ) . getDistance ( ) ;
}
}
2015-01-13 10:25:56 +01:00
}
2014-11-16 16:04:54 +01:00
2015-01-13 10:25:56 +01:00
protected TurnType justifyUTurn ( boolean leftside , List < RouteSegmentResult > result , int i , TurnType t ) {
2016-04-09 14:11:09 +02:00
boolean tl = TurnType . isLeftTurnNoUTurn ( t . getValue ( ) ) ;
boolean tr = TurnType . isRightTurnNoUTurn ( t . getValue ( ) ) ;
2015-01-13 10:25:56 +01:00
if ( tl | | tr ) {
2015-01-15 02:42:33 +01:00
TurnType tnext = result . get ( i + 1 ) . getTurnType ( ) ;
2016-04-09 14:11:09 +02:00
if ( tnext ! = null & & result . get ( i ) . getDistance ( ) < 50 ) { //
2015-01-13 10:25:56 +01:00
boolean ut = true ;
if ( i > 0 ) {
double uTurn = MapUtils . degreesDiff ( result . get ( i - 1 ) . getBearingEnd ( ) , result
. get ( i + 1 ) . getBearingBegin ( ) ) ;
if ( Math . abs ( uTurn ) < 120 ) {
ut = false ;
}
}
String highway = result . get ( i ) . getObject ( ) . getHighway ( ) ;
if ( highway = = null | | highway . endsWith ( " track " ) | | highway . endsWith ( " services " ) | | highway . endsWith ( " service " )
| | highway . endsWith ( " path " ) ) {
ut = false ;
}
2016-04-09 14:11:09 +02:00
if ( result . get ( i ) . getObject ( ) . getOneway ( ) = = 0 | | result . get ( i + 1 ) . getObject ( ) . getOneway ( ) = = 0 ) {
ut = false ;
}
2016-04-27 01:05:42 +02:00
if ( ! Algorithms . objectEquals ( result . get ( i ) . getObject ( ) . getName ( ) ,
result . get ( i + 1 ) . getObject ( ) . getName ( ) ) ) {
ut = false ;
}
2015-01-13 10:25:56 +01:00
if ( ut ) {
2015-01-15 02:42:33 +01:00
tnext . setSkipToSpeak ( true ) ;
2016-04-12 22:20:01 +02:00
if ( tl & & TurnType . isLeftTurnNoUTurn ( tnext . getValue ( ) ) ) {
2016-04-09 14:11:09 +02:00
TurnType tt = TurnType . valueOf ( TurnType . TU , false ) ;
tt . setLanes ( t . getLanes ( ) ) ;
return tt ;
2016-04-12 22:20:01 +02:00
} else if ( tr & & TurnType . isRightTurnNoUTurn ( tnext . getValue ( ) ) ) {
2016-04-09 14:11:09 +02:00
TurnType tt = TurnType . valueOf ( TurnType . TU , true ) ;
tt . setLanes ( t . getLanes ( ) ) ;
return tt ;
2015-01-13 10:25:56 +01:00
}
}
}
}
return null ;
2014-11-23 19:28:21 +01:00
}
private void determineTurnsToMerge ( boolean leftside , List < RouteSegmentResult > result ) {
2015-01-15 02:42:33 +01:00
RouteSegmentResult nextSegment = null ;
double dist = 0 ;
for ( int i = result . size ( ) - 1 ; i > = 0 ; i - - ) {
2014-11-16 16:04:54 +01:00
RouteSegmentResult currentSegment = result . get ( i ) ;
2014-11-28 15:39:18 +01:00
TurnType currentTurn = currentSegment . getTurnType ( ) ;
2015-01-15 02:42:33 +01:00
dist + = currentSegment . getDistance ( ) ;
if ( currentTurn = = null | | currentTurn . getLanes ( ) = = null ) {
// skip
} else {
if ( nextSegment ! = null ) {
String hw = currentSegment . getObject ( ) . getHighway ( ) ;
double mergeDistance = 200 ;
if ( hw ! = null & & ( hw . startsWith ( " trunk " ) | | hw . startsWith ( " motorway " ) ) ) {
mergeDistance = 400 ;
}
if ( dist < mergeDistance ) {
mergeTurnLanes ( leftside , currentSegment , nextSegment ) ;
}
}
nextSegment = currentSegment ;
dist = 0 ;
2014-11-16 16:04:54 +01:00
}
}
}
2015-01-13 10:25:56 +01:00
2015-01-15 02:42:33 +01:00
private class MergeTurnLaneTurn {
TurnType turn ;
int [ ] originalLanes ;
int [ ] disabledLanes ;
int activeStartIndex = - 1 ;
int activeEndIndex = - 1 ;
int activeLen = 0 ;
public MergeTurnLaneTurn ( RouteSegmentResult segment ) {
this . turn = segment . getTurnType ( ) ;
if ( turn ! = null ) {
originalLanes = turn . getLanes ( ) ;
2015-01-13 10:25:56 +01:00
}
2015-01-15 02:42:33 +01:00
if ( originalLanes ! = null ) {
disabledLanes = new int [ originalLanes . length ] ;
for ( int i = 0 ; i < originalLanes . length ; i + + ) {
int ln = originalLanes [ i ] ;
disabledLanes [ i ] = ln & ~ 1 ;
if ( ( ln & 1 ) > 0 ) {
if ( activeStartIndex = = - 1 ) {
activeStartIndex = i ;
}
activeEndIndex = i ;
activeLen + + ;
2014-11-16 16:04:54 +01:00
}
}
2015-01-13 10:25:56 +01:00
}
2015-01-15 02:42:33 +01:00
}
public boolean isActiveTurnMostLeft ( ) {
return activeStartIndex = = 0 ;
}
public boolean isActiveTurnMostRight ( ) {
return activeEndIndex = = originalLanes . length - 1 ;
}
}
private void mergeTurnLanes ( boolean leftSide , RouteSegmentResult currentSegment , RouteSegmentResult nextSegment ) {
MergeTurnLaneTurn active = new MergeTurnLaneTurn ( currentSegment ) ;
MergeTurnLaneTurn target = new MergeTurnLaneTurn ( nextSegment ) ;
if ( active . activeLen < 2 ) {
return ;
}
if ( target . activeStartIndex = = - 1 ) {
return ;
}
boolean changed = false ;
if ( target . isActiveTurnMostLeft ( ) ) {
// let only the most left lanes be enabled
if ( target . activeLen < = active . activeLen ) {
active . activeEndIndex - = ( active . activeLen - target . activeLen ) ;
changed = true ;
2014-11-16 16:04:54 +01:00
}
2015-01-15 02:42:33 +01:00
} else if ( target . isActiveTurnMostRight ( ) ) {
// next turn is right
// let only the most right lanes be enabled
if ( target . activeLen < active . activeLen ) {
active . activeStartIndex + = ( active . activeLen - target . activeLen ) ;
changed = true ;
2014-11-16 16:04:54 +01:00
}
2015-01-15 02:42:33 +01:00
} else {
// next turn is get through (take out the left and the right turn)
2015-01-15 03:17:26 +01:00
if ( target . activeLen < active . activeLen ) {
2016-04-13 00:47:05 +02:00
if ( target . originalLanes . length = = active . activeLen ) {
active . activeEndIndex = active . activeStartIndex + target . activeEndIndex ;
active . activeStartIndex = active . activeStartIndex + target . activeStartIndex ;
changed = true ;
} else {
float ratio = ( active . activeLen - target . activeLen ) / 2f ;
active . activeEndIndex = ( int ) Math . ceil ( active . activeEndIndex - ratio ) ;
active . activeStartIndex = ( int ) Math . floor ( active . activeStartIndex + ratio ) ;
changed = true ;
}
2014-11-16 16:04:54 +01:00
}
2015-01-15 02:42:33 +01:00
}
if ( ! changed ) {
return ;
}
// set the allowed lane bit
for ( int i = 0 ; i < active . disabledLanes . length ; i + + ) {
2016-04-13 00:47:05 +02:00
if ( i > = active . activeStartIndex & & i < = active . activeEndIndex & &
active . originalLanes [ i ] % 2 = = 1 ) {
2015-01-15 02:42:33 +01:00
active . disabledLanes [ i ] | = 1 ;
2014-11-25 02:24:47 +01:00
}
2014-11-16 16:04:54 +01:00
}
2015-01-15 02:42:33 +01:00
TurnType currentTurn = currentSegment . getTurnType ( ) ;
currentTurn . setLanes ( active . disabledLanes ) ;
2016-04-13 00:14:27 +02:00
// There is a test which fails that assumption and there is no contr-test how it could help here
// int turn = inferTurnFromLanes(active.disabledLanes);
// if (turn != 0 && turn != currentTurn.getValue()) {
// TurnType newTurnType = TurnType.valueOf(turn, leftSide);
// newTurnType.setLanes(active.disabledLanes);
// newTurnType.setSkipToSpeak(currentTurn.isSkipToSpeak());
// currentSegment.setTurnType(newTurnType);
// }
2013-04-18 23:35:02 +02:00
}
private static final int MAX_SPEAK_PRIORITY = 5 ;
private int highwaySpeakPriority ( String highway ) {
if ( highway = = null | | highway . endsWith ( " track " ) | | highway . endsWith ( " services " ) | | highway . endsWith ( " service " )
| | highway . endsWith ( " path " ) ) {
return MAX_SPEAK_PRIORITY ;
}
if ( highway . endsWith ( " _link " ) | | highway . endsWith ( " unclassified " ) | | highway . endsWith ( " road " )
| | highway . endsWith ( " living_street " ) | | highway . endsWith ( " residential " ) ) {
return 1 ;
}
return 0 ;
}
private TurnType getTurnInfo ( List < RouteSegmentResult > result , int i , boolean leftSide ) {
if ( i = = 0 ) {
return TurnType . valueOf ( TurnType . C , false ) ;
}
RouteSegmentResult prev = result . get ( i - 1 ) ;
if ( prev . getObject ( ) . roundabout ( ) ) {
// already analyzed!
return null ;
}
RouteSegmentResult rr = result . get ( i ) ;
if ( rr . getObject ( ) . roundabout ( ) ) {
return processRoundaboutTurn ( result , i , leftSide , prev , rr ) ;
}
TurnType t = null ;
if ( prev ! = null ) {
boolean noAttachedRoads = rr . getAttachedRoutes ( rr . getStartPointIndex ( ) ) . size ( ) = = 0 ;
// add description about turn
double mpi = MapUtils . degreesDiff ( prev . getBearingEnd ( ) , rr . getBearingBegin ( ) ) ;
if ( noAttachedRoads ) {
// TODO VICTOR : look at the comment inside direction route
2014-01-09 01:33:46 +01:00
// ? avoid small zigzags is covered at (search for "zigzags")
2013-04-18 23:35:02 +02:00
// double begin = rr.getObject().directionRoute(rr.getStartPointIndex(), rr.getStartPointIndex() <
// rr.getEndPointIndex(), 25);
// mpi = MapUtils.degreesDiff(prev.getBearingEnd(), begin);
}
if ( mpi > = TURN_DEGREE_MIN ) {
2015-09-11 19:43:31 +02:00
if ( mpi < 45 ) {
2015-09-13 10:58:27 +02:00
// Slight turn detection here causes many false positives where drivers would expect a "normal" TL. Best use limit-angle=TURN_DEGREE_MIN, this reduces TSL to the turn-lanes cases.
2013-04-18 23:35:02 +02:00
t = TurnType . valueOf ( TurnType . TSLL , leftSide ) ;
} else if ( mpi < 120 ) {
t = TurnType . valueOf ( TurnType . TL , leftSide ) ;
2015-09-11 19:43:31 +02:00
} else if ( mpi < 150 | | leftSide ) {
2013-04-18 23:35:02 +02:00
t = TurnType . valueOf ( TurnType . TSHL , leftSide ) ;
} else {
t = TurnType . valueOf ( TurnType . TU , leftSide ) ;
}
2015-01-15 02:42:33 +01:00
int [ ] lanes = getTurnLanesInfo ( prev , t . getValue ( ) ) ;
t . setLanes ( lanes ) ;
2013-04-18 23:35:02 +02:00
} else if ( mpi < - TURN_DEGREE_MIN ) {
2015-09-11 19:43:31 +02:00
if ( mpi > - 45 ) {
2013-04-18 23:35:02 +02:00
t = TurnType . valueOf ( TurnType . TSLR , leftSide ) ;
} else if ( mpi > - 120 ) {
t = TurnType . valueOf ( TurnType . TR , leftSide ) ;
2015-09-11 19:43:31 +02:00
} else if ( mpi > - 150 | | ! leftSide ) {
2013-04-18 23:35:02 +02:00
t = TurnType . valueOf ( TurnType . TSHR , leftSide ) ;
} else {
2015-01-15 02:42:33 +01:00
t = TurnType . valueOf ( TurnType . TRU , leftSide ) ;
2014-11-23 19:45:31 +01:00
}
2015-01-15 02:42:33 +01:00
int [ ] lanes = getTurnLanesInfo ( prev , t . getValue ( ) ) ;
t . setLanes ( lanes ) ;
2013-04-18 23:35:02 +02:00
} else {
2015-01-15 02:42:33 +01:00
t = attachKeepLeftInfoAndLanes ( leftSide , prev , rr ) ;
2013-04-18 23:35:02 +02:00
}
if ( t ! = null ) {
t . setTurnAngle ( ( float ) - mpi ) ;
}
}
return t ;
}
2015-01-15 02:42:33 +01:00
private int [ ] getTurnLanesInfo ( RouteSegmentResult prevSegm , int mainTurnType ) {
2014-10-13 15:32:35 +02:00
String turnLanes = getTurnLanesString ( prevSegm ) ;
2016-04-12 23:44:17 +02:00
int [ ] lanesArray ;
2014-10-13 15:32:35 +02:00
if ( turnLanes = = null ) {
2016-04-13 00:14:31 +02:00
if ( prevSegm . getTurnType ( ) ! = null & & prevSegm . getTurnType ( ) . getLanes ( ) ! = null
& & prevSegm . getDistance ( ) < 100 ) {
2016-04-12 23:44:17 +02:00
int [ ] lns = prevSegm . getTurnType ( ) . getLanes ( ) ;
TIntArrayList lst = new TIntArrayList ( ) ;
for ( int i = 0 ; i < lns . length ; i + + ) {
if ( lns [ i ] % 2 = = 1 ) {
lst . add ( ( lns [ i ] > > 1 ) < < 1 ) ;
}
}
if ( lst . isEmpty ( ) ) {
return null ;
}
lanesArray = lst . toArray ( ) ;
} else {
return null ;
}
} else {
lanesArray = calculateRawTurnLanes ( turnLanes , mainTurnType ) ;
2014-10-13 15:32:35 +02:00
}
2014-11-23 19:45:31 +01:00
// Manually set the allowed lanes.
boolean isSet = setAllowedLanes ( mainTurnType , lanesArray ) ;
if ( ! isSet & & lanesArray . length > 0 ) {
// In some cases (at least in the US), the rightmost lane might not have a right turn indicated as per turn:lanes,
// but is allowed and being used here. This section adds in that indicator. The same applies for where leftSide is true.
2015-01-15 02:42:33 +01:00
boolean leftTurn = TurnType . isLeftTurn ( mainTurnType ) ;
2014-11-23 19:45:31 +01:00
int ind = leftTurn ? 0 : lanesArray . length - 1 ;
final int tt = TurnType . getPrimaryTurn ( lanesArray [ ind ] ) ;
2016-03-28 15:51:52 +02:00
final int st = TurnType . getSecondaryTurn ( lanesArray [ ind ] ) ;
2014-11-23 19:45:31 +01:00
if ( leftTurn ) {
if ( ! TurnType . isLeftTurn ( tt ) ) {
2014-10-13 16:53:52 +02:00
// This was just to make sure that there's no bad data.
2015-01-15 02:42:33 +01:00
TurnType . setPrimaryTurnAndReset ( lanesArray , ind , TurnType . TL ) ;
2014-11-23 19:45:31 +01:00
TurnType . setSecondaryTurn ( lanesArray , ind , tt ) ;
2016-03-28 15:51:52 +02:00
TurnType . setTertiaryTurn ( lanesArray , ind , st ) ;
2015-05-27 04:56:31 +02:00
lanesArray [ ind ] | = 1 ;
2014-10-13 16:53:52 +02:00
}
2014-11-23 19:45:31 +01:00
} else {
if ( ! TurnType . isRightTurn ( tt ) ) {
2014-10-13 16:53:52 +02:00
// This was just to make sure that there's no bad data.
2015-01-15 02:42:33 +01:00
TurnType . setPrimaryTurnAndReset ( lanesArray , ind , TurnType . TR ) ;
2014-11-23 19:45:31 +01:00
TurnType . setSecondaryTurn ( lanesArray , ind , tt ) ;
2016-03-28 15:51:52 +02:00
TurnType . setTertiaryTurn ( lanesArray , ind , st ) ;
2015-05-27 04:56:31 +02:00
lanesArray [ ind ] | = 1 ;
2014-10-13 16:53:52 +02:00
}
}
2015-01-15 02:42:33 +01:00
setAllowedLanes ( tt , lanesArray ) ;
2014-10-13 16:53:52 +02:00
}
2014-11-23 19:45:31 +01:00
return lanesArray ;
}
2014-10-13 16:53:52 +02:00
2014-11-23 19:45:31 +01:00
protected boolean setAllowedLanes ( int mainTurnType , int [ ] lanesArray ) {
boolean turnSet = false ;
2014-10-13 15:32:35 +02:00
for ( int i = 0 ; i < lanesArray . length ; i + + ) {
2014-11-23 19:45:31 +01:00
if ( TurnType . getPrimaryTurn ( lanesArray [ i ] ) = = mainTurnType ) {
2014-10-13 15:32:35 +02:00
lanesArray [ i ] | = 1 ;
2014-11-23 19:45:31 +01:00
turnSet = true ;
2014-10-13 15:32:35 +02:00
}
}
2014-11-23 19:45:31 +01:00
return turnSet ;
2014-10-13 15:32:35 +02:00
}
2013-04-18 23:35:02 +02:00
private TurnType processRoundaboutTurn ( List < RouteSegmentResult > result , int i , boolean leftSide , RouteSegmentResult prev ,
RouteSegmentResult rr ) {
int exit = 1 ;
RouteSegmentResult last = rr ;
for ( int j = i ; j < result . size ( ) ; j + + ) {
RouteSegmentResult rnext = result . get ( j ) ;
last = rnext ;
if ( rnext . getObject ( ) . roundabout ( ) ) {
boolean plus = rnext . getStartPointIndex ( ) < rnext . getEndPointIndex ( ) ;
int k = rnext . getStartPointIndex ( ) ;
if ( j = = i ) {
// first exit could be immediately after roundabout enter
// k = plus ? k + 1 : k - 1;
}
while ( k ! = rnext . getEndPointIndex ( ) ) {
int attachedRoads = rnext . getAttachedRoutes ( k ) . size ( ) ;
if ( attachedRoads > 0 ) {
exit + + ;
}
k = plus ? k + 1 : k - 1 ;
}
} else {
break ;
}
}
// combine all roundabouts
2014-10-02 13:47:34 +02:00
TurnType t = TurnType . getExitTurn ( exit , 0 , leftSide ) ;
2013-04-18 23:35:02 +02:00
t . setTurnAngle ( ( float ) MapUtils . degreesDiff ( last . getBearingBegin ( ) , prev . getBearingEnd ( ) ) ) ;
return t ;
}
2015-01-15 02:42:33 +01:00
private class RoadSplitStructure {
boolean keepLeft = false ;
boolean keepRight = false ;
boolean speak = false ;
int leftLanes = 0 ;
int rightLanes = 0 ;
int roadsOnLeft = 0 ;
int addRoadsOnLeft = 0 ;
int roadsOnRight = 0 ;
int addRoadsOnRight = 0 ;
}
2013-04-18 23:35:02 +02:00
2015-01-15 02:42:33 +01:00
private TurnType attachKeepLeftInfoAndLanes ( boolean leftSide , RouteSegmentResult prevSegm , RouteSegmentResult currentSegm ) {
2013-04-18 23:35:02 +02:00
List < RouteSegmentResult > attachedRoutes = currentSegm . getAttachedRoutes ( currentSegm . getStartPointIndex ( ) ) ;
2015-01-15 02:42:33 +01:00
if ( attachedRoutes = = null | | attachedRoutes . size ( ) = = 0 ) {
return null ;
2013-04-18 23:35:02 +02:00
}
2015-01-15 02:42:33 +01:00
// keep left/right
RoadSplitStructure rs = calculateRoadSplitStructure ( prevSegm , currentSegm , attachedRoutes ) ;
if ( rs . roadsOnLeft + rs . roadsOnRight = = 0 ) {
return null ;
}
// turn lanes exist
String turnLanes = getTurnLanesString ( prevSegm ) ;
if ( turnLanes ! = null ) {
return createKeepLeftRightTurnBasedOnTurnTypes ( rs , prevSegm , currentSegm , turnLanes , leftSide ) ;
}
// turn lanes don't exist
if ( rs . keepLeft | | rs . keepRight ) {
return createSimpleKeepLeftRightTurn ( leftSide , prevSegm , currentSegm , rs ) ;
}
return null ;
}
protected TurnType createKeepLeftRightTurnBasedOnTurnTypes ( RoadSplitStructure rs , RouteSegmentResult prevSegm ,
2016-05-24 09:42:40 +02:00
RouteSegmentResult currentSegm , String turnLanes , boolean leftSide ) {
2015-01-15 02:42:33 +01:00
// Maybe going straight at a 90-degree intersection
TurnType t = TurnType . valueOf ( TurnType . C , leftSide ) ;
int [ ] rawLanes = calculateRawTurnLanes ( turnLanes , TurnType . C ) ;
if ( rs . keepLeft | | rs . keepRight ) {
String [ ] splitLaneOptions = turnLanes . split ( " \\ | " , - 1 ) ;
int activeBeginIndex = findActiveIndex ( rawLanes , splitLaneOptions , rs . leftLanes , true ,
rs . roadsOnLeft , rs . addRoadsOnLeft ) ;
int activeEndIndex = findActiveIndex ( rawLanes , splitLaneOptions , rs . rightLanes , false ,
rs . roadsOnRight , rs . addRoadsOnRight ) ;
if ( activeBeginIndex = = - 1 | | activeEndIndex = = - 1 | | activeBeginIndex > activeEndIndex ) {
// something went wrong
return createSimpleKeepLeftRightTurn ( leftSide , prevSegm , currentSegm , rs ) ;
}
for ( int k = 0 ; k < rawLanes . length ; k + + ) {
if ( k > = activeBeginIndex & & k < = activeEndIndex ) {
rawLanes [ k ] | = 1 ;
}
}
int tp = inferTurnFromLanes ( rawLanes ) ;
if ( tp ! = t . getValue ( ) & & tp ! = 0 ) {
t = TurnType . valueOf ( tp , leftSide ) ;
}
} else {
for ( int k = 0 ; k < rawLanes . length ; k + + ) {
2016-04-12 23:05:18 +02:00
int turn = TurnType . getPrimaryTurn ( rawLanes [ k ] ) ;
2015-01-15 02:42:33 +01:00
boolean active = false ;
2016-04-12 23:05:18 +02:00
if ( turn = = TurnType . C ) {
2015-01-15 02:42:33 +01:00
active = true ;
} else if ( TurnType . isRightTurn ( turn ) & & rs . roadsOnRight = = 0 ) {
// some turns go through many segments (to turn right or left)
// so on one first segment the lane could be available and may be only 1 possible
// all undesired lanes will be disabled through the 2nd pass
active = true ;
} else if ( TurnType . isLeftTurn ( turn ) & & rs . roadsOnLeft = = 0 ) {
active = true ;
}
if ( active ) {
rawLanes [ k ] | = 1 ;
}
}
}
2016-05-24 10:03:09 +02:00
//Test suppressing 'ghost turns' where lanes split with no action (go staight) or a subsequent turn will be announced anyway. This mostly causes Issue 2571.
if ( t = TurnType . valueOf ( TurnType . C , leftSide ) ) ;
rs . speak = false ;
}
2015-01-15 02:42:33 +01:00
t . setSkipToSpeak ( ! rs . speak ) ;
t . setLanes ( rawLanes ) ;
return t ;
}
protected int findActiveIndex ( int [ ] rawLanes , String [ ] splitLaneOptions , int lanes , boolean left ,
int roads , int addRoads ) {
int activeStartIndex = - 1 ;
boolean lookupSlightTurn = addRoads > 0 ;
for ( int i = 0 ; i < rawLanes . length ; i + + ) {
int ind = left ? i : ( rawLanes . length - i - 1 ) ;
if ( ! lookupSlightTurn | |
TurnType . isSlightTurn ( TurnType . getPrimaryTurn ( rawLanes [ ind ] ) )
| | TurnType . isSlightTurn ( TurnType . getSecondaryTurn ( rawLanes [ ind ] ) ) ) {
int cnt = countOccurrences ( splitLaneOptions [ ind ] , ';' ) + 1 ;
if ( cnt > 1 ) {
// sometimes slight right turn goes to the road with 2 lanes
// the better situation to group all the lanes and
// when ';' we know for sure the lane combines 2 group
2015-01-16 01:53:19 +01:00
roads - - ;
2015-01-15 02:42:33 +01:00
}
lanes - = cnt ;
// we already found slight turn others are turn in different direction
lookupSlightTurn = false ;
}
if ( lanes < 0 | | roads < 0 ) {
activeStartIndex = ind ;
break ;
}
}
return activeStartIndex ;
}
protected RoadSplitStructure calculateRoadSplitStructure ( RouteSegmentResult prevSegm , RouteSegmentResult currentSegm ,
List < RouteSegmentResult > attachedRoutes ) {
RoadSplitStructure rs = new RoadSplitStructure ( ) ;
2013-04-18 23:35:02 +02:00
int speakPriority = Math . max ( highwaySpeakPriority ( prevSegm . getObject ( ) . getHighway ( ) ) , highwaySpeakPriority ( currentSegm . getObject ( ) . getHighway ( ) ) ) ;
2015-01-15 02:42:33 +01:00
for ( RouteSegmentResult attached : attachedRoutes ) {
double ex = MapUtils . degreesDiff ( attached . getBearingBegin ( ) , currentSegm . getBearingBegin ( ) ) ;
double mpi = Math . abs ( MapUtils . degreesDiff ( prevSegm . getBearingEnd ( ) , attached . getBearingBegin ( ) ) ) ;
int rsSpeakPriority = highwaySpeakPriority ( attached . getObject ( ) . getHighway ( ) ) ;
2016-04-13 23:00:27 +02:00
int lanes = countLanesMinOne ( attached ) ;
boolean smallStraightVariation = mpi < TURN_DEGREE_MIN ;
boolean smallTargetVariation = Math . abs ( ex ) < TURN_DEGREE_MIN ;
boolean attachedOnTheRight = ex > = 0 ;
if ( attachedOnTheRight ) {
rs . roadsOnRight + + ;
} else {
rs . roadsOnLeft + + ;
}
2015-01-15 02:42:33 +01:00
if ( rsSpeakPriority ! = MAX_SPEAK_PRIORITY | | speakPriority = = MAX_SPEAK_PRIORITY ) {
if ( smallTargetVariation | | smallStraightVariation ) {
if ( attachedOnTheRight ) {
rs . keepLeft = true ;
rs . rightLanes + = lanes ;
} else {
rs . keepRight = true ;
rs . leftLanes + = lanes ;
}
2016-05-24 02:03:36 +02:00
rs . speak = rs . speak | | ( rsSpeakPriority < = speakPriority ) ;
2015-01-15 02:42:33 +01:00
} else {
if ( attachedOnTheRight ) {
rs . addRoadsOnRight + + ;
} else {
rs . addRoadsOnLeft + + ;
2013-04-18 23:35:02 +02:00
}
}
}
}
2015-01-15 02:42:33 +01:00
return rs ;
}
protected TurnType createSimpleKeepLeftRightTurn ( boolean leftSide , RouteSegmentResult prevSegm ,
RouteSegmentResult currentSegm , RoadSplitStructure rs ) {
2014-11-23 19:45:31 +01:00
int current = countLanesMinOne ( currentSegm ) ;
2015-01-15 02:42:33 +01:00
int ls = current + rs . leftLanes + rs . rightLanes ;
int [ ] lanes = new int [ ls ] ;
2014-11-23 19:45:31 +01:00
for ( int it = 0 ; it < ls ; it + + ) {
2015-01-15 02:42:33 +01:00
if ( it < rs . leftLanes | | it > = rs . leftLanes + current ) {
2014-11-23 19:45:31 +01:00
lanes [ it ] = 0 ;
} else {
lanes [ it ] = 1 ;
}
2013-04-18 23:35:02 +02:00
}
2014-11-23 19:45:31 +01:00
// sometimes links are
2016-05-23 21:22:33 +02:00
if ( ( current < = rs . leftLanes + rs . rightLanes ) & & ( rs . leftLanes > 1 | | rs . rightLanes > 1 ) ) {
rs . speak = true ;
}
2013-04-18 23:35:02 +02:00
double devation = Math . abs ( MapUtils . degreesDiff ( prevSegm . getBearingEnd ( ) , currentSegm . getBearingBegin ( ) ) ) ;
2013-04-21 13:08:21 +02:00
boolean makeSlightTurn = devation > 5 & & ( ! isMotorway ( prevSegm ) | | ! isMotorway ( currentSegm ) ) ;
2015-01-15 02:42:33 +01:00
TurnType t = null ;
if ( rs . keepLeft & & rs . keepRight ) {
2014-11-16 16:04:54 +01:00
t = TurnType . valueOf ( TurnType . C , leftSide ) ;
2016-05-24 09:23:09 +02:00
rs . speak = false ;
2015-01-15 02:42:33 +01:00
} else if ( rs . keepLeft ) {
2013-11-26 16:23:05 +01:00
t = TurnType . valueOf ( makeSlightTurn ? TurnType . TSLL : TurnType . KL , leftSide ) ;
2015-01-15 02:42:33 +01:00
} else if ( rs . keepRight ) {
2013-11-26 16:23:05 +01:00
t = TurnType . valueOf ( makeSlightTurn ? TurnType . TSLR : TurnType . KR , leftSide ) ;
2015-01-15 02:42:33 +01:00
} else {
2014-11-16 16:04:54 +01:00
return t ;
2013-04-18 23:35:02 +02:00
}
2015-01-15 02:42:33 +01:00
t . setSkipToSpeak ( ! rs . speak ) ;
t . setLanes ( lanes ) ;
2013-04-18 23:35:02 +02:00
return t ;
}
2014-10-02 17:50:02 +02:00
2014-11-23 19:45:31 +01:00
protected int countLanesMinOne ( RouteSegmentResult attached ) {
final boolean oneway = attached . getObject ( ) . getOneway ( ) ! = 0 ;
int lns = attached . getObject ( ) . getLanes ( ) ;
if ( lns = = 0 ) {
String tls = getTurnLanesString ( attached ) ;
if ( tls ! = null ) {
2014-11-23 19:51:47 +01:00
return Math . max ( 1 , countOccurrences ( tls , '|' ) ) ;
2014-11-23 19:45:31 +01:00
}
}
if ( oneway ) {
return Math . max ( 1 , lns ) ;
}
2014-10-02 17:50:02 +02:00
try {
if ( attached . isForwardDirection ( ) & & attached . getObject ( ) . getValue ( " lanes:forward " ) ! = null ) {
return Integer . parseInt ( attached . getObject ( ) . getValue ( " lanes:forward " ) ) ;
} else if ( ! attached . isForwardDirection ( ) & & attached . getObject ( ) . getValue ( " lanes:backward " ) ! = null ) {
return Integer . parseInt ( attached . getObject ( ) . getValue ( " lanes:backward " ) ) ;
}
2014-11-23 19:51:47 +01:00
} catch ( NumberFormatException e ) {
2014-10-02 17:50:02 +02:00
e . printStackTrace ( ) ;
}
2014-11-23 19:45:31 +01:00
return Math . max ( 1 , ( lns + 1 ) / 2 ) ;
2014-10-02 17:50:02 +02:00
}
2014-10-04 16:39:25 +02:00
2014-11-25 10:48:15 +01:00
protected static String getTurnLanesString ( RouteSegmentResult segment ) {
2014-10-04 16:39:25 +02:00
if ( segment . getObject ( ) . getOneway ( ) = = 0 ) {
if ( segment . isForwardDirection ( ) ) {
return segment . getObject ( ) . getValue ( " turn:lanes:forward " ) ;
} else {
return segment . getObject ( ) . getValue ( " turn:lanes:backward " ) ;
}
} else {
return segment . getObject ( ) . getValue ( " turn:lanes " ) ;
}
}
2015-01-15 02:42:33 +01:00
2014-10-04 16:39:25 +02:00
2014-10-08 04:38:48 +02:00
private int countOccurrences ( String haystack , char needle ) {
int count = 0 ;
for ( int i = 0 ; i < haystack . length ( ) ; i + + ) {
if ( haystack . charAt ( i ) = = needle ) {
count + + ;
}
}
return count ;
}
2014-11-25 10:48:15 +01:00
public static int [ ] parseTurnLanes ( RouteDataObject ro , double dirToNorthEastPi ) {
String turnLanes = null ;
if ( ro . getOneway ( ) = = 0 ) {
// we should get direction to detect forward or backward
double cmp = ro . directionRoute ( 0 , true ) ;
if ( Math . abs ( MapUtils . alignAngleDifference ( dirToNorthEastPi - cmp ) ) < Math . PI / 2 ) {
turnLanes = ro . getValue ( " turn:lanes:forward " ) ;
} else {
turnLanes = ro . getValue ( " turn:lanes:backward " ) ;
}
} else {
turnLanes = ro . getValue ( " turn:lanes " ) ;
}
if ( turnLanes = = null ) {
return null ;
}
2015-01-15 02:42:33 +01:00
return calculateRawTurnLanes ( turnLanes , 0 ) ;
2014-11-25 10:48:15 +01:00
}
2014-12-09 22:34:33 +01:00
public static int [ ] parseLanes ( RouteDataObject ro , double dirToNorthEastPi ) {
int lns = 0 ;
try {
if ( ro . getOneway ( ) = = 0 ) {
// we should get direction to detect forward or backward
double cmp = ro . directionRoute ( 0 , true ) ;
if ( Math . abs ( MapUtils . alignAngleDifference ( dirToNorthEastPi - cmp ) ) < Math . PI / 2 ) {
if ( ro . getValue ( " lanes:forward " ) ! = null ) {
lns = Integer . parseInt ( ro . getValue ( " lanes:forward " ) ) ;
}
} else {
if ( ro . getValue ( " lanes:backward " ) ! = null ) {
lns = Integer . parseInt ( ro . getValue ( " lanes:backward " ) ) ;
}
}
if ( lns = = 0 & & ro . getValue ( " lanes " ) ! = null ) {
lns = Integer . parseInt ( ro . getValue ( " lanes " ) ) / 2 ;
}
} else {
lns = Integer . parseInt ( ro . getValue ( " lanes " ) ) ;
}
if ( lns > 0 ) {
return new int [ lns ] ;
}
} catch ( NumberFormatException e ) {
}
return null ;
}
2015-01-15 02:42:33 +01:00
private static int [ ] calculateRawTurnLanes ( String turnLanes , int calcTurnType ) {
String [ ] splitLaneOptions = turnLanes . split ( " \\ | " , - 1 ) ;
2014-11-23 19:45:31 +01:00
int [ ] lanes = new int [ splitLaneOptions . length ] ;
2014-10-04 16:39:25 +02:00
for ( int i = 0 ; i < splitLaneOptions . length ; i + + ) {
String [ ] laneOptions = splitLaneOptions [ i ] . split ( " ; " ) ;
2016-04-05 11:58:05 +02:00
boolean isTertiaryTurn = false ;
2014-10-04 16:39:25 +02:00
for ( int j = 0 ; j < laneOptions . length ; j + + ) {
int turn ;
if ( laneOptions [ j ] . equals ( " none " ) | | laneOptions [ j ] . equals ( " through " ) ) {
turn = TurnType . C ;
} else if ( laneOptions [ j ] . equals ( " slight_right " ) ) {
turn = TurnType . TSLR ;
} else if ( laneOptions [ j ] . equals ( " slight_left " ) ) {
turn = TurnType . TSLL ;
} else if ( laneOptions [ j ] . equals ( " right " ) ) {
turn = TurnType . TR ;
} else if ( laneOptions [ j ] . equals ( " left " ) ) {
turn = TurnType . TL ;
} else if ( laneOptions [ j ] . equals ( " sharp_right " ) ) {
turn = TurnType . TSHR ;
} else if ( laneOptions [ j ] . equals ( " sharp_left " ) ) {
turn = TurnType . TSHL ;
} else if ( laneOptions [ j ] . equals ( " reverse " ) ) {
turn = TurnType . TU ;
} else {
// Unknown string
2016-04-13 00:14:31 +02:00
turn = TurnType . C ;
// continue;
2014-10-04 16:39:25 +02:00
}
2015-01-15 02:42:33 +01:00
final int primary = TurnType . getPrimaryTurn ( lanes [ i ] ) ;
if ( primary = = 0 ) {
TurnType . setPrimaryTurnAndReset ( lanes , i , turn ) ;
2014-10-04 16:39:25 +02:00
} else {
2015-01-15 02:42:33 +01:00
if ( turn = = calcTurnType | |
( TurnType . isRightTurn ( calcTurnType ) & & TurnType . isRightTurn ( turn ) ) | |
( TurnType . isLeftTurn ( calcTurnType ) & & TurnType . isLeftTurn ( turn ) )
) {
2016-03-28 15:51:52 +02:00
TurnType . setPrimaryTurnShiftOthers ( lanes , i , turn ) ;
2016-04-05 11:58:05 +02:00
} else if ( ! isTertiaryTurn ) {
2016-03-28 15:51:52 +02:00
TurnType . setSecondaryTurnShiftOthers ( lanes , i , turn ) ;
2016-04-05 11:58:05 +02:00
isTertiaryTurn = true ;
} else {
TurnType . setTertiaryTurn ( lanes , i , turn ) ;
break ;
2014-10-06 20:10:57 +02:00
}
2014-10-04 16:39:25 +02:00
}
}
}
2014-11-23 19:45:31 +01:00
return lanes ;
2014-10-04 16:39:25 +02:00
}
2014-11-23 19:45:31 +01:00
private int inferTurnFromLanes ( int [ ] oLanes ) {
TIntHashSet possibleTurns = new TIntHashSet ( ) ;
for ( int i = 0 ; i < oLanes . length ; i + + ) {
if ( ( oLanes [ i ] & 1 ) = = 0 ) {
2014-10-21 14:50:29 +02:00
continue ;
}
if ( possibleTurns . isEmpty ( ) ) {
// Nothing is in the list to compare to, so add the first elements
2014-11-23 19:45:31 +01:00
possibleTurns . add ( TurnType . getPrimaryTurn ( oLanes [ i ] ) ) ;
if ( TurnType . getSecondaryTurn ( oLanes [ i ] ) ! = 0 ) {
possibleTurns . add ( TurnType . getSecondaryTurn ( oLanes [ i ] ) ) ;
2014-10-21 14:50:29 +02:00
}
2016-03-28 15:51:52 +02:00
if ( TurnType . getTertiaryTurn ( oLanes [ i ] ) ! = 0 ) {
possibleTurns . add ( TurnType . getTertiaryTurn ( oLanes [ i ] ) ) ;
}
2014-10-21 14:50:29 +02:00
} else {
2014-11-23 19:45:31 +01:00
TIntArrayList laneTurns = new TIntArrayList ( ) ;
laneTurns . add ( TurnType . getPrimaryTurn ( oLanes [ i ] ) ) ;
if ( TurnType . getSecondaryTurn ( oLanes [ i ] ) ! = 0 ) {
laneTurns . add ( TurnType . getSecondaryTurn ( oLanes [ i ] ) ) ;
2014-10-21 14:50:29 +02:00
}
2016-03-28 15:51:52 +02:00
if ( TurnType . getTertiaryTurn ( oLanes [ i ] ) ! = 0 ) {
laneTurns . add ( TurnType . getTertiaryTurn ( oLanes [ i ] ) ) ;
}
2014-10-21 14:50:29 +02:00
possibleTurns . retainAll ( laneTurns ) ;
if ( possibleTurns . isEmpty ( ) ) {
// No common turns, so can't determine anything.
2014-11-23 19:45:31 +01:00
return 0 ;
2014-10-21 14:50:29 +02:00
}
}
}
// Remove all turns from lanes not selected...because those aren't it
2014-11-23 19:45:31 +01:00
for ( int i = 0 ; i < oLanes . length ; i + + ) {
if ( ( oLanes [ i ] & 1 ) = = 0 & & ! possibleTurns . isEmpty ( ) ) {
possibleTurns . remove ( ( Integer ) TurnType . getPrimaryTurn ( oLanes [ i ] ) ) ;
if ( TurnType . getSecondaryTurn ( oLanes [ i ] ) ! = 0 ) {
possibleTurns . remove ( ( Integer ) TurnType . getSecondaryTurn ( oLanes [ i ] ) ) ;
2014-10-21 14:50:29 +02:00
}
2016-03-28 15:51:52 +02:00
if ( TurnType . getTertiaryTurn ( oLanes [ i ] ) ! = 0 ) {
possibleTurns . remove ( ( Integer ) TurnType . getTertiaryTurn ( oLanes [ i ] ) ) ;
}
2014-10-21 14:50:29 +02:00
}
}
// Checking to see that there is only one unique turn
2014-11-23 19:45:31 +01:00
if ( possibleTurns . size ( ) = = 1 ) {
2015-01-15 03:17:26 +01:00
int infer = possibleTurns . iterator ( ) . next ( ) ;
for ( int i = 0 ; i < oLanes . length ; i + + ) {
if ( TurnType . getSecondaryTurn ( oLanes [ i ] ) = = infer ) {
int pt = TurnType . getPrimaryTurn ( oLanes [ i ] ) ;
int en = oLanes [ i ] & 1 ;
TurnType . setPrimaryTurnAndReset ( oLanes , i , infer ) ;
oLanes [ i ] | = en ;
TurnType . setSecondaryTurn ( oLanes , i , pt ) ;
}
}
return infer ;
2014-10-21 14:50:29 +02:00
}
2014-11-23 19:45:31 +01:00
return 0 ;
2014-10-21 14:50:29 +02:00
}
2013-04-21 13:08:21 +02:00
private boolean isMotorway ( RouteSegmentResult s ) {
String h = s . getObject ( ) . getHighway ( ) ;
return " motorway " . equals ( h ) | | " motorway_link " . equals ( h ) | |
" trunk " . equals ( h ) | | " trunk_link " . equals ( h ) ;
}
2013-04-18 23:35:02 +02:00
private void attachRoadSegments ( RoutingContext ctx , List < RouteSegmentResult > result , int routeInd , int pointInd , boolean plus ) throws IOException {
RouteSegmentResult rr = result . get ( routeInd ) ;
RouteDataObject road = rr . getObject ( ) ;
long nextL = pointInd < road . getPointsLength ( ) - 1 ? getPoint ( road , pointInd + 1 ) : 0 ;
long prevL = pointInd > 0 ? getPoint ( road , pointInd - 1 ) : 0 ;
// attach additional roads to represent more information about the route
RouteSegmentResult previousResult = null ;
// by default make same as this road id
long previousRoadId = road . getId ( ) ;
if ( pointInd = = rr . getStartPointIndex ( ) & & routeInd > 0 ) {
previousResult = result . get ( routeInd - 1 ) ;
previousRoadId = previousResult . getObject ( ) . getId ( ) ;
if ( previousRoadId ! = road . getId ( ) ) {
if ( previousResult . getStartPointIndex ( ) < previousResult . getEndPointIndex ( )
& & previousResult . getEndPointIndex ( ) < previousResult . getObject ( ) . getPointsLength ( ) - 1 ) {
rr . attachRoute ( pointInd , new RouteSegmentResult ( previousResult . getObject ( ) , previousResult . getEndPointIndex ( ) ,
previousResult . getObject ( ) . getPointsLength ( ) - 1 ) ) ;
} else if ( previousResult . getStartPointIndex ( ) > previousResult . getEndPointIndex ( )
& & previousResult . getEndPointIndex ( ) > 0 ) {
rr . attachRoute ( pointInd , new RouteSegmentResult ( previousResult . getObject ( ) , previousResult . getEndPointIndex ( ) , 0 ) ) ;
}
}
}
Iterator < RouteSegment > it ;
if ( rr . getPreAttachedRoutes ( pointInd ) ! = null ) {
final RouteSegmentResult [ ] list = rr . getPreAttachedRoutes ( pointInd ) ;
it = new Iterator < BinaryRoutePlanner . RouteSegment > ( ) {
int i = 0 ;
@Override
public boolean hasNext ( ) {
return i < list . length ;
}
@Override
public RouteSegment next ( ) {
RouteSegmentResult r = list [ i + + ] ;
return new RouteSegment ( r . getObject ( ) , r . getStartPointIndex ( ) ) ;
}
@Override
public void remove ( ) {
}
} ;
} else {
RouteSegment rt = ctx . loadRouteSegment ( road . getPoint31XTile ( pointInd ) , road . getPoint31YTile ( pointInd ) , ctx . config . memoryLimitation ) ;
it = rt = = null ? null : rt . getIterator ( ) ;
}
// try to attach all segments except with current id
while ( it ! = null & & it . hasNext ( ) ) {
RouteSegment routeSegment = it . next ( ) ;
if ( routeSegment . road . getId ( ) ! = road . getId ( ) & & routeSegment . road . getId ( ) ! = previousRoadId ) {
RouteDataObject addRoad = routeSegment . road ;
checkAndInitRouteRegion ( ctx , addRoad ) ;
// TODO restrictions can be considered as well
int oneWay = ctx . getRouter ( ) . isOneWay ( addRoad ) ;
if ( oneWay > = 0 & & routeSegment . getSegmentStart ( ) < addRoad . getPointsLength ( ) - 1 ) {
long pointL = getPoint ( addRoad , routeSegment . getSegmentStart ( ) + 1 ) ;
if ( pointL ! = nextL & & pointL ! = prevL ) {
// if way contains same segment (nodes) as different way (do not attach it)
rr . attachRoute ( pointInd , new RouteSegmentResult ( addRoad , routeSegment . getSegmentStart ( ) , addRoad . getPointsLength ( ) - 1 ) ) ;
}
}
if ( oneWay < = 0 & & routeSegment . getSegmentStart ( ) > 0 ) {
long pointL = getPoint ( addRoad , routeSegment . getSegmentStart ( ) - 1 ) ;
// if way contains same segment (nodes) as different way (do not attach it)
if ( pointL ! = nextL & & pointL ! = prevL ) {
rr . attachRoute ( pointInd , new RouteSegmentResult ( addRoad , routeSegment . getSegmentStart ( ) , 0 ) ) ;
}
}
}
}
}
private static void println ( String logMsg ) {
// log.info(logMsg);
System . out . println ( logMsg ) ;
}
private long getPoint ( RouteDataObject road , int pointInd ) {
return ( ( ( long ) road . getPoint31XTile ( pointInd ) ) < < 31 ) + ( long ) road . getPoint31YTile ( pointInd ) ;
}
private static double measuredDist ( int x1 , int y1 , int x2 , int y2 ) {
return MapUtils . getDistance ( MapUtils . get31LatitudeY ( y1 ) , MapUtils . get31LongitudeX ( x1 ) ,
MapUtils . get31LatitudeY ( y2 ) , MapUtils . get31LongitudeX ( x2 ) ) ;
}
}