diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 6694fb1..fb66d38 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -26,6 +26,9 @@ import btools.util.FrozenLongMap; public final class OsmTrack { + // csv-header-line + private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tOsmTags"; + public MatchedWaypoint endPoint; public long[] nogoChecksums; @@ -220,7 +223,7 @@ public final class OsmTrack sb.append( " xmlns=\"http://www.topografix.com/GPX/1/1\" \n" ); sb.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" ); sb.append( " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n" ); - sb.append( " creator=\"BRouter-1.1\" version=\"1.1\">\n" ); + sb.append( " creator=\"BRouter-1.2\" version=\"1.1\">\n" ); sb.append( " \n" ); sb.append(" ").append(name).append("\n"); sb.append( " \n" ); @@ -304,7 +307,19 @@ public final class OsmTrack sb.append( " \"track-length\": \"" ).append( distance ).append( "\",\n" ); sb.append( " \"filtered ascend\": \"" ).append( ascend ).append( "\",\n" ); sb.append( " \"plain-ascend\": \"" ).append( plainAscend ).append( "\",\n" ); - sb.append( " \"cost\": \"" ).append( cost ).append( "\"\n" ); + sb.append( " \"cost\": \"" ).append( cost ).append( "\",\n" ); + sb.append( " \"messages\": [\n" ); + sb.append( " [\"").append( MESSAGES_HEADER.replaceAll("\t", "\", \"") ).append( "\"],\n" ); + for( OsmPathElement n : nodes ) + { + if ( n.message != null ) + { + sb.append( " [\"").append( n.message.replaceAll("\t", "\", \"") ).append( "\"],\n" ); + } + } + sb.deleteCharAt( sb.lastIndexOf( "," ) ); + sb.append( " ]\n" ); + sb.append( " },\n" ); if ( iternity != null ) @@ -361,10 +376,7 @@ public final class OsmTrack public void writeMessages( BufferedWriter bw, RoutingContext rc ) throws Exception { - // csv-header-line - - String header = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tOsmTags"; - dumpLine( bw, header ); + dumpLine( bw, MESSAGES_HEADER ); for( OsmPathElement n : nodes ) { if ( n.message != null ) diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpression.java b/brouter-expressions/src/main/java/btools/expressions/BExpression.java index 80bce4f..59bcfb4 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpression.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpression.java @@ -1,5 +1,6 @@ package btools.expressions; +import java.util.StringTokenizer; final class BExpression { @@ -26,12 +27,28 @@ final class BExpression private float numberValue; private int variableIdx; private int lookupNameIdx; - private int lookupValueIdx; + private int[] lookupValueIdxArray; // Parse the expression and all subexpression public static BExpression parse( BExpressionContext ctx, int level ) throws Exception { + return parse( ctx, level, null ); + } + + private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception + { + boolean brackets = false; String operator = ctx.parseToken(); + if ( optionalToken != null && optionalToken.equals( operator ) ) + { + operator = ctx.parseToken(); + } + if ( "(".equals( operator ) ) + { + brackets = true; + operator = ctx.parseToken(); + } + if ( operator == null ) { if ( level == 0 ) return null; @@ -48,11 +65,17 @@ final class BExpression BExpression exp = new BExpression(); int nops = 3; + boolean ifThenElse = false; if ( "switch".equals( operator ) ) { exp.typ = SWITCH_EXP; } + else if ( "if".equals( operator ) ) + { + exp.typ = SWITCH_EXP; + ifThenElse = true; + } else { nops = 2; // check binary expressions @@ -105,17 +128,25 @@ final class BExpression { exp.typ = LOOKUP_EXP; String name = operator.substring( 0, idx ); - String value = operator.substring( idx+1 ); + String values = operator.substring( idx+1 ); exp.lookupNameIdx = ctx.getLookupNameIdx( name ); if ( exp.lookupNameIdx < 0 ) { throw new IllegalArgumentException( "unknown lookup name: " + name ); } - exp.lookupValueIdx = ctx.getLookupValueIdx( exp.lookupNameIdx, value ); - if ( exp.lookupValueIdx < 0 ) + StringTokenizer tk = new StringTokenizer( values, "|" ); + int nt = tk.countTokens(); + int nt2 = nt == 0 ? 1 : nt; + exp.lookupValueIdxArray = new int[nt2]; + for( int ti=0; ti= 0 ) @@ -123,6 +154,16 @@ final class BExpression exp.typ = VARIABLE_EXP; exp.variableIdx = idx; } + else if ( "true".equals( operator ) ) + { + exp.numberValue = 1.f; + exp.typ = NUMBER_EXP; + } + else if ( "false".equals( operator ) ) + { + exp.numberValue = 0.f; + exp.typ = NUMBER_EXP; + } else { try @@ -139,11 +180,35 @@ final class BExpression } } // parse operands - if ( nops > 0 ) exp.op1 = BExpression.parse( ctx, level+1 ); - if ( nops > 1 ) exp.op2 = BExpression.parse( ctx, level+1 ); - if ( nops > 2 ) exp.op3 = BExpression.parse( ctx, level+1 ); + if ( nops > 0 ) + { + exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null ); + } + if ( nops > 1 ) + { + if ( ifThenElse ) checkExpectedToken( ctx, "then" ); + exp.op2 = BExpression.parse( ctx, level+1, null ); + } + if ( nops > 2 ) + { + if ( ifThenElse ) checkExpectedToken( ctx, "else" ); + exp.op3 = BExpression.parse( ctx, level+1, null ); + } + if ( brackets ) + { + checkExpectedToken( ctx, ")" ); + } return exp; } + + private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception + { + String token = ctx.parseToken(); + if ( ! expected.equals( token ) ) + { + throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected ); + } + } // Evaluate the expression public float evaluate( BExpressionContext ctx ) @@ -157,7 +222,7 @@ final class BExpression case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) ); case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx); case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) ); - case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdx ); + case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray ); case NUMBER_EXP: return numberValue; case VARIABLE_EXP: return ctx.getVariableValue( variableIdx ); case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f; diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java index 7165821..54fa55e 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java @@ -19,6 +19,7 @@ import java.util.TreeMap; import btools.util.BitCoderContext; import btools.util.Crc32; +import java.util.Random; public final class BExpressionContext @@ -172,7 +173,7 @@ public final class BExpressionContext decode( ld2, false, ab ); for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names { - if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding " + getKeyValueDescription(false, ab) ); + if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding inum=" + inum + " val=" + ld[inum] + " " + getKeyValueDescription(false, ab) ); } return ab; @@ -469,6 +470,51 @@ public final class BExpressionContext return null; } + /** + * generate random values for regression testing + */ + public int[] generateRandomValues( Random rnd ) + { + int[] data = createNewLookupData(); + data[0] = 2*rnd.nextInt( 2 ); // reverse-direction = 0 or 2 + for( int inum = 1; inum < data.length; inum++ ) + { + int nvalues = lookupValues.get( inum ).length; + data[inum] = 0; + if ( inum > 1 && rnd.nextInt( 10 ) > 0 ) continue; // tags other than highway only 10% + data[inum] = rnd.nextInt( nvalues ); + } + lookupDataValid = true; + return data; + } + + public void assertAllVariablesEqual( BExpressionContext other ) + { + int nv = variableData.length; + int nv2 = other.variableData.length; + if ( nv != nv2 ) throw new RuntimeException( "mismatch in variable-count: " + nv + "<->" + nv2 ); + for( int i=0; i" + other.variableData[i] + + "\ntags = " + getKeyValueDescription( false, encode() ) ); + } + } + } + + private String variableName( int idx ) + { + for( Map.Entry e : variableNumbers.entrySet() ) + { + if ( e.getValue().intValue() == idx ) + { + return e.getKey(); + } + } + throw new RuntimeException( "no variable for index" + idx ); + } + /** * add a new lookup-value for the given name to the given lookupData array. * If no array is given (null value passed), the value is added to @@ -705,9 +751,16 @@ public final class BExpressionContext return minWriteIdx; } - float getLookupMatch( int nameIdx, int valueIdx ) + float getLookupMatch( int nameIdx, int[] valueIdxArray ) { - return lookupData[nameIdx] == valueIdx ? 1.0f : 0.0f; + for( int i=0; i " ); + return; + } + + File lookupFile = new File( args[0] ); + File profile1File = new File( args[1] ); + File profile2File = new File( args[2] ); + int nsamples = Integer.parseInt( args[3] ); + testContext( lookupFile, profile1File, profile2File, nsamples, "way" ); + testContext( lookupFile, profile1File, profile2File, nsamples, "node" ); + + } + + private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, String contextName ) + { + // read lookup.dat + profiles + BExpressionMetaData meta1 = new BExpressionMetaData(); + BExpressionMetaData meta2 = new BExpressionMetaData(); + BExpressionContext expctx1 = new BExpressionContext(contextName, 4096, meta1 ); + BExpressionContext expctx2 = new BExpressionContext(contextName, 4096, meta2 ); + meta1.readMetaData( lookupFile ); + meta2.readMetaData( lookupFile ); + expctx1.parseFile( profile1File, "global" ); + expctx2.parseFile( profile2File, "global" ); + + Random rnd = new Random(); + for( int i=0; i + android:versionCode="6" + android:versionName="1.2" package="btools.routingapp"> PROFILE_UPLOAD_URL.length() + 1 ) + if ( getline.startsWith("OPTIONS") ) { - // e.g. /brouter/profile/custom_1400767688382 - profileId = url.substring(PROFILE_UPLOAD_URL.length() + 1); + // handle CORS preflight request (Safari) + String corsHeaders = "Access-Control-Allow-Methods: GET, POST\n" + + "Access-Control-Allow-Headers: Content-Type\n"; + writeHttpHeader( bw, "text/plain", null, corsHeaders ); + bw.flush(); + return; } + else + { + writeHttpHeader(bw, "application/json"); - ProfileUploadHandler uploadHandler = new ProfileUploadHandler( serviceContext ); - uploadHandler.handlePostRequest( profileId, br, bw ); + String profileId = null; + if ( url.length() > PROFILE_UPLOAD_URL.length() + 1 ) + { + // e.g. /brouter/profile/custom_1400767688382 + profileId = url.substring(PROFILE_UPLOAD_URL.length() + 1); + } - bw.flush(); - return; + ProfileUploadHandler uploadHandler = new ProfileUploadHandler( serviceContext ); + uploadHandler.handlePostRequest( profileId, br, bw ); + + bw.flush(); + return; + } } else { @@ -126,7 +138,7 @@ public class RouteServer extends Thread public static void main(String[] args) throws Exception { - System.out.println("BRouter 1.1 / 27122014"); + System.out.println("BRouter 1.2 / 07022015"); if ( args.length != 5 ) { System.out.println("serve BRouter protocol"); @@ -213,6 +225,11 @@ public class RouteServer extends Thread } private static void writeHttpHeader( BufferedWriter bw, String mimeType, String fileName ) throws IOException + { + writeHttpHeader( bw, mimeType, fileName, null); + } + + private static void writeHttpHeader( BufferedWriter bw, String mimeType, String fileName, String headers ) throws IOException { // http-header bw.write( "HTTP/1.1 200 OK\n" ); @@ -223,6 +240,10 @@ public class RouteServer extends Thread bw.write( "Content-Disposition: attachment; filename=" + fileName + "\n" ); } bw.write( "Access-Control-Allow-Origin: *\n" ); + if ( headers != null ) + { + bw.write( headers ); + } bw.write( "\n" ); } } diff --git a/misc/profiles2/trekking.brf b/misc/profiles2/trekking.brf index 415be6a..02d9f56 100644 --- a/misc/profiles2/trekking.brf +++ b/misc/profiles2/trekking.brf @@ -8,21 +8,21 @@ # Use the following switches to change behaviour # (1=yes, 0=no): -assign consider_elevation 1 # set to 0 to ignore elevation in routing -assign allow_steps 1 # set to 0 to disallow steps -assign allow_ferries 1 # set to 0 to disallow ferries -assign ignore_cycleroutes 0 # set to 1 for better elevation results -assign stick_to_cycleroutes 0 # set to 1 to just follow cycleroutes -assign avoid_unsafe 0 # set to 1 to avoid standard highways +assign consider_elevation = true # set to false to ignore elevation in routing +assign allow_steps = true # set to false to disallow steps +assign allow_ferries = true # set to false to disallow ferries +assign ignore_cycleroutes = false # set to true for better elevation results +assign stick_to_cycleroutes = false # set to true to just follow cycleroutes +assign avoid_unsafe = false # set to true to avoid standard highways -assign validForBikes 1 +assign validForBikes = true # the elevation parameters -assign downhillcost switch consider_elevation 60 0 -assign downhillcutoff 1.5 -assign uphillcost 0 -assign uphillcutoff 1.5 +assign downhillcost = if consider_elevation then 60 else 0 +assign downhillcutoff = 1.5 +assign uphillcost = 0 +assign uphillcutoff = 1.5 ---context:way # following code refers to way-tags @@ -30,14 +30,25 @@ assign uphillcutoff 1.5 # pre-calculate some logical expressions # -assign any_cycleroute or route_bicycle_icn=yes or route_bicycle_ncn=yes or route_bicycle_rcn=yes route_bicycle_lcn=yes -assign nodeaccessgranted or any_cycleroute lcn=yes +assign any_cycleroute = + if route_bicycle_icn=yes then true + else if route_bicycle_ncn=yes then true + else if route_bicycle_rcn=yes then true + else if route_bicycle_lcn=yes then true + else false -assign is_ldcr and any_cycleroute not ignore_cycleroutes -assign isbike or bicycle=yes or or bicycle=permissive bicycle=designated lcn=yes -assign ispaved or surface=paved or surface=asphalt or surface=concrete surface=paving_stones -assign isunpaved not or surface= or ispaved or surface=fine_gravel surface=cobblestone -assign probablyGood or ispaved and isbike not isunpaved +assign nodeaccessgranted = + if any_cycleroute then true + else lcn=yes + +assign is_ldcr = + if ignore_cycleroutes then false + else any_cycleroute + +assign isbike = or bicycle=yes or or bicycle=permissive bicycle=designated lcn=yes +assign ispaved = surface=paved|asphalt|concrete|paving_stones +assign isunpaved = not or surface= or ispaved surface=fine_gravel|cobblestone +assign probablyGood = or ispaved and isbike not isunpaved # @@ -46,59 +57,53 @@ assign probablyGood or ispaved and isbike not isunpaved # (Suppressing turncost while following longdistance-cycleways # makes them a little bit more magnetic) # -assign turncost switch is_ldcr 0 90 +assign turncost = if is_ldcr then 0 else 90 # # calculate the initial cost # this is added to the total cost each time the costfactor # changed # -assign initialcost switch route=ferry 10000 0 +assign initialcost = if route=ferry then 10000 else 0 # # implicit access here just from the motorroad tag # (implicit access rules from highway tag handled elsewhere) # -assign defaultaccess - switch access= - not motorroad=yes - switch or access=private access=no - 0 - 1 +assign defaultaccess = + if access= then not motorroad=yes + else if access=private|no then false + else true # # calculate logical bike access # -assign bikeaccess - or any_cycleroute - switch bicycle= - switch vehicle= - defaultaccess - switch or vehicle=private vehicle=no - 0 - 1 - not or bicycle=private or bicycle=no bicycle=dismount +assign bikeaccess = + if any_cycleroute then true + else if bicycle= then + ( + if vehicle= then defaultaccess + else not vehicle=private|no + ) + else not bicycle=private|no|dismount # # calculate logical foot access # -assign footaccess - or bikeaccess - or bicycle=dismount - switch foot= - defaultaccess - not or foot=private foot=no +assign footaccess = + if bikeaccess then true + else if bicycle=dismount then true + else if foot= then defaultaccess + else not foot=private|no # # if not bike-, but foot-acess, just a moderate penalty, # otherwise access is forbidden # -assign accesspenalty - switch bikeaccess - 0 - switch footaccess - 4 - 100000 +assign accesspenalty = + if bikeaccess then 0 + else if footaccess then 4 + else 100000 # # handle one-ways. On primary roads, wrong-oneways should @@ -106,19 +111,21 @@ assign accesspenalty # 4 to the costfactor (making it at least 5 - you are allowed # to push your bike) # -assign oneway - switch oneway= - junction=roundabout - or oneway=yes or oneway=true oneway=1 +assign oneway = + if oneway= then junction=roundabout else oneway=yes|true|1 + +assign onewaypenalty = + if ( if reversedirection=yes then oneway else oneway=-1 ) then + ( + if ( cycleway=opposite|opposite_lane|opposite_track ) then 0 + else if ( oneway:bicycle=no ) then 0 + else if ( highway=primary|primary_link ) then 50 + else if ( highway=secondary|secondary_link ) then 30 + else if ( highway=tertiary|tertiary_link ) then 20 + else 4.0 + ) + else 0.0 -assign onewaypenalty - switch switch reversedirection=yes oneway oneway=-1 - switch or cycleway=opposite or cycleway=opposite_lane or cycleway=opposite_track oneway:bicycle=no 0 - switch or highway=primary highway=primary_link 50 - switch or highway=secondary highway=secondary_link 30 - switch or highway=tertiary highway=tertiary_link 20 - 4.0 - 0.0 # # calculate the cost-factor, which is the factor @@ -127,106 +134,98 @@ assign onewaypenalty # must be >=1 and it's supposed to be close to 1 for # the type of way the routing profile is searching for # -assign costfactor +assign costfactor = - add max onewaypenalty accesspenalty + add ( max onewaypenalty accesspenalty ) - switch and highway= not route=ferry 100000 + if ( and highway= not route=ferry ) then 100000 # # steps and ferries are special. Note this is handled # before the cycleroute-switch, to be able # to really exlude them be setting cost to infinity # - switch highway=steps switch allow_steps 40 100000 - switch route=ferry switch allow_ferries 5.67 100000 + else if ( highway=steps ) then ( if allow_steps then 40 else 100000 ) + else if ( route=ferry ) then ( if allow_ferries then 5.67 else 100000 ) # # handle long-distance cycle-routes. # - switch is_ldcr 1 # always treated as perfect (=1) - add switch stick_to_cycleroutes 0.5 0.05 # everything else somewhat up + else if ( is_ldcr ) then 1 # always treated as perfect (=1) + else + add ( if stick_to_cycleroutes then 0.5 else 0.05 ) # everything else somewhat up # # some other highway types # - switch highway=pedestrian 3 - switch highway=bridleway 5 - switch highway=cycleway 1 - switch or highway=residential highway=living_street switch isunpaved 1.5 1.1 - switch highway=service switch isunpaved 1.6 1.3 + if ( highway=pedestrian ) then 3 + else if ( highway=bridleway ) then 5 + else if ( highway=cycleway ) then 1 + else if ( highway=residential|living_street ) then ( if isunpaved then 1.5 else 1.1 ) + else if ( highway=service ) then ( if isunpaved then 1.6 else 1.3 ) # # tracks and track-like ways are rated mainly be tracktype/grade # But note that if no tracktype is given (mainly for road/path/footway) # it can be o.k. if there's any other hint for quality # - switch or highway=track or highway=road or highway=path highway=footway - switch tracktype=grade1 switch probablyGood 1.0 1.3 - switch tracktype=grade2 switch probablyGood 1.1 2.0 - switch tracktype=grade3 switch probablyGood 1.5 3.0 - switch tracktype=grade4 switch probablyGood 2.0 5.0 - switch tracktype=grade5 switch probablyGood 3.0 5.0 - switch probablyGood 1.0 5.0 + else if ( highway=track|road|path|footway ) then + ( + if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 ) + else if ( tracktype=grade2 ) then ( if probablyGood then 1.1 else 2.0 ) + else if ( tracktype=grade3 ) then ( if probablyGood then 1.5 else 3.0 ) + else if ( tracktype=grade4 ) then ( if probablyGood then 2.0 else 5.0 ) + else if ( tracktype=grade5 ) then ( if probablyGood then 3.0 else 5.0 ) + else ( if probablyGood then 1.0 else 5.0 ) + ) # # When avoiding unsafe ways, avoid highways without a bike hint # - add switch and avoid_unsafe not isbike 2 0 + else add ( if ( and avoid_unsafe not isbike ) then 2 else 0 ) # # exclude motorways and proposed roads # - switch or highway=motorway highway=motorway_link 100000 - switch or highway=proposed highway=abandoned 100000 + if ( highway=motorway|motorway_link ) then 100000 + else if ( highway=proposed|abandoned ) then 100000 # # actuals roads are o.k. if we have a bike hint # - switch or highway=trunk highway=trunk_link switch isbike 1.5 10 - switch or highway=primary highway=primary_link switch isbike 1.2 3 - switch or highway=secondary highway=secondary_link switch isbike 1.1 1.6 - switch or highway=tertiary highway=tertiary_link switch isbike 1.0 1.4 - switch highway=unclassified switch isbike 1.0 1.3 + else if ( highway=trunk|trunk_link ) then ( if isbike then 1.5 else 10 ) + else if ( highway=primary|primary_link ) then ( if isbike then 1.2 else 3 ) + else if ( highway=secondary|secondary_link ) then ( if isbike then 1.1 else 1.6 ) + else if ( highway=tertiary|tertiary_link ) then ( if isbike then 1.0 else 1.4 ) + else if ( highway=unclassified ) then ( if isbike then 1.0 else 1.3 ) # # default for any other highway type not handled above # - 2.0 + else 2.0 ---context:node # following code refers to node tags -assign defaultaccess - switch access= - 1 # add default barrier restrictions here! - switch or access=private access=no - 0 - 1 +assign defaultaccess = + if ( access= ) then true # add default barrier restrictions here! + else if ( access=private|no ) then false + else true -assign bikeaccess - or nodeaccessgranted=yes - switch bicycle= - switch vehicle= - defaultaccess - switch or vehicle=private vehicle=no - 0 - 1 - switch or bicycle=private or bicycle=no bicycle=dismount - 0 - 1 +assign bikeaccess = + if nodeaccessgranted=yes then true + else if bicycle= then + ( + if vehicle= then defaultaccess + else not vehicle=private|no + ) + else not bicycle=private|no|dismount -assign footaccess - or bicycle=dismount - switch foot= - defaultaccess - switch or foot=private foot=no - 0 - 1 +assign footaccess = + if bicycle=dismount then true + else if foot= then defaultaccess + else not foot=private|no -assign initialcost - switch bikeaccess - 0 - switch footaccess - 100 - 1000000 +assign initialcost = + if bikeaccess then 0 + else ( if footaccess then 100 else 1000000 ) diff --git a/misc/readmes/readme.txt b/misc/readmes/readme.txt index e332b55..3475692 100644 --- a/misc/readmes/readme.txt +++ b/misc/readmes/readme.txt @@ -66,7 +66,7 @@ is usually the one with the most space available. However, on Android 4.4, write access to the external card is restricted, and usually you will not get a proposal to use -the external card. Here you should accespt to go with the +the external card. Here you should accept to go with the internal card - later on you can setup a "secondary" data directory on the external card where you can move the datafiles to.