2013-04-18 23:35:02 +02:00
package net.osmand.binary ;
import gnu.trove.map.hash.TIntObjectHashMap ;
import java.text.MessageFormat ;
2017-08-30 15:04:11 +02:00
import java.util.Arrays ;
2013-04-18 23:35:02 +02:00
2018-07-25 22:48:43 +02:00
import net.osmand.Location ;
2013-04-18 23:35:02 +02:00
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion ;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule ;
2014-02-02 14:06:09 +01:00
import net.osmand.util.Algorithms ;
2016-04-26 17:31:37 +02:00
import net.osmand.util.MapUtils ;
2019-02-18 13:02:38 +01:00
import net.osmand.util.TransliterationHelper ;
2016-08-19 10:55:48 +02:00
2013-04-18 23:35:02 +02:00
public class RouteDataObject {
/*private */ static final int RESTRICTION_SHIFT = 3 ;
/*private */ static final int RESTRICTION_MASK = 7 ;
2017-02-08 09:54:38 +01:00
public static int HEIGHT_UNDEFINED = - 80000 ;
2013-04-18 23:35:02 +02:00
public final RouteRegion region ;
// all these arrays supposed to be immutable!
// These fields accessible from C++
public int [ ] types ;
public int [ ] pointsX ;
public int [ ] pointsY ;
public long [ ] restrictions ;
2018-07-25 22:48:43 +02:00
public long [ ] restrictionsVia ;
2013-04-18 23:35:02 +02:00
public int [ ] [ ] pointTypes ;
2015-07-06 07:08:10 +02:00
public String [ ] [ ] pointNames ;
public int [ ] [ ] pointNameTypes ;
2013-04-18 23:35:02 +02:00
public long id ;
public TIntObjectHashMap < String > names ;
public final static float NONE_MAX_SPEED = 40f ;
2015-10-05 17:43:34 +02:00
public int [ ] nameIds ;
2017-02-08 09:54:38 +01:00
// mixed array [0, height, cumulative_distance height, cumulative_distance, height, ...] - length is length(points)*2
public float [ ] heightDistanceArray = null ;
2013-04-18 23:35:02 +02:00
public RouteDataObject ( RouteRegion region ) {
this . region = region ;
}
public RouteDataObject ( RouteRegion region , int [ ] nameIds , String [ ] nameValues ) {
this . region = region ;
2015-10-05 17:43:34 +02:00
this . nameIds = nameIds ;
2013-04-18 23:35:02 +02:00
if ( nameIds . length > 0 ) {
names = new TIntObjectHashMap < String > ( ) ;
}
for ( int i = 0 ; i < nameIds . length ; i + + ) {
names . put ( nameIds [ i ] , nameValues [ i ] ) ;
}
}
public RouteDataObject ( RouteDataObject copy ) {
this . region = copy . region ;
this . pointsX = copy . pointsX ;
this . pointsY = copy . pointsY ;
this . types = copy . types ;
this . names = copy . names ;
this . restrictions = copy . restrictions ;
2018-07-25 22:48:43 +02:00
this . restrictionsVia = copy . restrictionsVia ;
2013-04-18 23:35:02 +02:00
this . pointTypes = copy . pointTypes ;
2015-07-06 07:08:10 +02:00
this . pointNames = copy . pointNames ;
this . pointNameTypes = copy . pointNameTypes ;
2013-04-18 23:35:02 +02:00
this . id = copy . id ;
}
2017-02-08 09:54:38 +01:00
2017-08-30 15:04:11 +02:00
public boolean compareRoute ( RouteDataObject thatObj ) {
if ( this . id = = thatObj . id
& & Arrays . equals ( this . pointsX , thatObj . pointsX )
2017-08-30 18:06:55 +02:00
& & Arrays . equals ( this . pointsY , thatObj . pointsY ) ) {
2017-08-30 15:04:11 +02:00
if ( this . region = = null ) {
throw new IllegalStateException ( " Illegal routing object: " + id ) ;
}
if ( thatObj . region = = null ) {
throw new IllegalStateException ( " Illegal routing object: " + thatObj . id ) ;
}
2017-08-30 18:06:55 +02:00
2017-08-30 15:04:11 +02:00
boolean equals = true ;
2017-08-30 18:06:55 +02:00
equals = equals & & Arrays . equals ( this . restrictions , thatObj . restrictions ) ;
2018-07-25 22:48:43 +02:00
equals = equals & & Arrays . equals ( this . restrictionsVia , thatObj . restrictionsVia ) ;
2017-09-02 22:08:38 +02:00
2017-08-30 15:04:11 +02:00
if ( equals ) {
if ( this . types = = null | | thatObj . types = = null ) {
equals = this . types = = thatObj . types ;
} else if ( types . length ! = thatObj . types . length ) {
equals = false ;
} else {
for ( int i = 0 ; i < this . types . length & & equals ; i + + ) {
String thisTag = region . routeEncodingRules . get ( types [ i ] ) . getTag ( ) ;
String thisValue = region . routeEncodingRules . get ( types [ i ] ) . getValue ( ) ;
String thatTag = thatObj . region . routeEncodingRules . get ( thatObj . types [ i ] ) . getTag ( ) ;
String thatValue = thatObj . region . routeEncodingRules . get ( thatObj . types [ i ] ) . getValue ( ) ;
equals = ( thisTag . equals ( thatTag ) & & thisValue . equals ( thatValue ) ) ;
}
}
}
2017-08-30 18:06:55 +02:00
if ( equals ) {
if ( this . nameIds = = null | | thatObj . nameIds = = null ) {
equals = this . nameIds = = thatObj . nameIds ;
} else if ( nameIds . length ! = thatObj . nameIds . length ) {
equals = false ;
} else {
for ( int i = 0 ; i < this . nameIds . length & & equals ; i + + ) {
String thisTag = region . routeEncodingRules . get ( nameIds [ i ] ) . getTag ( ) ;
String thisValue = names . get ( nameIds [ i ] ) ;
String thatTag = thatObj . region . routeEncodingRules . get ( thatObj . nameIds [ i ] ) . getTag ( ) ;
String thatValue = thatObj . names . get ( thatObj . nameIds [ i ] ) ;
equals = ( Algorithms . objectEquals ( thisTag , thatTag ) & & Algorithms . objectEquals ( thisValue , thatValue ) ) ;
}
}
}
2017-08-30 22:35:41 +02:00
if ( equals ) {
if ( this . pointTypes = = null | | thatObj . pointTypes = = null ) {
equals = this . pointTypes = = thatObj . pointTypes ;
} else if ( pointTypes . length ! = thatObj . pointTypes . length ) {
equals = false ;
} else {
for ( int i = 0 ; i < this . pointTypes . length & & equals ; i + + ) {
if ( this . pointTypes [ i ] = = null | | thatObj . pointTypes [ i ] = = null ) {
equals = this . pointTypes [ i ] = = thatObj . pointTypes [ i ] ;
} else if ( pointTypes [ i ] . length ! = thatObj . pointTypes [ i ] . length ) {
equals = false ;
} else {
for ( int j = 0 ; j < this . pointTypes [ i ] . length & & equals ; j + + ) {
String thisTag = region . routeEncodingRules . get ( pointTypes [ i ] [ j ] ) . getTag ( ) ;
String thisValue = region . routeEncodingRules . get ( pointTypes [ i ] [ j ] ) . getValue ( ) ;
String thatTag = thatObj . region . routeEncodingRules . get ( thatObj . pointTypes [ i ] [ j ] ) . getTag ( ) ;
String thatValue = thatObj . region . routeEncodingRules . get ( thatObj . pointTypes [ i ] [ j ] ) . getValue ( ) ;
equals = ( Algorithms . objectEquals ( thisTag , thatTag ) & & Algorithms . objectEquals ( thisValue , thatValue ) ) ;
}
}
}
}
}
2017-08-30 18:06:55 +02:00
if ( equals ) {
if ( this . pointNameTypes = = null | | thatObj . pointNameTypes = = null ) {
equals = this . pointNameTypes = = thatObj . pointNameTypes ;
} else if ( pointNameTypes . length ! = thatObj . pointNameTypes . length ) {
equals = false ;
} else {
for ( int i = 0 ; i < this . pointNameTypes . length & & equals ; i + + ) {
if ( this . pointNameTypes [ i ] = = null | | thatObj . pointNameTypes [ i ] = = null ) {
equals = this . pointNameTypes [ i ] = = thatObj . pointNameTypes [ i ] ;
} else if ( pointNameTypes [ i ] . length ! = thatObj . pointNameTypes [ i ] . length ) {
equals = false ;
} else {
for ( int j = 0 ; j < this . pointNameTypes [ i ] . length & & equals ; j + + ) {
String thisTag = region . routeEncodingRules . get ( pointNameTypes [ i ] [ j ] ) . getTag ( ) ;
String thisValue = pointNames [ i ] [ j ] ;
String thatTag = thatObj . region . routeEncodingRules . get ( thatObj . pointNameTypes [ i ] [ j ] ) . getTag ( ) ;
String thatValue = thatObj . pointNames [ i ] [ j ] ;
equals = ( Algorithms . objectEquals ( thisTag , thatTag ) & & Algorithms . objectEquals ( thisValue , thatValue ) ) ;
}
}
}
}
}
2017-09-02 22:08:38 +02:00
return equals ;
2017-08-30 15:04:11 +02:00
}
return false ;
}
2017-02-08 09:54:38 +01:00
public float [ ] calculateHeightArray ( ) {
if ( heightDistanceArray ! = null ) {
return heightDistanceArray ;
}
int startHeight = Algorithms . parseIntSilently ( getValue ( " osmand_ele_start " ) , HEIGHT_UNDEFINED ) ;
int endHeight = Algorithms . parseIntSilently ( getValue ( " osmand_ele_end " ) , startHeight ) ;
if ( startHeight = = HEIGHT_UNDEFINED ) {
heightDistanceArray = new float [ 0 ] ;
return heightDistanceArray ;
}
heightDistanceArray = new float [ 2 * getPointsLength ( ) ] ;
double plon = 0 ;
double plat = 0 ;
2017-02-13 00:52:07 +01:00
float prevHeight = startHeight ;
2017-02-08 09:54:38 +01:00
for ( int k = 0 ; k < getPointsLength ( ) ; k + + ) {
double lon = MapUtils . get31LongitudeX ( getPoint31XTile ( k ) ) ;
double lat = MapUtils . get31LatitudeY ( getPoint31YTile ( k ) ) ;
if ( k > 0 ) {
double dd = MapUtils . getDistance ( plat , plon , lat , lon ) ;
2017-02-13 00:52:07 +01:00
float height = HEIGHT_UNDEFINED ;
2017-02-08 09:54:38 +01:00
if ( k = = getPointsLength ( ) - 1 ) {
height = endHeight ;
} else {
int [ ] tps = getPointTypes ( k ) ;
if ( tps ! = null ) {
for ( int id : tps ) {
RouteTypeRule rt = region . quickGetEncodingRule ( id ) ;
if ( rt . getTag ( ) . equals ( " osmand_ele_asc " ) ) {
2017-02-13 00:52:07 +01:00
height = ( prevHeight + Float . parseFloat ( rt . getValue ( ) ) ) ;
2017-02-08 09:54:38 +01:00
break ;
} else if ( rt . getTag ( ) . equals ( " osmand_ele_desc " ) ) {
2017-02-13 00:52:07 +01:00
height = ( prevHeight - Float . parseFloat ( rt . getValue ( ) ) ) ;
2017-02-08 09:54:38 +01:00
break ;
}
}
}
}
heightDistanceArray [ 2 * k ] = ( float ) dd ;
heightDistanceArray [ 2 * k + 1 ] = height ;
if ( height ! = HEIGHT_UNDEFINED ) {
// interpolate undefined
double totalDistance = dd ;
int startUndefined = k ;
while ( startUndefined - 1 > = 0 & & heightDistanceArray [ 2 * ( startUndefined - 1 ) + 1 ] = = HEIGHT_UNDEFINED ) {
startUndefined - - ;
totalDistance + = heightDistanceArray [ 2 * ( startUndefined ) ] ;
}
if ( totalDistance > 0 ) {
double angle = ( height - prevHeight ) / totalDistance ;
for ( int j = startUndefined ; j < k ; j + + ) {
heightDistanceArray [ 2 * j + 1 ] = ( float ) ( ( heightDistanceArray [ 2 * j ] * angle ) + heightDistanceArray [ 2 * j - 1 ] ) ;
}
}
prevHeight = height ;
}
} else {
heightDistanceArray [ 0 ] = 0 ;
heightDistanceArray [ 1 ] = startHeight ;
}
plat = lat ;
plon = lon ;
}
return heightDistanceArray ;
}
2013-04-18 23:35:02 +02:00
public long getId ( ) {
return id ;
}
public String getName ( ) {
if ( names ! = null ) {
return names . get ( region . nameTypeRule ) ;
}
return null ;
}
2018-05-25 23:12:50 +02:00
2016-12-04 11:42:33 +01:00
2015-06-16 00:47:01 +02:00
public String getName ( String lang ) {
2016-10-11 15:39:42 +02:00
return getName ( lang , false ) ;
}
public String getName ( String lang , boolean transliterate ) {
2015-06-16 00:47:01 +02:00
if ( names ! = null ) {
if ( Algorithms . isEmpty ( lang ) ) {
return names . get ( region . nameTypeRule ) ;
}
int [ ] kt = names . keys ( ) ;
for ( int i = 0 ; i < kt . length ; i + + ) {
int k = kt [ i ] ;
if ( region . routeEncodingRules . size ( ) > k ) {
if ( ( " name: " + lang ) . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
return names . get ( k ) ;
}
}
}
2016-10-11 15:39:42 +02:00
String nmDef = names . get ( region . nameTypeRule ) ;
if ( transliterate & & nmDef ! = null & & nmDef . length ( ) > 0 ) {
2019-02-18 17:03:51 +01:00
return TransliterationHelper . getInstance ( ) . transliterate ( nmDef ) ;
2016-10-11 15:39:42 +02:00
}
return nmDef ;
2015-06-16 00:47:01 +02:00
}
return null ;
}
2015-10-05 17:43:34 +02:00
public int [ ] getNameIds ( ) {
return nameIds ;
}
2014-03-01 12:30:25 +01:00
public TIntObjectHashMap < String > getNames ( ) {
return names ;
}
2016-12-04 11:42:33 +01:00
public String getRef ( String lang , boolean transliterate , boolean direction ) {
2016-12-02 14:51:33 +01:00
//if (getDestinationRef(direction) != null) {
2016-12-02 14:54:11 +01:00
// return getDestinationRef(direction);
2016-12-02 14:51:33 +01:00
//}
2016-12-02 12:59:17 +01:00
if ( names ! = null ) {
2016-12-04 11:42:33 +01:00
if ( Algorithms . isEmpty ( lang ) ) {
return names . get ( region . refTypeRule ) ;
}
int [ ] kt = names . keys ( ) ;
for ( int i = 0 ; i < kt . length ; i + + ) {
int k = kt [ i ] ;
if ( region . routeEncodingRules . size ( ) > k ) {
if ( ( " ref: " + lang ) . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
return names . get ( k ) ;
}
}
}
String refDefault = names . get ( region . refTypeRule ) ;
if ( transliterate & & refDefault ! = null & & refDefault . length ( ) > 0 ) {
2019-02-18 17:03:51 +01:00
return TransliterationHelper . getInstance ( ) . transliterate ( refDefault ) ;
2016-12-04 11:42:33 +01:00
}
return refDefault ;
2016-12-02 12:59:17 +01:00
}
return null ;
}
public String getDestinationRef ( boolean direction ) {
2014-12-10 00:27:03 +01:00
if ( names ! = null ) {
2016-08-19 18:55:53 +02:00
int [ ] kt = names . keys ( ) ;
String refTag = ( direction = = true ) ? " destination:ref:forward " : " destination:ref:backward " ;
2016-08-19 19:33:36 +02:00
String refTagDefault = " destination:ref " ;
String refDefault = null ;
2016-08-19 18:55:53 +02:00
for ( int i = 0 ; i < kt . length ; i + + ) {
int k = kt [ i ] ;
if ( region . routeEncodingRules . size ( ) > k ) {
if ( refTag . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
return names . get ( k ) ;
}
2016-08-19 19:33:36 +02:00
if ( refTagDefault . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
refDefault = names . get ( k ) ;
}
2016-08-19 18:55:53 +02:00
}
}
2016-08-19 19:42:09 +02:00
if ( refDefault ! = null ) {
2016-08-19 19:33:36 +02:00
return refDefault ;
2014-12-10 00:27:03 +01:00
}
2016-12-02 12:59:17 +01:00
//return names.get(region.refTypeRule);
2013-04-18 23:35:02 +02:00
}
return null ;
}
2016-10-11 15:39:42 +02:00
public String getDestinationName ( String lang , boolean transliterate , boolean direction ) {
2016-12-02 14:51:33 +01:00
//Issue #3289: Treat destination:ref like a destination, not like a ref
2016-12-04 11:42:33 +01:00
String destRef = ( ( getDestinationRef ( direction ) = = null ) | | getDestinationRef ( direction ) . equals ( getRef ( lang , transliterate , direction ) ) ) ? " " : getDestinationRef ( direction ) ;
2016-12-02 15:06:39 +01:00
String destRef1 = ( " " . equals ( destRef ) ) ? " " : destRef + " , " ;
2014-12-10 00:27:03 +01:00
if ( names ! = null ) {
2015-06-16 00:47:01 +02:00
int [ ] kt = names . keys ( ) ;
2016-10-26 20:44:40 +02:00
2016-10-31 09:12:56 +01:00
// Issue #3181: Parse destination keys in this order:
2016-10-26 20:44:40 +02:00
// destination:lang:XX:forward/backward
// destination:forward/backward
// destination:lang:XX
2016-10-26 21:09:26 +02:00
// destination
2016-10-26 20:44:40 +02:00
String destinationTagLangFB = " destination:lang:XX " ;
if ( ! Algorithms . isEmpty ( lang ) ) {
destinationTagLangFB = ( direction = = true ) ? " destination:lang: " + lang + " :forward " : " destination:lang: " + lang + " :backward " ;
}
String destinationTagFB = ( direction = = true ) ? " destination:forward " : " destination:backward " ;
String destinationTagLang = " destination:lang:XX " ;
2016-08-19 13:47:57 +02:00
if ( ! Algorithms . isEmpty ( lang ) ) {
2016-10-26 10:33:14 +02:00
destinationTagLang = " destination:lang: " + lang ;
2016-08-19 13:47:57 +02:00
}
2016-08-19 19:33:36 +02:00
String destinationTagDefault = " destination " ;
String destinationDefault = null ;
2016-08-19 13:47:57 +02:00
2015-06-16 00:47:01 +02:00
for ( int i = 0 ; i < kt . length ; i + + ) {
int k = kt [ i ] ;
if ( region . routeEncodingRules . size ( ) > k ) {
2016-10-26 20:44:40 +02:00
if ( ! Algorithms . isEmpty ( lang ) & & destinationTagLangFB . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
2019-02-18 17:03:51 +01:00
return destRef1 + ( ( transliterate ) ? TransliterationHelper . getInstance ( ) . transliterate ( names . get ( k ) ) : names . get ( k ) ) ;
2016-10-26 20:44:40 +02:00
}
if ( destinationTagFB . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
2019-02-18 17:03:51 +01:00
return destRef1 + ( ( transliterate ) ? TransliterationHelper . getInstance ( ) . transliterate ( names . get ( k ) ) : names . get ( k ) ) ;
2015-06-16 00:47:01 +02:00
}
2016-10-26 10:23:37 +02:00
if ( ! Algorithms . isEmpty ( lang ) & & destinationTagLang . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
2019-02-18 17:03:51 +01:00
return destRef1 + ( ( transliterate ) ? TransliterationHelper . getInstance ( ) . transliterate ( names . get ( k ) ) : names . get ( k ) ) ;
2016-10-26 10:23:37 +02:00
}
2016-08-19 19:33:36 +02:00
if ( destinationTagDefault . equals ( region . routeEncodingRules . get ( k ) . getTag ( ) ) ) {
destinationDefault = names . get ( k ) ;
}
2015-06-16 00:47:01 +02:00
}
}
2016-12-04 11:47:20 +01:00
if ( destinationDefault ! = null ) {
2019-02-18 17:03:51 +01:00
return destRef1 + ( ( transliterate ) ? TransliterationHelper . getInstance ( ) . transliterate ( destinationDefault ) : destinationDefault ) ;
2016-10-11 15:39:42 +02:00
}
2013-04-18 23:35:02 +02:00
}
2016-12-02 14:51:33 +01:00
return " " . equals ( destRef ) ? null : destRef ;
2013-04-18 23:35:02 +02:00
}
2016-12-02 14:51:33 +01:00
2013-04-18 23:35:02 +02:00
public int getPoint31XTile ( int i ) {
return pointsX [ i ] ;
}
public int getPoint31YTile ( int i ) {
return pointsY [ i ] ;
}
public int getPointsLength ( ) {
return pointsX . length ;
}
public int getRestrictionLength ( ) {
return restrictions = = null ? 0 : restrictions . length ;
}
public int getRestrictionType ( int i ) {
return ( int ) ( restrictions [ i ] & RESTRICTION_MASK ) ;
}
2017-09-02 18:54:57 +02:00
2018-07-25 22:48:43 +02:00
public RestrictionInfo getRestrictionInfo ( int k ) {
RestrictionInfo ri = new RestrictionInfo ( ) ;
ri . toWay = getRestrictionId ( k ) ;
ri . type = getRestrictionType ( k ) ;
if ( restrictionsVia ! = null & & k < restrictionsVia . length ) {
ri . viaWay = restrictionsVia [ k ] ;
}
2018-07-25 23:26:40 +02:00
return ri ;
2017-09-02 18:54:57 +02:00
}
2018-07-25 22:48:43 +02:00
public long getRestrictionVia ( int i ) {
if ( restrictionsVia ! = null & & restrictionsVia . length > i ) {
return restrictionsVia [ i ] ;
}
return 0 ;
}
2013-04-18 23:35:02 +02:00
public long getRestrictionId ( int i ) {
return restrictions [ i ] > > RESTRICTION_SHIFT ;
}
2016-04-26 20:08:37 +02:00
public boolean hasPointTypes ( ) {
return pointTypes ! = null ;
}
public boolean hasPointNames ( ) {
return pointNames ! = null ;
}
2013-04-18 23:35:02 +02:00
public void insert ( int pos , int x31 , int y31 ) {
int [ ] opointsX = pointsX ;
int [ ] opointsY = pointsY ;
int [ ] [ ] opointTypes = pointTypes ;
pointsX = new int [ pointsX . length + 1 ] ;
pointsY = new int [ pointsY . length + 1 ] ;
boolean insTypes = this . pointTypes ! = null & & this . pointTypes . length > pos ;
if ( insTypes ) {
pointTypes = new int [ opointTypes . length + 1 ] [ ] ;
}
int i = 0 ;
for ( ; i < pos ; i + + ) {
pointsX [ i ] = opointsX [ i ] ;
pointsY [ i ] = opointsY [ i ] ;
if ( insTypes ) {
pointTypes [ i ] = opointTypes [ i ] ;
}
}
pointsX [ i ] = x31 ;
pointsY [ i ] = y31 ;
if ( insTypes ) {
pointTypes [ i ] = null ;
}
for ( i = i + 1 ; i < pointsX . length ; i + + ) {
pointsX [ i ] = opointsX [ i - 1 ] ;
pointsY [ i ] = opointsY [ i - 1 ] ;
if ( insTypes & & i < pointTypes . length ) {
pointTypes [ i ] = opointTypes [ i - 1 ] ;
}
}
}
2015-07-06 07:08:10 +02:00
public String [ ] getPointNames ( int ind ) {
if ( pointNames = = null | | ind > = pointNames . length ) {
return null ;
}
return pointNames [ ind ] ;
}
public int [ ] getPointNameTypes ( int ind ) {
if ( pointNameTypes = = null | | ind > = pointNameTypes . length ) {
return null ;
}
return pointNameTypes [ ind ] ;
}
2013-04-18 23:35:02 +02:00
public int [ ] getPointTypes ( int ind ) {
if ( pointTypes = = null | | ind > = pointTypes . length ) {
return null ;
}
return pointTypes [ ind ] ;
}
public int [ ] getTypes ( ) {
return types ;
}
2016-08-19 14:37:15 +02:00
2015-08-31 00:11:48 +02:00
public float getMaximumSpeed ( boolean direction ) {
2013-04-18 23:35:02 +02:00
int sz = types . length ;
2016-08-19 14:37:15 +02:00
float maxSpeed = 0 ;
2013-04-18 23:35:02 +02:00
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
2015-08-31 00:11:48 +02:00
if ( r . isForward ( ) ! = 0 ) {
2016-10-25 12:55:21 +02:00
if ( ( r . isForward ( ) = = 1 ) ! = direction ) {
2015-08-31 00:11:48 +02:00
continue ;
}
}
2016-08-19 14:37:15 +02:00
float mx = r . maxSpeed ( ) ;
if ( mx > 0 ) {
maxSpeed = mx ;
// conditional has priority
if ( r . conditional ( ) ) {
break ;
}
}
2013-04-18 23:35:02 +02:00
}
2013-09-12 01:24:09 +02:00
return maxSpeed ;
2013-04-18 23:35:02 +02:00
}
2016-08-19 14:37:15 +02:00
2014-02-02 14:06:09 +01:00
public static float parseSpeed ( String v , float def ) {
if ( v . equals ( " none " ) ) {
return RouteDataObject . NONE_MAX_SPEED ;
} else {
int i = Algorithms . findFirstNumberEndIndex ( v ) ;
if ( i > 0 ) {
float f = Float . parseFloat ( v . substring ( 0 , i ) ) ;
f / = 3 . 6 ; // km/h -> m/s
if ( v . contains ( " mph " ) ) {
f * = 1 . 6 ;
}
return f ;
}
}
return def ;
}
public static float parseLength ( String v , float def ) {
2015-05-09 23:53:16 +02:00
float f = 0 ;
// 14'10" 14 - inches, 10 feet
2014-02-02 14:06:09 +01:00
int i = Algorithms . findFirstNumberEndIndex ( v ) ;
if ( i > 0 ) {
2015-05-09 23:53:16 +02:00
f + = Float . parseFloat ( v . substring ( 0 , i ) ) ;
String pref = v . substring ( i , v . length ( ) ) . trim ( ) ;
float add = 0 ;
for ( int ik = 0 ; ik < pref . length ( ) ; ik + + ) {
2015-08-04 10:51:46 +02:00
if ( Algorithms . isDigit ( pref . charAt ( ik ) ) | | pref . charAt ( ik ) = = '.' | | pref . charAt ( ik ) = = '-' ) {
2015-05-09 23:53:16 +02:00
int first = Algorithms . findFirstNumberEndIndex ( pref . substring ( ik ) ) ;
if ( first ! = - 1 ) {
add = parseLength ( pref . substring ( ik ) , 0 ) ;
pref = pref . substring ( 0 , ik ) ;
2015-05-09 14:44:10 +02:00
}
2015-05-09 23:53:16 +02:00
break ;
2015-05-09 14:44:10 +02:00
}
2014-02-02 14:06:09 +01:00
}
2015-05-09 23:53:16 +02:00
if ( pref . contains ( " km " ) ) {
f * = 1000 ;
}
2016-06-15 01:20:36 +02:00
if ( pref . contains ( " \" " ) | | pref . contains ( " in " ) ) {
2015-05-09 23:53:16 +02:00
f * = 0 . 0254 ;
2016-06-15 01:20:36 +02:00
} else if ( pref . contains ( " \ ' " ) | | pref . contains ( " ft " ) | | pref . contains ( " feet " ) ) {
2015-05-09 23:53:16 +02:00
// foot to meters
f * = 0 . 3048 ;
2016-06-15 01:20:36 +02:00
} else if ( pref . contains ( " cm " ) ) {
f * = 0 . 01 ;
} else if ( pref . contains ( " mile " ) ) {
f * = 1609 . 34f ;
2015-05-09 23:53:16 +02:00
}
return f + add ;
2014-02-02 14:06:09 +01:00
}
return def ;
}
public static float parseWeightInTon ( String v , float def ) {
int i = Algorithms . findFirstNumberEndIndex ( v ) ;
if ( i > 0 ) {
float f = Float . parseFloat ( v . substring ( 0 , i ) ) ;
if ( v . contains ( " \" " ) | | v . contains ( " lbs " ) ) {
// lbs -> kg -> ton
f = ( f * 0 . 4535f ) / 1000f ;
}
return f ;
}
return def ;
}
2013-04-18 23:35:02 +02:00
public boolean loop ( ) {
return pointsX [ 0 ] = = pointsX [ pointsX . length - 1 ] & & pointsY [ 0 ] = = pointsY [ pointsY . length - 1 ] ;
}
2018-12-14 12:06:59 +01:00
public boolean platform ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( r . getTag ( ) . equals ( " railway " ) & & r . getValue ( ) . equals ( " platform " ) ) {
return true ;
}
if ( r . getTag ( ) . equals ( " public_transport " ) & & r . getValue ( ) . equals ( " platform " ) ) {
return true ;
}
}
return false ;
}
2013-04-18 23:35:02 +02:00
public boolean roundabout ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( r . roundabout ( ) ) {
return true ;
} else if ( r . onewayDirection ( ) ! = 0 & & loop ( ) ) {
return true ;
}
}
return false ;
}
2013-10-29 21:13:57 +01:00
public boolean tunnel ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( r . getTag ( ) . equals ( " tunnel " ) & & r . getValue ( ) . equals ( " yes " ) ) {
return true ;
}
if ( r . getTag ( ) . equals ( " layer " ) & & r . getValue ( ) . equals ( " -1 " ) ) {
return true ;
}
}
return false ;
}
2013-04-18 23:35:02 +02:00
public int getOneway ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( r . onewayDirection ( ) ! = 0 ) {
return r . onewayDirection ( ) ;
} else if ( r . roundabout ( ) ) {
return 1 ;
}
}
return 0 ;
}
public String getRoute ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( " route " . equals ( r . getTag ( ) ) ) {
return r . getValue ( ) ;
}
}
return null ;
}
public String getHighway ( ) {
return getHighway ( types , region ) ;
}
2014-10-02 14:14:34 +02:00
2017-04-11 17:28:58 +02:00
public boolean hasPrivateAccess ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( " motorcar " . equals ( r . getTag ( ) )
| | " motor_vehicle " . equals ( r . getTag ( ) )
| | " vehicle " . equals ( r . getTag ( ) )
| | " access " . equals ( r . getTag ( ) ) ) {
if ( r . getValue ( ) . equals ( " private " ) ) {
return true ;
}
}
}
return false ;
}
2014-10-02 14:14:34 +02:00
public String getValue ( String tag ) {
for ( int i = 0 ; i < types . length ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
if ( r . getTag ( ) . equals ( tag ) ) {
return r . getValue ( ) ;
}
}
return null ;
}
2013-04-18 23:35:02 +02:00
public static String getHighway ( int [ ] types , RouteRegion region ) {
String highway = null ;
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
highway = r . highwayRoad ( ) ;
if ( highway ! = null ) {
break ;
}
}
return highway ;
}
public int getLanes ( ) {
int sz = types . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( types [ i ] ) ;
int ln = r . lanes ( ) ;
if ( ln > 0 ) {
return ln ;
}
}
return - 1 ;
}
public double directionRoute ( int startPoint , boolean plus ) {
// same goes to C++
// Victor : the problem to put more than 5 meters that BinaryRoutePlanner will treat
// 2 consequent Turn Right as UT and here 2 points will have same turn angle
// So it should be fix in both places
return directionRoute ( startPoint , plus , 5 ) ;
}
2016-08-19 10:55:48 +02:00
public boolean bearingVsRouteDirection ( Location loc ) {
boolean direction = true ;
if ( loc ! = null & & loc . hasBearing ( ) ) {
double diff = MapUtils . alignAngleDifference ( directionRoute ( 0 , true ) - loc . getBearing ( ) / 180f * Math . PI ) ;
direction = Math . abs ( diff ) < Math . PI / 2f ;
}
return direction ;
}
2018-05-25 23:12:50 +02:00
public boolean isRoadDeleted ( ) {
int [ ] pt = getTypes ( ) ;
int sz = pt . length ;
for ( int i = 0 ; i < sz ; i + + ) {
RouteTypeRule r = region . quickGetEncodingRule ( pt [ i ] ) ;
if ( " osmand_change " . equals ( r . getTag ( ) ) & & " delete " . equals ( r . getValue ( ) ) ) {
return true ;
}
}
return false ;
}
2016-08-19 10:55:48 +02:00
2017-10-29 17:29:41 +01:00
public boolean isStopApplicable ( boolean direction , int intId , int startPointInd , int endPointInd ) {
2017-10-28 15:09:01 +02:00
int [ ] pt = getPointTypes ( intId ) ;
int sz = pt . length ;
2017-10-28 09:52:34 +02:00
for ( int i = 0 ; i < sz ; i + + ) {
2017-10-28 15:09:01 +02:00
RouteTypeRule r = region . quickGetEncodingRule ( pt [ i ] ) ;
2017-10-28 16:41:05 +02:00
// Evaluate direction tag if present
2017-10-28 09:52:34 +02:00
if ( r . getTag ( ) . equals ( " direction " ) ) {
String dv = r . getValue ( ) ;
if ( ( dv . equals ( " forward " ) & & direction = = true ) | | ( dv . equals ( " backward " ) & & direction = = false ) ) {
2017-10-28 16:41:05 +02:00
return true ;
2017-10-28 09:52:34 +02:00
} else if ( ( dv . equals ( " forward " ) & & direction = = false ) | | ( dv . equals ( " backward " ) & & direction = = true ) ) {
2017-10-28 16:41:05 +02:00
return false ;
2016-08-19 22:31:34 +02:00
}
}
2017-10-28 20:58:30 +02:00
// Tagging stop=all should be ok anyway, usually tagged on intersection node itself, so not needed here
2017-10-28 09:52:34 +02:00
//if (r.getTag().equals("stop") && r.getValue().equals("all")) {
2017-10-28 16:41:05 +02:00
// return true;
2017-10-28 09:52:34 +02:00
//}
2016-08-19 22:31:34 +02:00
}
2017-10-29 18:18:17 +01:00
// Heuristic fallback: Distance analysis for STOP with no recognized directional tagging:
// Mask STOPs closer to the start than to the end of the routing segment if it is within 50m of start, but do not mask STOPs mapped directly on start/end (likely intersection node)
2017-10-29 17:29:41 +01:00
double d2Start = distance ( startPointInd , intId ) ;
double d2End = distance ( intId , endPointInd ) ;
2017-10-29 18:18:17 +01:00
if ( ( d2Start < d2End ) & & d2Start ! = 0 & & d2End ! = 0 & & d2Start < 50 ) {
2017-10-28 16:41:05 +02:00
return false ;
2017-10-28 16:22:18 +02:00
}
2017-10-28 16:41:05 +02:00
// No directional info detected
return true ;
2016-08-19 22:31:34 +02:00
}
2013-11-26 16:23:05 +01:00
public double distance ( int startPoint , int endPoint ) {
if ( startPoint > endPoint ) {
int k = endPoint ;
endPoint = startPoint ;
startPoint = k ;
}
double d = 0 ;
for ( int k = startPoint ; k < endPoint & & k < getPointsLength ( ) - 1 ; k + + ) {
int x = getPoint31XTile ( k ) ;
int y = getPoint31YTile ( k ) ;
int kx = getPoint31XTile ( k + 1 ) ;
int ky = getPoint31YTile ( k + 1 ) ;
d + = simplifyDistance ( kx , ky , x , y ) ;
}
return d ;
}
2013-04-18 23:35:02 +02:00
// Gives route direction of EAST degrees from NORTH ]-PI, PI]
public double directionRoute ( int startPoint , boolean plus , float dist ) {
int x = getPoint31XTile ( startPoint ) ;
int y = getPoint31YTile ( startPoint ) ;
int nx = startPoint ;
int px = x ;
int py = y ;
double total = 0 ;
do {
if ( plus ) {
nx + + ;
if ( nx > = getPointsLength ( ) ) {
break ;
}
} else {
nx - - ;
if ( nx < 0 ) {
break ;
}
}
px = getPoint31XTile ( nx ) ;
py = getPoint31YTile ( nx ) ;
// translate into meters
2013-11-26 16:23:05 +01:00
total + = simplifyDistance ( x , y , px , py ) ;
2013-04-18 23:35:02 +02:00
} while ( total < dist ) ;
return - Math . atan2 ( x - px , y - py ) ;
}
2013-11-26 16:23:05 +01:00
private double simplifyDistance ( int x , int y , int px , int py ) {
return Math . abs ( px - x ) * 0 . 011d + Math . abs ( py - y ) * 0 . 01863d ;
}
2013-04-18 23:35:02 +02:00
2015-05-09 23:53:16 +02:00
private static void assertTrueLength ( String vl , float exp ) {
float dest = parseLength ( vl , 0 ) ;
if ( exp ! = dest ) {
System . err . println ( " FAIL " + vl + " " + dest ) ;
} else {
System . out . println ( " OK " + vl ) ;
}
}
public static void main ( String [ ] args ) {
assertTrueLength ( " 10 km " , 10000 ) ;
2016-06-15 01:20:36 +02:00
assertTrueLength ( " 0.01 km " , 10 ) ;
assertTrueLength ( " 0.01 km 10 m " , 20 ) ;
assertTrueLength ( " 10 m " , 10 ) ;
assertTrueLength ( " 10m " , 10 ) ;
assertTrueLength ( " 3.4 m " , 3 . 4f ) ;
assertTrueLength ( " 3.40 m " , 3 . 4f ) ;
assertTrueLength ( " 10 m 10m " , 20 ) ;
assertTrueLength ( " 14'10 \" " , 4 . 5212f ) ;
assertTrueLength ( " 14.5' " , 4 . 4196f ) ;
assertTrueLength ( " 14.5 ft " , 4 . 4196f ) ;
assertTrueLength ( " 14'0 \" " , 4 . 2672f ) ;
assertTrueLength ( " 15ft " , 4 . 572f ) ;
assertTrueLength ( " 15 ft 1 in " , 4 . 5974f ) ;
assertTrueLength ( " 4.1 metres " , 4 . 1f ) ;
assertTrueLength ( " 14'0'' " , 4 . 2672f ) ;
assertTrueLength ( " 14 feet " , 4 . 2672f ) ;
assertTrueLength ( " 14 mile " , 22530 . 76f ) ;
assertTrueLength ( " 14 cm " , 0 . 14f ) ;
// float badValue = -1;
// assertTrueLength("none", badValue);
// assertTrueLength("m 4.1", badValue);
// assertTrueLength("1F4 m", badValue);
2015-05-09 23:53:16 +02:00
}
2016-04-26 17:31:37 +02:00
public String coordinates ( ) {
StringBuilder b = new StringBuilder ( ) ;
b . append ( " lat/lon : " ) ;
for ( int i = 0 ; i < getPointsLength ( ) ; i + + ) {
float x = ( float ) MapUtils . get31LongitudeX ( getPoint31XTile ( i ) ) ;
float y = ( float ) MapUtils . get31LatitudeY ( getPoint31YTile ( i ) ) ;
b . append ( y ) . append ( " / " ) . append ( x ) . append ( " , " ) ;
}
return b . toString ( ) ;
}
2013-04-18 23:35:02 +02:00
@Override
public String toString ( ) {
String name = getName ( ) ;
2016-12-04 11:42:33 +01:00
String rf = getRef ( " " , false , true ) ;
2016-06-15 00:59:00 +02:00
return MessageFormat . format ( " Road id {0} name {1} ref {2} " , ( getId ( ) / 64 ) + " " , name = = null ? " " : name ,
rf = = null ? " " : rf ) ;
2013-04-18 23:35:02 +02:00
}
2018-07-25 22:48:43 +02:00
public static class RestrictionInfo {
public int type ;
public long toWay ;
public long viaWay ;
public RestrictionInfo next ; // optional to simulate linked list
public int length ( ) {
if ( next = = null ) {
return 1 ;
}
return next . length ( ) + 1 ;
}
}
public void setRestriction ( int k , long to , int type , long viaWay ) {
long valto = ( to < < RouteDataObject . RESTRICTION_SHIFT ) | ( ( long ) type & RouteDataObject . RESTRICTION_MASK ) ;
restrictions [ k ] = valto ;
if ( viaWay ! = 0 ) {
setRestrictionVia ( k , viaWay ) ;
}
}
public void setRestrictionVia ( int k , long viaWay ) {
if ( restrictionsVia ! = null ) {
long [ ] nrestrictionsVia = new long [ Math . max ( k + 1 , restrictions . length ) ] ;
System . arraycopy ( restrictions , 0 , nrestrictionsVia , 0 , restrictions . length ) ;
restrictionsVia = nrestrictionsVia ;
} else {
restrictionsVia = new long [ k + 1 ] ;
}
restrictionsVia [ k ] = viaWay ;
}
2015-05-09 14:44:10 +02:00
}