diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml
index 88ce852..0dab983 100644
--- a/.github/workflows/gradle-publish.yml
+++ b/.github/workflows/gradle-publish.yml
@@ -12,6 +12,7 @@ jobs:
build:
runs-on: ubuntu-latest
+ environment: BRouter
permissions:
contents: read
packages: write
@@ -27,10 +28,19 @@ jobs:
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
+ - name: Setup keystore
+ env:
+ BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
+ run: |
+ echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
- name: Build with Gradle
+ env:
+ ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
+ ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
+ ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
+ ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
run: gradle build
-
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
# the publishing section of your build.gradle
- name: Publish to GitHub Packages
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index f27ddf3..354677c 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -13,7 +13,7 @@ jobs:
build:
runs-on: ubuntu-latest
-
+ environment: BRouter
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
@@ -24,7 +24,17 @@ jobs:
cache: gradle
- name: Create local.properties
run: touch local.properties
+ - name: Setup keystore
+ env:
+ BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
+ run: |
+ echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
- name: Build with Gradle
+ env:
+ ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
+ ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
+ ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
+ ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
run: ./gradlew build
- name: Upload ZIP
uses: actions/upload-artifact@v2
diff --git a/README.md b/README.md
index bef1cfa..b1fcbc2 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ Segments files from the whole planet are generated weekly at
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
You can download one or more segments files, covering the area of the planet
-your want to route, into the `misc/segments4` directory.
+you want to route, into the `misc/segments4` directory.
#### Generate your own segments files
diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
index 8cd8f30..16223a1 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
@@ -15,7 +15,7 @@ public final class MicroCache2 extends MicroCache
private int latBase;
private int cellsize;
- public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
+ public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor )
{
super( databuffer ); // sets ab=databuffer, aboffset=0
@@ -34,7 +34,7 @@ public final class MicroCache2 extends MicroCache
return b;
}
- public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
+ public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher )
{
super( null );
cellsize = 1000000 / divisor;
diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java
index 4a42b0c..edbf738 100644
--- a/brouter-core/src/main/java/btools/router/OsmPath.java
+++ b/brouter-core/src/main/java/btools/router/OsmPath.java
@@ -255,8 +255,8 @@ abstract class OsmPath implements OsmLinkHolder
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
{
if ( rc.inverseDirection
- ? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
- : TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode, rc.carMode ) )
+ ? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode )
+ : TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode ) )
{
cost = -1;
return;
diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java
index fde92a6..b094551 100644
--- a/brouter-core/src/main/java/btools/router/OsmTrack.java
+++ b/brouter-core/src/main/java/btools/router/OsmTrack.java
@@ -33,8 +33,8 @@ import btools.util.StringUtils;
public final class OsmTrack
{
- final public static String version = "1.6.2";
- final public static String versionDate = "10102021";
+ final public static String version = "1.6.3";
+ final public static String versionDate = "21122021";
// csv-header-line
private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
@@ -591,8 +591,10 @@ public final class OsmTrack
if ( turnInstructionMode == 2 )
{
- sb.append( " " ).append( "" + voiceHints.getLocusRouteType() ).append( "\n" );
- sb.append( " 1\n" );
+ sb.append( " \n" );
+ sb.append( " " ).append( "" + voiceHints.getLocusRouteType() ).append( "\n" );
+ sb.append( " 1\n" );
+ sb.append( " \n" );
}
sb.append( " \n" );
diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java
index 2076484..1da65d4 100644
--- a/brouter-core/src/main/java/btools/router/RoutingContext.java
+++ b/brouter-core/src/main/java/btools/router/RoutingContext.java
@@ -147,8 +147,8 @@ public final class RoutingContext
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
- // turn-restrictions used per default for car profiles
- considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", 1.f );
+ // turn-restrictions not used per default for foot profiles
+ considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", footMode ? 0.f : 1.f );
// process tags not used in the profile (to have them in the data-tab)
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
diff --git a/brouter-core/src/main/java/btools/router/SuspectInfo.java b/brouter-core/src/main/java/btools/router/SuspectInfo.java
new file mode 100644
index 0000000..db86592
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/SuspectInfo.java
@@ -0,0 +1,66 @@
+package btools.router;
+
+import java.util.Map;
+
+public class SuspectInfo
+{
+ public static final int TRIGGER_DEAD_END = 1;
+ public static final int TRIGGER_DEAD_START = 2;
+ public static final int TRIGGER_NODE_BLOCK = 4;
+ public static final int TRIGGER_BAD_ACCESS = 8;
+ public static final int TRIGGER_UNK_ACCESS = 16;
+ public static final int TRIGGER_SHARP_EXIT = 32;
+ public static final int TRIGGER_SHARP_ENTRY = 64;
+ public static final int TRIGGER_SHARP_LINK = 128;
+ public static final int TRIGGER_BAD_TR = 256;
+
+ public int prio;
+ public int triggers;
+
+ public static void addSuspect( Map map, long id, int prio, int trigger )
+ {
+ Long iD = Long.valueOf( id );
+ SuspectInfo info = map.get( iD );
+ if ( info == null )
+ {
+ info = new SuspectInfo();
+ map.put( iD, info );
+ }
+ info.prio = Math.max( info.prio, prio );
+ info.triggers |= trigger;
+ }
+
+ public static SuspectInfo addTrigger( SuspectInfo old, int prio, int trigger )
+ {
+ if ( old == null )
+ {
+ old = new SuspectInfo();
+ }
+ old.prio = Math.max( old.prio, prio );
+ old.triggers |= trigger;
+ return old;
+ }
+
+ public static String getTriggerText( int triggers )
+ {
+ StringBuilder sb = new StringBuilder();
+ addText( sb, "dead-end" , triggers, TRIGGER_DEAD_END );
+ addText( sb, "dead-start" , triggers, TRIGGER_DEAD_START );
+ addText( sb, "node-block" , triggers, TRIGGER_NODE_BLOCK );
+ addText( sb, "bad-access" , triggers, TRIGGER_BAD_ACCESS );
+ addText( sb, "unkown-access", triggers, TRIGGER_UNK_ACCESS );
+ addText( sb, "sharp-exit" , triggers, TRIGGER_SHARP_EXIT );
+ addText( sb, "sharp-entry" , triggers, TRIGGER_SHARP_ENTRY );
+ addText( sb, "sharp-link" , triggers, TRIGGER_SHARP_LINK );
+ addText( sb, "bad-tr" , triggers, TRIGGER_BAD_TR );
+ return sb.toString();
+ }
+
+ private static void addText( StringBuilder sb, String text, int mask, int bit )
+ {
+ if ( ( bit & mask ) == 0 ) return;
+ if ( sb.length() > 0 ) sb.append( "," );
+ sb.append( text );
+ }
+
+}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
index 2c7679e..8620e12 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
@@ -622,8 +622,8 @@ public abstract class BExpressionContext implements IByteArrayUnifier
if ( lookupData2 != null )
{
// do not create unknown value for external data array,
- // record as 'other' instead
- lookupData2[inum] = 0;
+ // record as 'unknown' instead
+ lookupData2[inum] = 1; // 1 == unknown
if (bFoundAsterix) {
// found value for lookup *
//System.out.println( "add unknown " + name + " " + value );
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmCutter.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmCutter.java
index 749950c..ab5b050 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmCutter.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmCutter.java
@@ -260,30 +260,11 @@ public class OsmCutter extends MapCreatorBase
@Override
public void nextRestriction( RelationData r, long fromWid, long toWid, long viaNid ) throws Exception
{
- if ( fromWid == 0 || toWid == 0 || viaNid == 0 )
- {
- return;
- }
String type = r.getTag( "type" );
if ( type == null || !"restriction".equals( type ) )
{
return;
}
- String restriction = r.getTag( "restriction" );
- if ( restriction == null )
- {
- return;
- }
- boolean isPositive = true;
- if ( restriction.startsWith( "no_" ) )
- {
- isPositive = false;
- }
- else if ( !restriction.startsWith( "only_" ) )
- {
- return;
- }
-
short exceptions = 0;
String except = r.getTag( "except" );
if ( except != null )
@@ -296,23 +277,31 @@ public class OsmCutter extends MapCreatorBase
exceptions |= toBit( "hgv" , 4, except );
}
- // System.out.println( "restriction id = " + r.rid + " isPositive=" + isPositive + " fromWid = " + fromWid + " toWid = " + toWid+ " viaNid = " + viaNid );
- RestrictionData res = new RestrictionData();
- res.isPositive = isPositive;
- res.exceptions = exceptions;
- res.fromWid = fromWid;
- res.toWid = toWid;
- res.viaNid = viaNid;
-
- if ( restrictionsDos != null )
+ for( String restrictionKey : r.getTagsOrNull().keySet() )
{
- res.writeTo( restrictionsDos );
- }
- if ( restrictionCutter != null )
- {
- restrictionCutter.nextRestriction( res );
- }
+ if ( !( restrictionKey.equals( "restriction" ) || restrictionKey.startsWith( "restriction:" ) ) )
+ {
+ continue;
+ }
+ String restriction = r.getTag( restrictionKey );
+
+ RestrictionData res = new RestrictionData();
+ res.restrictionKey = restrictionKey;
+ res.restriction = restriction;
+ res.exceptions = exceptions;
+ res.fromWid = fromWid;
+ res.toWid = toWid;
+ res.viaNid = viaNid;
+ if ( restrictionsDos != null )
+ {
+ res.writeTo( restrictionsDos );
+ }
+ if ( restrictionCutter != null )
+ {
+ restrictionCutter.nextRestriction( res );
+ }
+ }
}
private static short toBit( String tag, int bitpos, String s )
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java
index 936072f..92b1b54 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java
@@ -175,11 +175,11 @@ public class OsmNodeP extends OsmLinkP
RestrictionData r = getFirstRestriction();
while( r != null )
{
- if ( r.fromLon != 0 && r.toLon != 0 )
+ if ( r.isValid() && r.fromLon != 0 && r.toLon != 0 )
{
mc.writeBoolean( true ); // restriction follows
mc.writeShort( r.exceptions );
- mc.writeBoolean( r.isPositive );
+ mc.writeBoolean( r.isPositive() );
mc.writeInt( r.fromLon );
mc.writeInt( r.fromLat );
mc.writeInt( r.toLon );
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java
index 72ac0d4..50f3130 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java
@@ -1,9 +1,14 @@
package btools.mapcreator;
+import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.TreeSet;
-import btools.util.LongList;
+import btools.util.CheapAngleMeter;
/**
* Container for a turn restriction
@@ -12,26 +17,125 @@ import btools.util.LongList;
*/
public class RestrictionData extends MapCreatorBase
{
- public boolean isPositive;
+ public String restrictionKey;
+ public String restriction;
public short exceptions;
public long fromWid;
public long toWid;
public long viaNid;
public RestrictionData next;
+ public int viaLon;
+ public int viaLat;
+
public int fromLon;
public int fromLat;
public int toLon;
public int toLat;
+
+ public boolean badWayMatch;
+
+ private static HashMap names = new HashMap<>();
+ private static TreeSet badTRs = new TreeSet<>();
public RestrictionData()
{
}
+
+ public boolean isPositive()
+ {
+ return restriction.startsWith( "only_" );
+ }
+
+ public boolean isValid()
+ {
+ boolean valid = fromLon != 0 && toLon != 0 && ( restriction.startsWith( "only_" ) || restriction.startsWith( "no_" ) );
+ if ( (!valid) || badWayMatch || !(checkGeometry()) )
+ {
+ synchronized( badTRs )
+ {
+ badTRs.add( ( (long) viaLon ) << 32 | viaLat );
+ }
+ }
+ return valid && "restriction".equals( restrictionKey );
+ }
+
+ private boolean checkGeometry()
+ {
+ double a = (new CheapAngleMeter()).calcAngle( fromLon, fromLat, viaLon, viaLat, toLon, toLat );
+ String t;
+ if ( restriction.startsWith( "only_" ) )
+ {
+ t = restriction.substring( "only_".length() );
+ }
+ else if ( restriction.startsWith( "no_" ) )
+ {
+ t = restriction.substring( "no_".length() );
+ }
+ else throw new RuntimeException( "ups" );
+
+ if ( restrictionKey.endsWith( ":conditional" ) )
+ {
+ int idx = t.indexOf( '@' );
+ if ( idx >= 0 )
+ {
+ t = t.substring(0, idx ).trim();
+ }
+ }
+
+ if ( "left_turn".equals( t ) )
+ {
+ return a < -5. && a > -175.;
+ }
+ if ( "right_turn".equals( t ) )
+ {
+ return a > 5. && a < 175.;
+ }
+ if ( "straight_on".equals( t ) )
+ {
+ return a > -85. && a < 85.;
+ }
+ if ( "u_turn".equals( t ) )
+ {
+ return a < - 95. || a > 95.;
+ }
+ return "entry".equals( t ) || "exit".equals( t );
+ }
+
+ private static String unifyName( String name )
+ {
+ synchronized( names )
+ {
+ String n = names.get(name);
+ if ( n == null )
+ {
+ names.put( name, name );
+ n = name;
+ }
+ return n;
+ }
+ }
+
+ public static void dumpBadTRs()
+ {
+ try( BufferedWriter bw = new BufferedWriter( new FileWriter( "badtrs.txt" ) ) )
+ {
+ for( Long id : badTRs )
+ {
+ bw.write( "" + id + " 26\n" );
+ }
+ }
+ catch( IOException ioe )
+ {
+ throw new RuntimeException( ioe );
+ }
+ }
public RestrictionData( DataInputStream di ) throws Exception
{
- isPositive = di.readBoolean();
+ restrictionKey = unifyName( di.readUTF() );
+ restriction = unifyName( di.readUTF() );
exceptions = di.readShort();
fromWid = readId( di );
toWid = readId( di );
@@ -40,7 +144,8 @@ public class RestrictionData extends MapCreatorBase
public void writeTo( DataOutputStream dos ) throws Exception
{
- dos.writeBoolean( isPositive );
+ dos.writeUTF( restrictionKey );
+ dos.writeUTF( restriction );
dos.writeShort( exceptions );
writeId( dos, fromWid );
writeId( dos, toWid );
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java
index 99e127c..ffb491d 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java
@@ -138,6 +138,9 @@ public class WayLinker extends MapCreatorBase implements Runnable
new WayLinker().process( new File( args[0] ), new File( args[1] ), new File( args[2] ), new File( args[3] ), new File( args[4] ), new File( args[5] ), new File(
args[6] ), args[7] );
+
+ System.out.println( "dumping bad TRs" );
+ RestrictionData.dumpBadTRs();
}
public void process( File nodeTilesIn, File wayTilesIn, File borderFileIn, File restrictionsFileIn, File lookupFile, File profileFile, File dataTilesOut,
@@ -287,6 +290,8 @@ public class WayLinker extends MapCreatorBase implements Runnable
nodesMap.put( res.viaNid, n );
}
OsmNodePT nt = (OsmNodePT) n;
+ res.viaLon = nt.ilon;
+ res.viaLat = nt.ilat;
res.next = nt.firstRestriction;
nt.firstRestriction = res;
ntr++;
@@ -351,36 +356,49 @@ public class WayLinker extends MapCreatorBase implements Runnable
// the leg according to the mapped direction
private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w )
{
- checkRestriction( n1, n2, w.wid, true );
- checkRestriction( n2, n1, w.wid, false );
+ checkRestriction( n1, n2, w, true );
+ checkRestriction( n2, n1, w, false );
}
- private void checkRestriction( OsmNodeP n1, OsmNodeP n2, long wid, boolean checkFrom )
+ private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w, boolean checkFrom )
{
RestrictionData r = n2.getFirstRestriction();
while ( r != null )
{
- if ( r.fromWid == wid )
+ if ( r.fromWid == w.wid )
{
if ( r.fromLon == 0 || checkFrom )
{
r.fromLon = n1.ilon;
r.fromLat = n1.ilat;
n1.bits |= OsmNodeP.DP_SURVIVOR_BIT;
+ if ( !isEndNode( n2, w ) )
+ {
+ r.badWayMatch = true;
+ }
}
}
- if ( r.toWid == wid )
+ if ( r.toWid == w.wid )
{
if ( r.toLon == 0 || !checkFrom )
{
r.toLon = n1.ilon;
r.toLat = n1.ilat;
n1.bits |= OsmNodeP.DP_SURVIVOR_BIT;
+ if ( !isEndNode( n2, w ) )
+ {
+ r.badWayMatch = true;
+ }
}
}
r = r.next;
}
}
+
+ private boolean isEndNode( OsmNodeP n, WayData w )
+ {
+ return n == nodesMap.get( w.nodes.get( 0 ) ) || n == nodesMap.get( w.nodes.get( w.nodes.size() - 1 ) );
+ }
@Override
public void nextWay( WayData way ) throws Exception
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
index ed25fd8..dc7191e 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
@@ -20,7 +20,7 @@ public final class DirectWeaver extends ByteDataWriter
private int size = 0;
- public DirectWeaver( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes ) throws Exception
+ public DirectWeaver( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes )
{
super( null );
int cellsize = 1000000 / divisor;
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
index bf03490..f2c481b 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
@@ -35,7 +35,7 @@ final class OsmFile
private int ncaches;
private int indexsize;
- public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws Exception
+ public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws IOException
{
this.lonDegree = lonDegree;
this.latDegree = latDegree;
@@ -111,7 +111,7 @@ final class OsmFile
return idx == -1 ? indexsize : posIdx[idx];
}
- public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws Exception
+ public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws IOException
{
int startPos = getPosIdx( subIdx - 1 );
int endPos = getPosIdx( subIdx );
@@ -128,7 +128,7 @@ final class OsmFile
}
public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator,
- WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes ) throws Exception
+ WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes ) throws IOException
{
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
index 1c378e0..1798fd3 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
@@ -31,13 +31,12 @@ final public class PhysicalFile
{
MicroCache.debug = true;
- String message = checkFileIntegrity( new File( args[0] ) );
-
- if ( message != null )
- {
- System.out.println( "************************************" );
- System.out.println( message );
- System.out.println( "************************************" );
+ try {
+ checkFileIntegrity( new File( args[0] ) );
+ } catch (IOException e) {
+ System.err.println( "************************************" );
+ e.printStackTrace();
+ System.err.println( "************************************" );
}
}
@@ -46,7 +45,7 @@ final public class PhysicalFile
*
* @return the error message if file corrupt, else null
*/
- public static String checkFileIntegrity( File f )
+ public static String checkFileIntegrity( File f ) throws IOException
{
PhysicalFile pf = null;
try
@@ -66,14 +65,6 @@ final public class PhysicalFile
}
}
}
- catch (IllegalArgumentException iae)
- {
- return iae.getMessage();
- }
- catch (Exception e)
- {
- return e.toString();
- }
finally
{
if ( pf != null )
@@ -88,7 +79,7 @@ final public class PhysicalFile
return null;
}
- public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws Exception
+ public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws IOException
{
fileName = f.getName();
byte[] iobuffer = dataBuffers.iobuffer;
@@ -102,7 +93,7 @@ final public class PhysicalFile
short readVersion = (short)(lv >> 48);
if ( i == 0 && lookupVersion != -1 && readVersion != lookupVersion )
{
- throw new IllegalArgumentException( "lookup version mismatch (old rd5?) lookups.dat="
+ throw new IOException( "lookup version mismatch (old rd5?) lookups.dat="
+ lookupVersion + " " + f. getAbsolutePath() + "=" + readVersion );
}
fileIndex[i] = lv & 0xffffffffffffL;
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
index 86ff392..3b33904 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
@@ -8,8 +8,10 @@ package btools.mapaccess;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
final public class Rd5DiffManager
{
@@ -93,30 +95,31 @@ final public class Rd5DiffManager
}
}
- public static String getMD5( File f ) throws Exception
+ public static String getMD5( File f ) throws IOException
{
- MessageDigest md = MessageDigest.getInstance("MD5");
- BufferedInputStream bis = new BufferedInputStream( new FileInputStream( f ) );
- DigestInputStream dis = new DigestInputStream(bis, md);
- byte[] buf = new byte[8192];
- for(;;)
- {
- int len = dis.read( buf );
- if ( len <= 0 )
- {
- break;
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
+ DigestInputStream dis = new DigestInputStream(bis, md);
+ byte[] buf = new byte[8192];
+ for (; ; ) {
+ int len = dis.read(buf);
+ if (len <= 0) {
+ break;
+ }
}
- }
- dis.close();
- byte[] bytes = md.digest();
+ dis.close();
+ byte[] bytes = md.digest();
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < bytes.length; j++)
- {
- int v = bytes[j] & 0xff;
- sb.append( hexChar( v >>> 4 ) ).append( hexChar( v & 0xf ) );
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xff;
+ sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
+ }
+ return sb.toString();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("MD5 algorithm not available", e);
}
- return sb.toString();
}
private static char hexChar( int v )
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
index ac2f1d9..c5ffbf1 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
@@ -12,6 +12,7 @@ import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.Arrays;
import btools.codec.DataBuffers;
@@ -49,9 +50,8 @@ final public class Rd5DiffTool implements ProgressListener
}
@Override
- public void updateProgress( String progress )
- {
- System.out.println( progress );
+ public void updateProgress(String task, int progress) {
+ System.out.println(task + ": " + progress + "%");
}
@Override
@@ -60,7 +60,7 @@ final public class Rd5DiffTool implements ProgressListener
return false;
}
- private static long[] readFileIndex( DataInputStream dis, DataOutputStream dos ) throws Exception
+ private static long[] readFileIndex( DataInputStream dis, DataOutputStream dos ) throws IOException
{
long[] fileIndex = new long[25];
for( int i=0; i<25; i++ )
@@ -85,7 +85,7 @@ final public class Rd5DiffTool implements ProgressListener
return index[tileIndex];
}
- private static int[] readPosIndex( DataInputStream dis, DataOutputStream dos ) throws Exception
+ private static int[] readPosIndex( DataInputStream dis, DataOutputStream dos ) throws IOException
{
int[] posIndex = new int[1024];
for( int i=0; i<1024; i++ )
@@ -105,7 +105,7 @@ final public class Rd5DiffTool implements ProgressListener
return idx == -1 ? 4096 : posIdx[idx];
}
- private static byte[] createMicroCache( int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode ) throws Exception
+ private static byte[] createMicroCache( int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode ) throws IOException
{
if ( posIdx == null )
{
@@ -125,7 +125,7 @@ final public class Rd5DiffTool implements ProgressListener
return ab;
}
- private static MicroCache createMicroCache( byte[] ab, DataBuffers dataBuffers ) throws Exception
+ private static MicroCache createMicroCache( byte[] ab, DataBuffers dataBuffers )
{
if ( ab == null || ab.length == 0 )
{
@@ -286,7 +286,7 @@ final public class Rd5DiffTool implements ProgressListener
}
- public static void recoverFromDelta( File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */ ) throws Exception
+ public static void recoverFromDelta( File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */ ) throws IOException
{
if ( f2.length() == 0L )
{
@@ -341,7 +341,7 @@ final public class Rd5DiffTool implements ProgressListener
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
if ( pct != lastPct )
{
- progress.updateProgress( "Applying delta: " + pct + "%" );
+ progress.updateProgress("Applying delta", pct);
lastPct = pct;
}
@@ -468,7 +468,7 @@ final public class Rd5DiffTool implements ProgressListener
}
}
- public static void copyFile( File f1, File outFile, ProgressListener progress ) throws Exception
+ public static void copyFile( File f1, File outFile, ProgressListener progress ) throws IOException
{
boolean canceled = false;
DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) );
@@ -489,7 +489,7 @@ final public class Rd5DiffTool implements ProgressListener
int pct = (int)( (100. * sizeRead) / (sizeTotal+1) + 0.5 );
if ( pct != lastPct )
{
- progress.updateProgress( "Copying: " + pct + "%" );
+ progress.updateProgress("Copying", pct);
lastPct = pct;
}
int len = dis1.read( buf );
@@ -756,7 +756,7 @@ final public class Rd5DiffTool implements ProgressListener
this.dataBuffers = dataBuffers;
}
- public MicroCache readMC() throws Exception
+ public MicroCache readMC() throws IOException
{
if (skips < 0 )
{
@@ -775,7 +775,7 @@ final public class Rd5DiffTool implements ProgressListener
return mc;
}
- public void finish() throws Exception
+ public void finish()
{
skips = -1;
}
diff --git a/brouter-routing-app/build.gradle b/brouter-routing-app/build.gradle
index dee8277..aa4c375 100644
--- a/brouter-routing-app/build.gradle
+++ b/brouter-routing-app/build.gradle
@@ -5,23 +5,25 @@ plugins {
}
android {
- compileSdkVersion 30
+ compileSdkVersion 31
defaultConfig {
applicationId "btools.routingapp"
- versionCode 42
+ versionCode 45
versionName project.version
resValue('string', 'app_version', defaultConfig.versionName)
setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName)
minSdkVersion 14
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets.main.assets.srcDirs += new File(project.buildDir, 'assets')
- if (project.hasProperty("RELEASE_STORE_FILE")) {
+ if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
signingConfigs {
// this uses a file ~/.gradle/gradle.properties
// with content:
@@ -49,7 +51,7 @@ android {
release {
minifyEnabled false
debuggable false
- if (project.hasProperty("RELEASE_STORE_FILE")) {
+ if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
signingConfig signingConfigs.release
}
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -96,14 +98,21 @@ android {
}
dependencies {
-
- implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation "androidx.constraintlayout:constraintlayout:2.1.3"
+ implementation 'androidx.work:work-runtime:2.7.1'
+ implementation 'com.google.android.material:material:1.5.0'
implementation project(':brouter-mapaccess')
implementation project(':brouter-core')
implementation project(':brouter-expressions')
implementation project(':brouter-util')
+ testImplementation 'junit:junit:4.13.2'
+
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation 'androidx.work:work-testing:2.7.1'
}
task generateProfiles(type: Exec) {
diff --git a/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java b/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java
new file mode 100644
index 0000000..6a6a9cd
--- /dev/null
+++ b/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java
@@ -0,0 +1,47 @@
+package btools.routingapp;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+import android.os.Build;
+import android.os.Environment;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class BRouterActivityTest {
+ @Rule
+ public ActivityScenarioRule rule = new ActivityScenarioRule<>(BRouterActivity.class);
+
+ @Test
+ public void storageDirectories() {
+ ActivityScenario scenario = rule.getScenario();
+ scenario.onActivity(activity -> {
+ List storageDirectories = activity.getStorageDirectories();
+
+ // Before Android Q (10) legacy storage access is possible
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ assertThat(storageDirectories, hasItem(Environment.getExternalStorageDirectory()));
+ }
+
+ // When targeting older SDK we can access legacy storage on any android version
+ if (activity.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.Q) {
+ assertThat(storageDirectories, hasItem(Environment.getExternalStorageDirectory()));
+ }
+
+ assertThat(storageDirectories, not(empty()));
+ });
+ }
+
+}
diff --git a/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java
new file mode 100644
index 0000000..a9004c0
--- /dev/null
+++ b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java
@@ -0,0 +1,71 @@
+package btools.routingapp;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.work.Data;
+import androidx.work.ListenableWorker.Result;
+import androidx.work.testing.TestWorkerBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadWorkerTest {
+ private Context context;
+ private Executor executor;
+
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void testDownloadNewFile() {
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, new String[]{"E105_N50"})
+ .build();
+
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .setInputData(inputData)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.success()));
+ }
+
+ @Test
+ public void testDownloadInvalidSegment() {
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, new String[]{"X00"})
+ .build();
+
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .setInputData(inputData)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.failure()));
+ }
+
+ @Test
+ public void testDownloadNoSegments() {
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.failure()));
+ }
+}
diff --git a/brouter-routing-app/src/main/AndroidManifest.xml b/brouter-routing-app/src/main/AndroidManifest.xml
index c1ffbdf..76f6b51 100644
--- a/brouter-routing-app/src/main/AndroidManifest.xml
+++ b/brouter-routing-app/src/main/AndroidManifest.xml
@@ -3,7 +3,9 @@
-
@@ -11,17 +13,16 @@
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:preserveLegacyExternalStorage="true"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:theme="@style/Theme.App">
+ android:screenOrientation="unspecified">
@@ -29,20 +30,18 @@
+ android:launchMode="singleTask"
+ android:screenOrientation="landscape" />
-
+ android:label="Import Profile">
+
+
@@ -88,15 +87,10 @@
-
diff --git a/brouter-routing-app/src/main/assets/segments4.zip b/brouter-routing-app/src/main/assets/segments4.zip
index 310b75f..515c7ff 100644
Binary files a/brouter-routing-app/src/main/assets/segments4.zip and b/brouter-routing-app/src/main/assets/segments4.zip differ
diff --git a/brouter-routing-app/src/main/assets/serverconfig.txt b/brouter-routing-app/src/main/assets/serverconfig.txt
index 9128b11..dab2409 100644
--- a/brouter-routing-app/src/main/assets/serverconfig.txt
+++ b/brouter-routing-app/src/main/assets/serverconfig.txt
@@ -13,4 +13,4 @@ profiles_url=https://brouter.de/brouter/profiles2/
# these are comma separated arrays
check_lookup=lookups.dat
-check_profiles=car-eco.brf,car-eco-de.brf,car-eco-suspect_scan.brf,car-fast.brf,fastbike.brf,fastbike-asia-pacific.brf,fastbike-lowtraffic.brf,fastbike-verylowtraffic.brf,hiking-beta.brf,moped.brf,rail.brf,river.brf,safety.brf,shortest.brf,trekking.brf,trekking-ignore-cr.brf,trekking-noferries.brf,trekking-nosteps.brf,trekking-steep.brf,vm-forum-liegerad-schnell.brf,vm-forum-velomobil-schnell.brf
\ No newline at end of file
+check_profiles=car-eco.brf,car-eco-de.brf,car-fast.brf,fastbike.brf,fastbike-asia-pacific.brf,fastbike-lowtraffic.brf,fastbike-verylowtraffic.brf,hiking-mountain.brf,moped.brf,rail.brf,river.brf,safety.brf,shortest.brf,trekking.brf,trekking-ignore-cr.brf,trekking-noferries.brf,trekking-nosteps.brf,trekking-steep.brf,vm-forum-liegerad-schnell.brf,vm-forum-velomobil-schnell.brf
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
index 3d5f085..2550bf9 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
@@ -1,6 +1,5 @@
package btools.routingapp;
-import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -12,6 +11,7 @@ import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedReader;
import java.io.File;
@@ -20,7 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-public class BImportActivity extends Activity {
+public class BImportActivity extends AppCompatActivity {
// profile size is generally < 30 kb, so set max size to 100 kb
private static final int MAX_PROFILE_SIZE = 100000;
private EditText mTextFilename;
@@ -76,8 +76,8 @@ public class BImportActivity extends Activity {
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
- filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
- filesize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
+ filename = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
+ filesize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
}
}
// is the file extention ".brf" in the file name
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
index 9fdbc76..c12ba62 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
@@ -1,112 +1,208 @@
package btools.routingapp;
-import java.util.HashSet;
-import java.util.Set;
+import static btools.routingapp.BInstallerView.MASK_CURRENT_RD5;
+import static btools.routingapp.BInstallerView.MASK_DELETED_RD5;
+import static btools.routingapp.BInstallerView.MASK_INSTALLED_RD5;
+import static btools.routingapp.BInstallerView.MASK_SELECTED_RD5;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.speech.tts.TextToSpeech.OnInitListener;
import android.os.StatFs;
-import android.util.Log;
+import android.text.format.Formatter;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
-public class BInstallerActivity extends Activity {
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.work.Constraints;
+import androidx.work.Data;
+import androidx.work.NetworkType;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkInfo;
+import androidx.work.WorkManager;
+import androidx.work.WorkRequest;
- public static final String DOWNLOAD_ACTION = "btools.routingapp.download";
+import com.google.android.material.progressindicator.LinearProgressIndicator;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import btools.router.RoutingHelper;
+
+public class BInstallerActivity extends AppCompatActivity {
private static final int DIALOG_CONFIRM_DELETE_ID = 1;
-
+ public static boolean downloadCanceled = false;
+ private File mBaseDir;
private BInstallerView mBInstallerView;
- private PowerManager mPowerManager;
- private WakeLock mWakeLock;
- private DownloadReceiver myReceiver;
+ private Button mButtonDownload;
+ private TextView mSummaryInfo;
+ private LinearProgressIndicator mProgressIndicator;
+ public static long getAvailableSpace(String baseDir) {
+ StatFs stat = new StatFs(baseDir);
- public class DownloadReceiver extends BroadcastReceiver {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
+ } else {
+ //noinspection deprecation
+ return (long) stat.getAvailableBlocks() * stat.getBlockSize();
+ }
+ }
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.hasExtra("txt")) {
- String txt = intent.getStringExtra("txt");
- boolean ready = intent.getBooleanExtra("ready", false);
- mBInstallerView.setState(txt, ready);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+ setContentView(R.layout.activity_binstaller);
+ mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
+ mBInstallerView = findViewById(R.id.BInstallerView);
+ mBInstallerView.setOnSelectListener(
+ () -> {
+ updateDownloadButton();
+ }
+ );
+ mButtonDownload = findViewById(R.id.buttonDownload);
+ mButtonDownload.setOnClickListener(
+ view -> {
+ if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) {
+ showConfirmDelete();
+ } else if (mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5).size() > 0) {
+ downloadSelectedTiles();
+ } else {
+ downloadInstalledTiles();
+ }
+ }
+ );
+ mProgressIndicator = findViewById(R.id.progressDownload);
+
+ mBaseDir = ConfigHelper.getBaseDir(this);
+ scanExistingFiles();
+ }
+
+ private String getSegmentsPlural(int count) {
+ Resources res = getResources();
+ return res.getQuantityString(R.plurals.numberOfSegments, count, count);
+ }
+
+ private void updateDownloadButton() {
+ final ArrayList selectedTilesDownload = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
+ final ArrayList selectedTilesUpdate = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
+ final ArrayList selectedTilesDelete = mBInstallerView.getSelectedTiles(MASK_DELETED_RD5);
+ mSummaryInfo.setText("");
+
+ if (selectedTilesDelete.size() > 0) {
+ mButtonDownload.setText(getString(R.string.action_delete, getSegmentsPlural(selectedTilesDelete.size())));
+ mButtonDownload.setEnabled(true);
+ } else if (selectedTilesDownload.size() > 0) {
+ long tileSize = 0;
+ for (int tileIndex : selectedTilesDownload) {
+ tileSize += BInstallerSizes.getRd5Size(tileIndex);
+ }
+ mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size())));
+ mButtonDownload.setEnabled(true);
+ mSummaryInfo.setText(getString(R.string.summary_segments, Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath()))));
+ } else if (selectedTilesUpdate.size() > 0) {
+ mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size())));
+ mButtonDownload.setEnabled(true);
+ } else {
+ mButtonDownload.setText(getString(R.string.action_select));
+ mButtonDownload.setEnabled(false);
+ }
+ }
+
+ private void deleteRawTracks() {
+ File modeDir = new File(mBaseDir, "brouter/modes");
+ String[] fileNames = modeDir.list();
+ if (fileNames == null) return;
+ for (String fileName : fileNames) {
+ if (fileName.endsWith("_rawtrack.dat")) {
+ File f = new File(modeDir, fileName);
+ f.delete();
}
}
}
+ public void downloadAll(ArrayList downloadList) {
+ ArrayList urlparts = new ArrayList<>();
+ for (Integer i : downloadList) {
+ urlparts.add(baseNameForTile(i));
+ }
- /**
- * Called when the activity is first created.
- */
- @Override
- @SuppressWarnings("deprecation")
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ downloadCanceled = false;
- // Get an instance of the PowerManager
- mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0]))
+ .build();
- // Create a bright wake lock
- mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
- .getName());
+ Constraints constraints = new Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build();
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ WorkRequest downloadWorkRequest =
+ new OneTimeWorkRequest.Builder(DownloadWorker.class)
+ .setInputData(inputData)
+ .setConstraints(constraints)
+ .build();
- // instantiate our simulation view and set it as the activity's content
- mBInstallerView = new BInstallerView(this);
- setContentView(mBInstallerView);
+ WorkManager workManager = WorkManager.getInstance(getApplicationContext());
+ workManager.enqueue(downloadWorkRequest);
+
+ workManager
+ .getWorkInfoByIdLiveData(downloadWorkRequest.getId())
+ .observe(this, workInfo -> {
+ if (workInfo != null) {
+ if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
+ Toast.makeText(this, "Download scheduled. Check internet connection if it doesn't start.", Toast.LENGTH_LONG).show();
+ mProgressIndicator.hide();
+ mProgressIndicator.setIndeterminate(true);
+ mProgressIndicator.show();
+ }
+
+ if (workInfo.getState() == WorkInfo.State.RUNNING) {
+ Data progress = workInfo.getProgress();
+ String segmentName = progress.getString(DownloadWorker.PROGRESS_SEGMENT_NAME);
+ int percent = progress.getInt(DownloadWorker.PROGRESS_SEGMENT_PERCENT, 0);
+ if (percent > 0) {
+ mProgressIndicator.setIndeterminate(false);
+ }
+ mProgressIndicator.setProgress(percent);
+ }
+
+ if (workInfo.getState().isFinished()) {
+ String result;
+ switch (workInfo.getState()) {
+ case FAILED:
+ result = "failed.";
+ break;
+ case CANCELLED:
+ result = "cancelled";
+ break;
+ case SUCCEEDED:
+ result = "succeeded";
+ break;
+ default:
+ result = "";
+ }
+ Toast.makeText(this, "Download " + result + ".", Toast.LENGTH_SHORT).show();
+ mProgressIndicator.hide();
+ scanExistingFiles();
+ }
+ }
+ });
+
+ deleteRawTracks(); // invalidate raw-tracks after data update
}
@Override
- protected void onResume() {
- super.onResume();
- /*
- * when the activity is resumed, we acquire a wake-lock so that the
- * screen stays on, since the user will likely not be fiddling with the
- * screen or buttons.
- */
- mWakeLock.acquire();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(DOWNLOAD_ACTION);
-
- myReceiver = new DownloadReceiver();
- registerReceiver(myReceiver, filter);
-
- // Start the download manager
- mBInstallerView.startInstaller();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
-
- super.onPause();
-
- mWakeLock.release();
-
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (myReceiver != null) unregisterReceiver(myReceiver);
- System.exit(0);
- }
-
- @Override
- @SuppressWarnings("deprecation")
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder;
switch (id) {
@@ -116,7 +212,7 @@ public class BInstallerActivity extends Activity {
.setTitle("Confirm Delete")
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- mBInstallerView.deleteSelectedTiles();
+ deleteSelectedTiles();
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
@@ -129,29 +225,76 @@ public class BInstallerActivity extends Activity {
}
}
- @SuppressWarnings("deprecation")
public void showConfirmDelete() {
showDialog(DIALOG_CONFIRM_DELETE_ID);
}
- private Set dialogIds = new HashSet();
+ private void scanExistingFiles() {
+ mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5);
- private void showNewDialog(int id) {
- if (dialogIds.contains(Integer.valueOf(id))) {
- removeDialog(id);
+ scanExistingFiles(new File(mBaseDir, "brouter/segments4"));
+
+ File secondary = RoutingHelper.getSecondarySegmentDir(new File(mBaseDir, "brouter/segments4"));
+ if (secondary != null) {
+ scanExistingFiles(secondary);
}
- dialogIds.add(Integer.valueOf(id));
- showDialog(id);
}
+ private void scanExistingFiles(File dir) {
+ String[] fileNames = dir.list();
+ if (fileNames == null) return;
+ String suffix = ".rd5";
+ for (String fileName : fileNames) {
+ if (fileName.endsWith(suffix)) {
+ String basename = fileName.substring(0, fileName.length() - suffix.length());
+ int tileIndex = tileForBaseName(basename);
+ mBInstallerView.setTileStatus(tileIndex, MASK_INSTALLED_RD5);
- static public long getAvailableSpace(String baseDir) {
- StatFs stat = new StatFs(baseDir);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
- } else {
- return stat.getAvailableBlocks() * stat.getBlockSize();
+ long age = System.currentTimeMillis() - new File(dir, fileName).lastModified();
+ if (age < 10800000) mBInstallerView.setTileStatus(tileIndex, MASK_CURRENT_RD5); // 3 hours
+ }
}
}
+
+ private void deleteSelectedTiles() {
+ ArrayList selectedTiles = mBInstallerView.getSelectedTiles(MASK_DELETED_RD5);
+ for (int tileIndex : selectedTiles) {
+ new File(mBaseDir, "brouter/segments4/" + baseNameForTile(tileIndex) + ".rd5").delete();
+ }
+ scanExistingFiles();
+ }
+
+ private void downloadSelectedTiles() {
+ ArrayList selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
+ downloadAll(selectedTiles);
+ mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5);
+ }
+
+ private void downloadInstalledTiles() {
+ ArrayList selectedTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
+ downloadAll(selectedTiles);
+ }
+
+ private int tileForBaseName(String basename) {
+ String uname = basename.toUpperCase(Locale.ROOT);
+ int idx = uname.indexOf("_");
+ if (idx < 0) return -1;
+ String slon = uname.substring(0, idx);
+ String slat = uname.substring(idx + 1);
+ int ilon = slon.charAt(0) == 'W' ? -Integer.parseInt(slon.substring(1)) :
+ (slon.charAt(0) == 'E' ? Integer.parseInt(slon.substring(1)) : -1);
+ int ilat = slat.charAt(0) == 'S' ? -Integer.parseInt(slat.substring(1)) :
+ (slat.charAt(0) == 'N' ? Integer.parseInt(slat.substring(1)) : -1);
+ if (ilon < -180 || ilon >= 180 || ilon % 5 != 0) return -1;
+ if (ilat < -90 || ilat >= 90 || ilat % 5 != 0) return -1;
+ return (ilon + 180) / 5 + 72 * ((ilat + 90) / 5);
+ }
+
+ private String baseNameForTile(int tileIndex) {
+ int lon = (tileIndex % 72) * 5 - 180;
+ int lat = (tileIndex / 72) * 5 - 90;
+ String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
+ String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
+ return slon + "_" + slat;
+ }
}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
index f262cfc..ffe75c2 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
@@ -1,18 +1,7 @@
package btools.routingapp;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Locale;
-
-import android.app.Activity;
+import android.annotation.SuppressLint;
import android.content.Context;
-import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -20,262 +9,37 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
-import android.os.AsyncTask;
-import android.os.PowerManager;
-import android.os.StatFs;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
+import android.view.GestureDetector;
import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
import android.view.View;
-import android.widget.Toast;
-import btools.mapaccess.PhysicalFile;
-import btools.mapaccess.Rd5DiffManager;
-import btools.mapaccess.Rd5DiffTool;
-import btools.router.RoutingHelper;
-import btools.util.ProgressListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
public class BInstallerView extends View {
- private static final int MASK_SELECTED_RD5 = 1;
- private static final int MASK_DELETED_RD5 = 2;
- private static final int MASK_INSTALLED_RD5 = 4;
- private static final int MASK_CURRENT_RD5 = 8;
-
- private int imgwOrig;
- private int imghOrig;
- private float scaleOrig;
-
- private int imgw;
- private int imgh;
-
- private float lastDownX;
- private float lastDownY;
-
- private Bitmap bmp;
-
+ public static final int MASK_SELECTED_RD5 = 1;
+ public static final int MASK_DELETED_RD5 = 2;
+ public static final int MASK_INSTALLED_RD5 = 4;
+ public static final int MASK_CURRENT_RD5 = 8;
+ private static final float SCALE_GRID_VISIBLE = 3;
+ private final Bitmap bmp;
+ private final float[] testVector = new float[2];
+ private final int[] tileStatus;
+ private final Matrix mat;
+ private final GestureDetector mGestureDetector;
+ private final ScaleGestureDetector mScaleGestureDetector;
+ Paint paintGrid = new Paint();
+ Paint paintTiles = new Paint();
private float viewscale;
-
- private float[] testVector = new float[2];
-
- private int[] tileStatus;
-
private boolean tilesVisible = false;
+ private OnSelectListener mOnSelectListener;
- private long availableSize;
- private File baseDir;
- private File segmentDir;
+ public BInstallerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
- private boolean isDownloading = false;
- public static boolean downloadCanceled = false;
-
- private long currentDownloadSize;
- private String currentDownloadFile = "";
- private volatile String currentDownloadOperation = "";
- private String downloadAction = "";
- private volatile String newDownloadAction = "";
-
- private long totalSize = 0;
- private long rd5Tiles = 0;
- private long delTiles = 0;
-
- Paint pnt_1 = new Paint();
- Paint pnt_2 = new Paint();
- Paint paint = new Paint();
-
- Activity mActivity;
-
-
- protected String baseNameForTile(int tileIndex) {
- int lon = (tileIndex % 72) * 5 - 180;
- int lat = (tileIndex / 72) * 5 - 90;
- String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
- String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
- return slon + "_" + slat;
- }
-
- private int gridPos2Tileindex(int ix, int iy) {
- return (35 - iy) * 72 + (ix >= 70 ? ix - 70 : ix + 2);
- }
-
- private int tileForBaseName(String basename) {
- String uname = basename.toUpperCase(Locale.ROOT);
- int idx = uname.indexOf("_");
- if (idx < 0) return -1;
- String slon = uname.substring(0, idx);
- String slat = uname.substring(idx + 1);
- int ilon = slon.charAt(0) == 'W' ? -Integer.parseInt(slon.substring(1)) :
- (slon.charAt(0) == 'E' ? Integer.parseInt(slon.substring(1)) : -1);
- int ilat = slat.charAt(0) == 'S' ? -Integer.parseInt(slat.substring(1)) :
- (slat.charAt(0) == 'N' ? Integer.parseInt(slat.substring(1)) : -1);
- if (ilon < -180 || ilon >= 180 || ilon % 5 != 0) return -1;
- if (ilat < -90 || ilat >= 90 || ilat % 5 != 0) return -1;
- return (ilon + 180) / 5 + 72 * ((ilat + 90) / 5);
- }
-
-
- public boolean isDownloadCanceled() {
- return downloadCanceled;
- }
-
- private void toggleDownload() {
- if (isDownloading) {
- downloadCanceled = true;
- downloadAction = "Canceling...";
- return;
- }
-
- if (delTiles > 0) {
- ((BInstallerActivity) getContext()).showConfirmDelete();
- return;
- }
-
- int tidx_min = -1;
- int min_size = Integer.MAX_VALUE;
-
- ArrayList downloadList = new ArrayList<>();
- // prepare download list
- for (int ix = 0; ix < 72; ix++) {
- for (int iy = 0; iy < 36; iy++) {
- int tidx = gridPos2Tileindex(ix, iy);
- if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
- int tilesize = BInstallerSizes.getRd5Size(tidx);
- downloadList.add(tidx);
- if (tilesize > 0 && tilesize < min_size) {
- tidx_min = tidx;
- min_size = tilesize;
- }
- }
- }
- }
-
- if (downloadList.size() > 0) {
- isDownloading = true;
- downloadAll(downloadList);
- for (Integer i : downloadList) {
- tileStatus[i.intValue()] ^= tileStatus[i.intValue()] & MASK_SELECTED_RD5;
- }
- downloadList.clear();
- }
- }
-
- private void downloadAll(ArrayList downloadList) {
- ArrayList urlparts = new ArrayList<>();
- for (Integer i : downloadList) {
- urlparts.add(baseNameForTile(i.intValue()));
- }
-
- currentDownloadOperation = "Start download ...";
- downloadAction = "";
- downloadCanceled = false;
- isDownloading = true;
-
- //final DownloadBackground downloadTask = new DownloadBackground(getContext(), urlparts, baseDir);
- //downloadTask.execute( );
- Intent intent = new Intent(mActivity, DownloadService.class);
- intent.putExtra("dir", baseDir.getAbsolutePath() + "/brouter/");
- intent.putExtra("urlparts", urlparts);
- mActivity.startService(intent);
-
- deleteRawTracks(); // invalidate raw-tracks after data update
- }
-
-
- public void downloadDone(boolean success) {
- isDownloading = false;
- if (success) {
- scanExistingFiles();
- toggleDownload(); // keep on if no error
- }
- invalidate();
- }
-
- public void setState(String txt, boolean b) {
- currentDownloadOperation = txt;
- downloadAction = "";
- isDownloading = b;
- if (!b) {
- scanExistingFiles();
- }
- invalidate();
- }
-
- private int tileIndex(float x, float y) {
- int ix = (int) (72.f * x / bmp.getWidth());
- int iy = (int) (36.f * y / bmp.getHeight());
- if (ix >= 0 && ix < 72 && iy >= 0 && iy < 36) return gridPos2Tileindex(ix, iy);
- return -1;
- }
-
- private void clearTileSelection(int mask) {
- // clear selection if zooming out
- for (int ix = 0; ix < 72; ix++)
- for (int iy = 0; iy < 36; iy++) {
- int tidx = gridPos2Tileindex(ix, iy);
- tileStatus[tidx] ^= tileStatus[tidx] & mask;
- }
- }
-
- // get back the current image scale
- private float currentScale() {
- testVector[1] = 1.f;
- mat.mapVectors(testVector);
- return testVector[1] / viewscale;
- }
-
- private void deleteRawTracks() {
- File modeDir = new File(baseDir, "brouter/modes");
- String[] fileNames = modeDir.list();
- if (fileNames == null) return;
- for (String fileName : fileNames) {
- if (fileName.endsWith("_rawtrack.dat")) {
- File f = new File(modeDir, fileName);
- f.delete();
- }
- }
- }
-
- private void scanExistingFiles() {
- clearTileSelection(MASK_INSTALLED_RD5 | MASK_CURRENT_RD5);
-
- scanExistingFiles(new File(baseDir, "brouter/segments4"));
-
- File secondary = RoutingHelper.getSecondarySegmentDir(new File(baseDir, "brouter/segments4"));
- if (secondary != null) {
- scanExistingFiles(secondary);
- }
-
- availableSize = -1;
- try {
- availableSize = (long) ((BInstallerActivity) getContext()).getAvailableSpace(baseDir.getAbsolutePath());
- //StatFs stat = new StatFs(baseDir.getAbsolutePath ());
- //availableSize = (long)stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
- } catch (Exception e) { /* ignore */ }
- }
-
- private void scanExistingFiles(File dir) {
- String[] fileNames = dir.list();
- if (fileNames == null) return;
- String suffix = ".rd5";
- for (String fileName : fileNames) {
- if (fileName.endsWith(suffix)) {
- String basename = fileName.substring(0, fileName.length() - suffix.length());
- int tidx = tileForBaseName(basename);
- tileStatus[tidx] |= MASK_INSTALLED_RD5;
-
- long age = System.currentTimeMillis() - new File(dir, fileName).lastModified();
- if (age < 10800000) tileStatus[tidx] |= MASK_CURRENT_RD5; // 3 hours
- }
- }
- }
-
- private Matrix mat;
- private Matrix matText;
-
- public void startInstaller() {
-
- baseDir = ConfigHelper.getBaseDir(getContext());
- segmentDir = new File(baseDir, "brouter/segments4");
try {
AssetManager assetManager = getContext().getAssets();
InputStream istr = assetManager.open("world.png");
@@ -286,56 +50,111 @@ public class BInstallerView extends View {
}
tileStatus = new int[72 * 36];
- scanExistingFiles();
-
- float scaleX = imgwOrig / ((float) bmp.getWidth());
- float scaley = imghOrig / ((float) bmp.getHeight());
-
- viewscale = scaleX < scaley ? scaleX : scaley;
-
mat = new Matrix();
- mat.postScale(viewscale, viewscale);
- tilesVisible = false;
+ mGestureDetector = new GestureDetector(context, new GestureListener());
+ mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
}
- public BInstallerView(Context context) {
- super(context);
- mActivity = (Activity) context;
+ public void setOnSelectListener(OnSelectListener listener) {
+ mOnSelectListener = listener;
+ }
- DisplayMetrics metrics = new DisplayMetrics();
- ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
- imgwOrig = metrics.widthPixels;
- imghOrig = metrics.heightPixels;
- int im = imgwOrig > imghOrig ? imgwOrig : imghOrig;
+ private void setRatio(float ratio, float focusX, float focusY) {
+ if (currentScale() * ratio >= 1) {
+ mat.postScale(ratio, ratio, focusX, focusY);
+ fitBounds();
+ tilesVisible = currentScale() >= SCALE_GRID_VISIBLE;
- scaleOrig = im / 480.f;
+ invalidate();
+ }
+ }
- matText = new Matrix();
- matText.preScale(scaleOrig, scaleOrig);
+ private void setScale(float scale, float focusX, float focusY) {
+ float ratio = scale / currentScale();
+ setRatio(ratio, focusX, focusY);
+ }
- imgw = (int) (imgwOrig / scaleOrig);
- imgh = (int) (imghOrig / scaleOrig);
+ public void setTileStatus(int tileIndex, int tileMask) {
+ tileStatus[tileIndex] |= tileMask;
+ if (mOnSelectListener != null) {
+ mOnSelectListener.onSelect();
+ }
+ invalidate();
+ }
- totalSize = 0;
- rd5Tiles = 0;
- delTiles = 0;
+ public void toggleTileStatus(int tileIndex, int tileMask) {
+ tileStatus[tileIndex] ^= tileMask;
+ if (mOnSelectListener != null) {
+ mOnSelectListener.onSelect();
+ }
+ invalidate();
+ }
+
+ public void clearAllTilesStatus(int tileMask) {
+ for (int ix = 0; ix < 72; ix++) {
+ for (int iy = 0; iy < 36; iy++) {
+ int tileIndex = gridPos2Tileindex(ix, iy);
+ tileStatus[tileIndex] ^= tileStatus[tileIndex] & tileMask;
+ }
+ }
+ if (mOnSelectListener != null) {
+ mOnSelectListener.onSelect();
+ }
+ invalidate();
+ }
+
+ public ArrayList getSelectedTiles(int tileMask) {
+ ArrayList selectedTiles = new ArrayList<>();
+ for (int ix = 0; ix < 72; ix++) {
+ for (int iy = 0; iy < 36; iy++) {
+ int tileIndex = gridPos2Tileindex(ix, iy);
+ if ((tileStatus[tileIndex] & tileMask) != 0 && BInstallerSizes.getRd5Size(tileIndex) > 0) {
+ selectedTiles.add(tileIndex);
+ }
+ }
+ }
+
+ return selectedTiles;
+ }
+
+ private int gridPos2Tileindex(int ix, int iy) {
+ return (35 - iy) * 72 + (ix >= 70 ? ix - 70 : ix + 2);
+ }
+
+ private int tileIndex(float x, float y) {
+ int ix = (int) (72.f * x / bmp.getWidth());
+ int iy = (int) (36.f * y / bmp.getHeight());
+ if (ix >= 0 && ix < 72 && iy >= 0 && iy < 36) return gridPos2Tileindex(ix, iy);
+ return -1;
+ }
+
+ // get back the current image scale
+ private float currentScale() {
+ testVector[0] = 1.f;
+ mat.mapVectors(testVector);
+ return testVector[0] / viewscale;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- }
- private void toast(String msg) {
- Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
+ int imgwOrig = getWidth();
+ int imghOrig = getHeight();
+
+ float scaleX = imgwOrig / ((float) bmp.getWidth());
+ float scaleY = imghOrig / ((float) bmp.getHeight());
+
+ viewscale = Math.max(scaleX, scaleY);
+
+ mat.postScale(viewscale, viewscale);
+ tilesVisible = false;
}
@Override
protected void onDraw(Canvas canvas) {
- if (!isDownloading) {
- canvas.setMatrix(mat);
- canvas.drawBitmap(bmp, 0, 0, null);
- }
+ canvas.setMatrix(mat);
+ canvas.drawBitmap(bmp, 0, 0, null);
// draw 5*5 lattice starting at scale=3
int iw = bmp.getWidth();
@@ -343,290 +162,164 @@ public class BInstallerView extends View {
float fw = iw / 72.f;
float fh = ih / 36.f;
- boolean drawGrid = tilesVisible && !isDownloading;
-
- if (drawGrid) {
-
- pnt_1.setColor(Color.GREEN);
-
- for (int ix = 1; ix < 72; ix++) {
- float fx = fw * ix;
- canvas.drawLine(fx, 0, fx, ih, pnt_1);
+ if (tilesVisible) {
+ paintGrid.setColor(Color.GREEN);
+ paintGrid.setStyle(Paint.Style.STROKE);
+ for (int ix = 0; ix < 72; ix++) {
+ for (int iy = 0; iy < 36; iy++) {
+ int tidx = gridPos2Tileindex(ix, iy);
+ int tilesize = BInstallerSizes.getRd5Size(tidx);
+ if (tilesize > 0) {
+ canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, paintGrid);
+ }
+ }
}
- for (int iy = 1; iy < 36; iy++) {
- float fy = fh * iy;
- canvas.drawLine(0, fy, iw, fy, pnt_1);
- }
- }
- rd5Tiles = 0;
- delTiles = 0;
- totalSize = 0;
- int mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
- int mask3 = mask2 | MASK_CURRENT_RD5;
- pnt_2.setColor(Color.GRAY);
- pnt_2.setStrokeWidth(1);
- drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5, mask3, false, false, drawGrid);
- pnt_2.setColor(Color.BLUE);
- pnt_2.setStrokeWidth(1);
- drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3, false, false, drawGrid);
- pnt_2.setColor(Color.GREEN);
- pnt_2.setStrokeWidth(2);
- drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5, mask2, true, false, drawGrid);
- pnt_2.setColor(Color.YELLOW);
- pnt_2.setStrokeWidth(2);
- drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2, true, false, drawGrid);
- pnt_2.setColor(Color.RED);
- pnt_2.setStrokeWidth(2);
- drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2, false, true, drawGrid);
+ int mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
+ int mask3 = mask2 | MASK_CURRENT_RD5;
- canvas.setMatrix(matText);
-
- paint.setColor(Color.RED);
-
- long mb = 1024 * 1024;
-
- if (isDownloading) {
- String sizeHint = currentDownloadSize > 0 ? " (" + ((currentDownloadSize + mb - 1) / mb) + " MB)" : "";
- paint.setTextSize(30);
- canvas.drawText(currentDownloadOperation, 30, (imgh / 3) * 2 - 30, paint);
- // canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
- canvas.drawText(downloadAction, 30, (imgh / 3) * 2, paint);
- }
- if (!tilesVisible && !isDownloading) {
- paint.setTextSize(35);
- canvas.drawText("Touch region to zoom in!", 30, (imgh / 3) * 2, paint);
- }
- paint.setTextSize(20);
-
-
- String totmb = ((totalSize + mb - 1) / mb) + " MB";
- String freemb = availableSize >= 0 ? ((availableSize + mb - 1) / mb) + " MB" : "?";
- canvas.drawText("Selected segments=" + rd5Tiles, 10, 25, paint);
- canvas.drawText("Size=" + totmb + " Free=" + freemb, 10, 45, paint);
-
-
- String btnText = null;
- if (isDownloading) btnText = "Cancel Download";
- else if (delTiles > 0) btnText = "Delete " + delTiles + " tiles";
- else if (rd5Tiles > 0) btnText = "Start Download";
- else if (tilesVisible &&
- rd5Tiles == 0 &&
- RoutingHelper.hasDirectoryAnyDatafiles(segmentDir)) btnText = "Update all";
-
- if (btnText != null) {
- canvas.drawLine(imgw - btnw, imgh - btnh, imgw - btnw, imgh - 2, paint);
- canvas.drawLine(imgw - btnw, imgh - btnh, imgw - 2, imgh - btnh, paint);
- canvas.drawLine(imgw - btnw, imgh - btnh, imgw - btnw, imgh - 2, paint);
- canvas.drawLine(imgw - 2, imgh - btnh, imgw - 2, imgh - 2, paint);
- canvas.drawLine(imgw - btnw, imgh - 2, imgw - 2, imgh - 2, paint);
- canvas.drawText(btnText, imgw - btnw + 5, imgh - 10, paint);
+ paintTiles.setStyle(Paint.Style.STROKE);
+ paintTiles.setColor(Color.GRAY);
+ paintTiles.setStrokeWidth(1);
+ drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5, mask3);
+ paintTiles.setColor(Color.BLUE);
+ paintTiles.setStrokeWidth(1);
+ drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3);
+ paintTiles.setColor(Color.GREEN);
+ paintTiles.setStrokeWidth(2);
+ drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5, mask2);
+ paintTiles.setColor(Color.YELLOW);
+ paintTiles.setStrokeWidth(2);
+ drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2);
+ paintTiles.setColor(Color.RED);
+ paintTiles.setStrokeWidth(2);
+ drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2);
}
}
- int btnh = 40;
- int btnw = 160;
-
-
- float tx, ty;
-
- private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask, boolean doCount, boolean cntDel, boolean doDraw) {
+ private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask) {
for (int ix = 0; ix < 72; ix++)
for (int iy = 0; iy < 36; iy++) {
int tidx = gridPos2Tileindex(ix, iy);
if ((tileStatus[tidx] & mask) == status) {
int tilesize = BInstallerSizes.getRd5Size(tidx);
if (tilesize > 0) {
- if (doCount) {
- rd5Tiles++;
- totalSize += BInstallerSizes.getRd5Size(tidx);
- }
- if (cntDel) {
- delTiles++;
- totalSize += BInstallerSizes.getRd5Size(tidx);
- }
- if (!doDraw)
- continue;
// draw cross
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
// draw frame
- canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * iy, pnt);
- canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * (iy + 1), pnt);
- canvas.drawLine(fw * ix, fh * iy, fw * ix, fh * (iy + 1), pnt);
- canvas.drawLine(fw * (ix + 1), fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
+ canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
}
}
}
}
- public void deleteSelectedTiles() {
- for (int ix = 0; ix < 72; ix++) {
- for (int iy = 0; iy < 36; iy++) {
- int tidx = gridPos2Tileindex(ix, iy);
- if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
- new File(baseDir, "brouter/segments4/" + baseNameForTile(tidx) + ".rd5").delete();
- }
- }
+ private void fitBounds() {
+ float[] srcPoints = new float[]{
+ 0, 0,
+ bmp.getWidth(), bmp.getHeight()
+ };
+ float[] dstPoints = new float[srcPoints.length];
+ float transX = 0;
+ float transY = 0;
+ mat.mapPoints(dstPoints, srcPoints);
+ if (dstPoints[0] > 0) {
+ transX = -dstPoints[0];
+ } else if (dstPoints[2] < getWidth()) {
+ transX = getWidth() - dstPoints[2];
+ }
+ if (dstPoints[1] > 0) {
+ transY = -dstPoints[1];
+ } else if (dstPoints[3] < getHeight()) {
+ transY = getHeight() - dstPoints[3];
+ }
+ if (transX != 0 || transY != 0) {
+ mat.postTranslate(transX, transY);
}
- scanExistingFiles();
- invalidate();
}
+ @SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
-
- // get pointer index from the event object
- int pointerIndex = event.getActionIndex();
-
- // get pointer ID
- int pointerId = event.getPointerId(pointerIndex);
-
- // get masked (not specific to a pointer) action
- int maskedAction = event.getActionMasked();
-
- switch (maskedAction) {
-
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN: {
- lastDownX = event.getX();
- lastDownY = event.getY();
-
- break;
- }
- case MotionEvent.ACTION_MOVE: { // a pointer was moved
-
- if (isDownloading) break;
- int np = event.getPointerCount();
- int nh = event.getHistorySize();
- if (nh == 0) break;
-
- float x0 = event.getX(0);
- float y0 = event.getY(0);
- float hx0 = event.getHistoricalX(0, 0);
- float hy0 = event.getHistoricalY(0, 0);
-
- if (np > 1) // multi-touch
- {
- float x1 = event.getX(1);
- float y1 = event.getY(1);
- float hx1 = event.getHistoricalX(1, 0);
- float hy1 = event.getHistoricalY(1, 0);
-
- float r = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
- float hr = (float) Math.sqrt((hx1 - hx0) * (hx1 - hx0) + (hy1 - hy0) * (hy1 - hy0));
-
- if (hr > 0.) {
- float ratio = r / hr;
-
- float mx = (x1 + x0) / 2.f;
- float my = (y1 + y0) / 2.f;
-
- float scale = currentScale();
- float newscale = scale * ratio;
-
- if (newscale > 10.f) ratio *= (10.f / newscale);
- if (newscale < 0.5f) ratio *= (0.5f / newscale);
-
- mat.postScale(ratio, ratio, mx, my);
-
- mat.postScale(ratio, ratio, mx, my);
-
- boolean tilesv = currentScale() >= 3.f;
- if (tilesVisible && !tilesv) {
- clearTileSelection(MASK_SELECTED_RD5 | MASK_DELETED_RD5);
- }
- tilesVisible = tilesv;
- }
-
- break;
- }
- mat.postTranslate(x0 - hx0, y0 - hy0);
-
- break;
- }
- case MotionEvent.ACTION_UP:
-
- long downTime = event.getEventTime() - event.getDownTime();
-
- if (downTime < 5 || downTime > 500) {
- break;
- }
-
- if (Math.abs(lastDownX - event.getX()) > 10 || Math.abs(lastDownY - event.getY()) > 10) {
- break;
- }
-
- // download button?
- if ((delTiles > 0 || rd5Tiles >= 0 || isDownloading) && event.getX() > imgwOrig - btnw * scaleOrig && event.getY() > imghOrig - btnh * scaleOrig) {
- if (rd5Tiles == 0) {
- for (int ix = 0; ix < 72; ix++) {
- for (int iy = 0; iy < 36; iy++) {
- int tidx = gridPos2Tileindex(ix, iy);
- if (tidx != -1) {
- if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
- tileStatus[tidx] |= MASK_SELECTED_RD5;
- }
- }
-
- }
- }
- }
- toggleDownload();
- invalidate();
- break;
- }
-
- if (!tilesVisible) {
- float scale = currentScale();
- if (scale > 0f && scale < 5f) {
- float ratio = 5f / scale;
- mat.postScale(ratio, ratio, event.getX(), event.getY());
- tilesVisible = true;
- }
- break;
- }
-
- if (isDownloading) break;
-
- Matrix imat = new Matrix();
- if (mat.invert(imat)) {
- float[] touchpoint = new float[2];
- touchpoint[0] = event.getX();
- touchpoint[1] = event.getY();
- imat.mapPoints(touchpoint);
-
- int tidx = tileIndex(touchpoint[0], touchpoint[1]);
- if (tidx != -1) {
- if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
- tileStatus[tidx] ^= MASK_SELECTED_RD5;
- if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
- tileStatus[tidx] |= MASK_DELETED_RD5;
- }
- } else if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
- tileStatus[tidx] ^= MASK_DELETED_RD5;
- } else {
- tileStatus[tidx] ^= MASK_SELECTED_RD5;
- }
- }
-
- tx = touchpoint[0];
- ty = touchpoint[1];
- }
-
-
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_CANCEL: {
- // TODO use data
- break;
- }
- }
- invalidate();
-
- return true;
+ boolean retVal = mScaleGestureDetector.onTouchEvent(event);
+ retVal = mGestureDetector.onTouchEvent(event) || retVal;
+ return retVal || super.onTouchEvent(event);
}
+ interface OnSelectListener {
+ void onSelect();
+ }
+
+ class GestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (tilesVisible) {
+ Matrix imat = new Matrix();
+ if (mat.invert(imat)) {
+ float[] touchPoint = {e.getX(), e.getY()};
+ imat.mapPoints(touchPoint);
+
+ int tidx = tileIndex(touchPoint[0], touchPoint[1]);
+ if (tidx != -1) {
+ if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
+ toggleTileStatus(tidx, MASK_SELECTED_RD5);
+ if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
+ setTileStatus(tidx, MASK_DELETED_RD5);
+ }
+ } else if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
+ toggleTileStatus(tidx, MASK_DELETED_RD5);
+ } else {
+ toggleTileStatus(tidx, MASK_SELECTED_RD5);
+ }
+ }
+ }
+ invalidate();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (!tilesVisible) {
+ setScale(5, e.getX(), e.getY());
+ } else {
+ setScale(1, e.getX(), e.getY());
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ mat.postTranslate(-distanceX, -distanceY);
+ fitBounds();
+ invalidate();
+ return true;
+ }
+ }
+
+ class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
+ return true;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+ float focusX = scaleGestureDetector.getFocusX();
+ float focusY = scaleGestureDetector.getFocusY();
+ float ratio = scaleGestureDetector.getScaleFactor();
+
+ setRatio(ratio, focusX, focusY);
+
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
+ }
+ }
}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
index 40bcb5f..c3253ce 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
@@ -1,7 +1,24 @@
package btools.routingapp;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.StatFs;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.os.EnvironmentCompat;
+
import java.io.File;
-import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -9,36 +26,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.StatFs;
-import android.speech.tts.TextToSpeech.OnInitListener;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.os.EnvironmentCompat;
-
import btools.router.OsmNodeNamed;
-public class BRouterActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback {
+public class BRouterActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int DIALOG_SELECTPROFILE_ID = 1;
private static final int DIALOG_EXCEPTION_ID = 2;
@@ -129,11 +119,11 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
.setMessage(
"*** Attention: ***\n\n" + "The Download Manager is used to download routing-data "
+ "files which can be up to 170MB each. Do not start the Download Manager " + "on a cellular data connection without a data plan! "
- + "Download speed is restricted to 4 MBit/s.").setPositiveButton("I know", new DialogInterface.OnClickListener() {
+ + "Download speed is restricted to 16 MBit/s.").setPositiveButton("I know", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
startActivity(intent);
- // finish();
+ showNewDialog(DIALOG_MAINACTION_ID);
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
@@ -391,39 +381,12 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
private String maptoolDirCandidate;
- public boolean isOnline(Context context) {
- boolean result = false;
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- Network nw = connectivityManager.getActiveNetwork();
- if (nw == null) return false;
- NetworkCapabilities nwc = connectivityManager.getNetworkCapabilities(nw);
- if (nwc == null) return false;
- result = nwc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) |
- nwc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) |
- nwc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
-
- } else {
- NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
- if (ni == null) return false;
- result = ni.getType() == ConnectivityManager.TYPE_WIFI ||
- ni.getType() == ConnectivityManager.TYPE_MOBILE ||
- ni.getType() == ConnectivityManager.TYPE_ETHERNET;
- }
-
- return result;
- }
-
@SuppressWarnings("deprecation")
public void selectProfile(String[] items) {
availableProfiles = items;
- // if we have internet access, first show the main action dialog
- if (isOnline(this)) {
- showDialog(DIALOG_MAINACTION_ID);
- } else {
- showDialog(DIALOG_SELECTPROFILE_ID);
- }
+ // show main dialog
+ showDialog(DIALOG_MAINACTION_ID);
}
@SuppressWarnings("deprecation")
@@ -626,6 +589,7 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mBRouterView.startSetup(null, true);
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
index 9b04dba..c21af97 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
@@ -1,5 +1,21 @@
package btools.routingapp;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -12,36 +28,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.os.Build;
-import android.os.Environment;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
import btools.mapaccess.OsmNode;
import btools.router.OsmNodeNamed;
@@ -108,11 +101,6 @@ public class BRouterView extends View {
public void init() {
try {
- DisplayMetrics metrics = new DisplayMetrics();
- ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
- imgw = metrics.widthPixels;
- imgh = metrics.heightPixels;
-
// get base dir from private file
File baseDir = ConfigHelper.getBaseDir(getContext());
// check if valid
@@ -123,6 +111,15 @@ public class BRouterView extends View {
if (brd.isDirectory()) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q &&
!brd.getAbsolutePath().contains("/Android/media/btools.routingapp")) {
+
+ // don't ask twice
+ String version = "v" + getContext().getString(R.string.app_version);
+ File vFile = new File(brd, "profiles2/"+version );
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q
+ && vFile.exists()) {
+ startSetup(baseDir, false);
+ return;
+ }
String message = "(previous basedir " + baseDir + " has to migrate )";
((BRouterActivity) getContext()).selectBasedir(((BRouterActivity) getContext()).getStorageDirectories(), guessBaseDir(), message);
@@ -204,7 +201,9 @@ public class BRouterView extends View {
// new init is done move old files
if (waitingForMigration) {
- moveFolders(oldMigrationPath, basedir + "/brouter");
+ Log.d("BR", "path " + oldMigrationPath + " " + basedir);
+ if (!oldMigrationPath.equals(basedir + "/brouter"))
+ moveFolders(oldMigrationPath, basedir + "/brouter");
waitingForMigration = false;
}
@@ -254,7 +253,7 @@ public class BRouterView extends View {
}
}
if (tracksDir == null) {
- tracksDir = new File(basedir, "router"); // fallback
+ tracksDir = new File(basedir, "brouter"); // fallback
}
String[] fileNames = profileDir.list();
@@ -569,7 +568,7 @@ public class BRouterView extends View {
// for profile remote, use ref-track logic same as service interface
rc.rawTrackPath = rawTrackPath;
- cr = new RoutingEngine(tracksDir + "/brouter", null, segmentDir, wpList, rc);
+ cr = new RoutingEngine(tracksDir.getAbsolutePath()+"/brouter", null, segmentDir, wpList, rc);
cr.start();
invalidate();
@@ -606,16 +605,18 @@ public class BRouterView extends View {
break;
String name = ze.getName();
File outfile = new File(path, name);
- outfile.getParentFile().mkdirs();
- FileOutputStream fos = new FileOutputStream(outfile);
+ if (!outfile.exists()) {
+ outfile.getParentFile().mkdirs();
+ FileOutputStream fos = new FileOutputStream(outfile);
- for (; ; ) {
- int len = zis.read(data, 0, 1024);
- if (len < 0)
- break;
- fos.write(data, 0, len);
+ for (; ; ) {
+ int len = zis.read(data, 0, 1024);
+ if (len < 0)
+ break;
+ fos.write(data, 0, len);
+ }
+ fos.close();
}
- fos.close();
}
is.close();
return true;
@@ -690,6 +691,8 @@ public class BRouterView extends View {
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ imgw = w;
+ imgh = h;
}
private void toast(String msg) {
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
deleted file mode 100644
index c1a0c0a..0000000
--- a/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
+++ /dev/null
@@ -1,487 +0,0 @@
-package btools.routingapp;
-
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Intent;
-import android.net.TrafficStats;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.List;
-
-import btools.mapaccess.PhysicalFile;
-import btools.mapaccess.Rd5DiffManager;
-import btools.mapaccess.Rd5DiffTool;
-import btools.util.ProgressListener;
-
-public class DownloadService extends Service implements ProgressListener {
-
- private static final boolean DEBUG = false;
-
- private ServerConfig mServerConfig;
-
- private NotificationHelper mNotificationHelper;
- private List mUrlList;
- private String baseDir;
-
- private volatile String newDownloadAction = "";
- private volatile String currentDownloadOperation = "";
- private long availableSize;
-
- private Looper mServiceLooper;
- private ServiceHandler mServiceHandler;
- private NotificationManager mNM;
- String downloadUrl;
- public static boolean serviceState = false;
- private boolean bIsDownloading;
-
- // Handler that receives messages from the thread
- private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- bIsDownloading = true;
- downloadFiles();
-
- stopForeground(true);
- stopSelf(msg.arg1);
- mNotificationHelper.stopNotification();
- }
- }
-
-
- @Override
- public void onCreate() {
- if (DEBUG) Log.d("SERVICE", "onCreate");
- serviceState = true;
- mServerConfig = new ServerConfig(getApplicationContext());
-
- HandlerThread thread = new HandlerThread("ServiceStartArguments", 1);
- thread.start();
-
- // Get the HandlerThread's Looper and use it for our Handler
- mServiceLooper = thread.getLooper();
- mServiceHandler = new ServiceHandler(mServiceLooper);
-
- availableSize = -1;
- try {
- availableSize = BInstallerActivity.getAvailableSpace(baseDir);
- //StatFs stat = new StatFs(baseDir);
- //availableSize = (long)stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
- } catch (Exception e) { /* ignore */ }
-
- }
-
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (DEBUG) Log.d("SERVICE", "onStartCommand");
-
- mNotificationHelper = new NotificationHelper(this);
- Bundle extra = intent.getExtras();
- if (extra != null) {
- String dir = extra.getString("dir");
- List urlparts = extra.getStringArrayList("urlparts");
- mUrlList = urlparts;
- baseDir = dir;
- }
-
- mNotificationHelper.startNotification(this);
-
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- mServiceHandler.sendMessage(msg);
-
- // If we get killed, after returning from here, restart
- return START_STICKY;
- }
-
-
- @Override
- public void onDestroy() {
- if (DEBUG) Log.d("SERVICE", "onDestroy");
- serviceState = false;
- super.onDestroy();
- }
-
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
-
- public void downloadFiles() {
-
- // first check lookup table and profiles
- String result = checkScripts();
- if (result != null) {
- if (DEBUG) Log.d("BR", "error: " + result);
- bIsDownloading = false;
- updateProgress("finished ");
-
- Toast.makeText(this, result, Toast.LENGTH_LONG).show();
- return;
- }
-
-
- int count = 1;
- int size = mUrlList.size();
- for (String part : mUrlList) {
- String url = mServerConfig.getSegmentUrl() + part + ".rd5";
- if (DEBUG) Log.d("BR", "downlaod " + url);
-
- result = download(count, size, url);
- if (result != null) {
- if (DEBUG) Log.d("BR", "" + result);
- Toast.makeText(this, result, Toast.LENGTH_LONG).show();
- break;
- } else {
- updateProgress("Download " + part + " " + count + "/" + size + " finshed");
- }
- count++;
- }
-
- bIsDownloading = false;
- updateProgress("finished ");
- }
-
-
- public void updateProgress(String progress) {
- if (!newDownloadAction.equals(progress)) {
- if (DEBUG) Log.d("BR", "up " + progress);
- Intent intent = new Intent(BInstallerActivity.DOWNLOAD_ACTION);
- intent.putExtra("txt", progress);
- intent.putExtra("ready", bIsDownloading);
- sendBroadcast(intent);
- ;
- newDownloadAction = progress;
- mNotificationHelper.progressUpdate(newDownloadAction);
- }
-
- }
-
- private String download(int counter, int size, String surl) {
- InputStream input = null;
- OutputStream output = null;
- HttpURLConnection connection = null;
- File fname = null;
- File tmp_file = null;
- try {
- try {
- TrafficStats.setThreadStatsTag(1);
-
- int slidx = surl.lastIndexOf("segments4/");
- String name = surl.substring(slidx + 10);
- String surlBase = surl.substring(0, slidx + 10);
- fname = new File(baseDir, "segments4/" + name);
-
- boolean delta = true;
-
- // if (!targetFile.getParentFile().exists()) targetFile.getParentFile().mkdirs();
- if (fname.exists()) {
- updateProgress("Calculating local checksum..");
-
- // first check for a delta file
- String md5 = Rd5DiffManager.getMD5(fname);
- String surlDelta = surlBase + "diff/" + name.replace(".rd5", "/" + md5 + ".df5");
-
- URL urlDelta = new URL(surlDelta);
-
- connection = (HttpURLConnection) urlDelta.openConnection();
- connection.setConnectTimeout(5000);
- connection.connect();
-
- // 404 kind of expected here, means there's no delta file
- if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- connection = null;
- } else {
- updateProgress("Connecting.." + surlDelta);
- }
- }
-
- if (connection == null) {
- updateProgress("Connecting.." + name);
-
- delta = false;
- URL url = new URL(surl);
- connection = (HttpURLConnection) url.openConnection();
- connection.setConnectTimeout(5000);
- connection.connect();
- }
-
- updateProgress("Connecting.." + counter + "/" + size);
-
- // expect HTTP 200 OK, so we don't mistakenly save error report
- // instead of the file
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- return "Server returned HTTP " + connection.getResponseCode()
- + " " + connection.getResponseMessage();
- }
-
- // this will be useful to display download percentage
- // might be -1: server did not report the length
- int fileLength = connection.getContentLength();
- long currentDownloadSize = fileLength;
- if (availableSize >= 0 && fileLength > availableSize) return "not enough space on sd-card";
-
- currentDownloadOperation = delta ? "Updating" : "Loading";
- updateProgress(currentDownloadOperation);
-
- // download the file
- input = connection.getInputStream();
-
- tmp_file = new File(fname.getAbsolutePath() + (delta ? "_diff" : "_tmp"));
- output = new FileOutputStream(tmp_file);
-
- byte[] data = new byte[4096];
- long total = 0;
- long t0 = System.currentTimeMillis();
- int count;
- while ((count = input.read(data)) != -1) {
- if (isCanceled()) {
- return "Download canceled!";
- }
- total += count;
- // publishing the progress....
- if (fileLength > 0) // only if total length is known
- {
- int pct = (int) (total * 100 / fileLength);
- updateProgress("Progress " + counter + "/" + size + " .. " + pct + "%");
- } else {
- updateProgress("Progress (unnown size)");
- }
-
- output.write(data, 0, count);
-
- // enforce < 2 Mbit/s
- long dt = t0 + total / 524 - System.currentTimeMillis();
- if (dt > 0) {
- try {
- Thread.sleep(dt);
- } catch (InterruptedException ie) {
- }
- }
- }
- output.close();
- output = null;
-
- if (delta) {
- updateProgress("Applying delta..");
- File diffFile = tmp_file;
- tmp_file = new File(fname + "_tmp");
- Rd5DiffTool.recoverFromDelta(fname, diffFile, tmp_file, this);
- diffFile.delete();
- }
- if (isCanceled()) {
- return "Canceled!";
- }
- if (tmp_file != null) {
- updateProgress("Verifying integrity..");
- String check_result = PhysicalFile.checkFileIntegrity(tmp_file);
- if (check_result != null) {
- if (check_result.startsWith("version old lookups.dat")) {
-
- }
- return check_result;
- }
- if (fname.exists()) fname.delete();
- if (!tmp_file.renameTo(fname)) {
- return "Could not rename to " + fname.getAbsolutePath();
- }
-
- }
- return null;
- } catch (Exception e) {
- //e.printStackTrace(); ;
- return e.toString();
- } finally {
- try {
- if (output != null)
- output.close();
- if (input != null)
- input.close();
- } catch (IOException ignored) {
- }
-
- if (connection != null)
- connection.disconnect();
- }
- } finally {
- if (tmp_file != null) tmp_file.delete(); // just to be sure
- }
- }
-
- private String checkScripts() {
-
- String[] sa = mServerConfig.getLookups();
- for (String f : sa) {
- if (f.length() > 0) {
- File file = new File(baseDir + "profiles2", f);
- checkOrDownloadLookup(f, file);
- }
- }
-
- sa = mServerConfig.getProfiles();
- for (String f : sa) {
- if (f.length() > 0) {
- File file = new File(baseDir + "profiles2", f);
- if (file.exists()) {
- String result = checkOrDownloadScript(f, file);
- if (result != null) {
- return result;
- }
- }
- }
- }
- return null;
- }
-
- private String checkOrDownloadLookup(String fileName, File f) {
- String url = mServerConfig.getLookupUrl() + fileName;
- return downloadScript(url, f);
- }
-
- private String checkOrDownloadScript(String fileName, File f) {
- String url = mServerConfig.getProfilesUrl() + fileName;
- return downloadScript(url, f);
- }
-
- private String downloadScript(String surl, File f) {
- long size = 0L;
- if (f.exists()) {
- size = f.length();
- }
-
- InputStream input = null;
- OutputStream output = null;
- HttpURLConnection connection = null;
- File tmp_file = null;
- File targetFile = f;
-
- try {
- try {
- TrafficStats.setThreadStatsTag(1);
-
- URL url = new URL(surl);
- connection = (HttpURLConnection) url.openConnection();
- connection.setConnectTimeout(5000);
- connection.connect();
-
- // expect HTTP 200 OK, so we don't mistakenly save error report
- // instead of the file
- if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- return null;
- }
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- return "Server returned HTTP " + connection.getResponseCode()
- + " " + connection.getResponseMessage() + " " + f.getName();
- }
-
-
- // this will be useful to display download percentage
- // might be -1: server did not report the length
- long fileLength = (long) connection.getContentLength();
- if (DEBUG) Log.d("BR", "file size " + size + " == " + fileLength + " " + f.getName());
- if (fileLength != size) {
- long currentDownloadSize = fileLength;
- if (availableSize >= 0 && fileLength > availableSize)
- return "not enough space on sd-card";
-
- currentDownloadOperation = "Updating";
-
- // download the file
- input = connection.getInputStream();
-
- tmp_file = new File(f.getAbsolutePath() + "_tmp");
- output = new FileOutputStream(tmp_file);
-
- byte data[] = new byte[4096];
- long total = 0;
- long t0 = System.currentTimeMillis();
- int count;
- while ((count = input.read(data)) != -1) {
- if (isCanceled()) {
- return "Download canceled!";
- }
- total += count;
- // publishing the progress....
- if (fileLength > 0) // only if total length is known
- {
- int pct = (int) (total * 100 / fileLength);
- updateProgress("Progress " + pct + "%");
- } else {
- updateProgress("Progress (unnown size)");
- }
-
- output.write(data, 0, count);
-
- // enforce < 2 Mbit/s
- long dt = t0 + total / 524 - System.currentTimeMillis();
- if (dt > 0) {
- try {
- Thread.sleep(dt);
- } catch (InterruptedException ie) {
- }
- }
- }
- output.close();
- output = null;
- }
-
- if (isCanceled()) {
- return "Canceled!";
- }
- if (tmp_file != null) {
- f.delete();
-
- if (!tmp_file.renameTo(f)) {
- return "Could not rename to " + f.getName();
- }
- if (DEBUG) Log.d("BR", "update " + f.getName());
- }
- return null;
- } catch (Exception e) {
- return e.toString();
- } finally {
- try {
- if (output != null)
- output.close();
- if (input != null)
- input.close();
- } catch (IOException ignored) {
- }
-
- if (connection != null)
- connection.disconnect();
-
- }
- } finally {
- if (tmp_file != null) tmp_file.delete(); // just to be sure
- }
-
- }
-
-
- public boolean isCanceled() {
- return BInstallerView.downloadCanceled;
- }
-
-}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java
new file mode 100644
index 0000000..0b493fe
--- /dev/null
+++ b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java
@@ -0,0 +1,327 @@
+package btools.routingapp;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import androidx.work.Data;
+import androidx.work.ForegroundInfo;
+import androidx.work.WorkManager;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Random;
+
+import btools.mapaccess.PhysicalFile;
+import btools.mapaccess.Rd5DiffManager;
+import btools.mapaccess.Rd5DiffTool;
+import btools.util.ProgressListener;
+
+public class DownloadWorker extends Worker {
+ public static final String KEY_INPUT_SEGMENT_NAMES = "SEGMENT_NAMES";
+ public static final String PROGRESS_SEGMENT_NAME = "PROGRESS_SEGMENT_NAME";
+ public static final String PROGRESS_SEGMENT_PERCENT = "PROGRESS_SEGMENT_PERCENT";
+
+ private final static boolean DEBUG = false;
+ private static final int NOTIFICATION_ID = new Random().nextInt();
+ private static final String PROFILES_DIR = "profiles2/";
+ private static final String SEGMENTS_DIR = "segments4/";
+ private static final String SEGMENT_DIFF_SUFFIX = ".df5";
+ private static final String SEGMENT_SUFFIX = ".rd5";
+ private static final String LOG_TAG = "DownloadWorker";
+
+ private final NotificationManager notificationManager;
+ private final ServerConfig mServerConfig;
+ private final File baseDir;
+ private final ProgressListener diffProgressListener;
+ private final DownloadProgressListener downloadProgressListener;
+ private final Data.Builder progressBuilder = new Data.Builder();
+ private final NotificationCompat.Builder notificationBuilder;
+
+ public DownloadWorker(
+ @NonNull Context context,
+ @NonNull WorkerParameters parameters) {
+ super(context, parameters);
+ notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ mServerConfig = new ServerConfig(context);
+ baseDir = new File(ConfigHelper.getBaseDir(context), "brouter");
+
+ notificationBuilder = createNotificationBuilder();
+
+ downloadProgressListener = new DownloadProgressListener() {
+ private String currentDownloadName;
+ private DownloadType currentDownloadType;
+ private int lastProgressPercent;
+
+ @Override
+ public void onDownloadStart(String downloadName, DownloadType downloadType) {
+ currentDownloadName = downloadName;
+ currentDownloadType = downloadType;
+ if (downloadType == DownloadType.SEGMENT) {
+ progressBuilder.putString(PROGRESS_SEGMENT_NAME, downloadName);
+ notificationBuilder.setContentText(downloadName);
+ }
+ }
+
+ @Override
+ public void onDownloadInfo(String info) {
+ notificationBuilder.setContentText(currentDownloadName + ": " + info);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ }
+
+ @Override
+ public void onDownloadProgress(int max, int progress) {
+ int progressPercent = (int) (progress * 100L / max);
+
+ // Only report segments and update if it changed to avoid hammering NotificationManager
+ if (currentDownloadType != DownloadType.SEGMENT || progressPercent == lastProgressPercent) {
+ return;
+ }
+
+ if (max > 0) {
+ notificationBuilder.setProgress(max, progress, false);
+ progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, progressPercent);
+ } else {
+ notificationBuilder.setProgress(0, 0, true);
+ progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, -1);
+ }
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ setProgressAsync(progressBuilder.build());
+
+ lastProgressPercent = progressPercent;
+ }
+
+ @Override
+ public void onDownloadFinished() {
+ }
+ };
+
+ diffProgressListener = new ProgressListener() {
+ @Override
+ public void updateProgress(String task, int progress) {
+ downloadProgressListener.onDownloadInfo(task);
+ downloadProgressListener.onDownloadProgress(100, progress);
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return isStopped();
+ }
+ };
+ }
+
+ @NonNull
+ @Override
+ public Result doWork() {
+ Data inputData = getInputData();
+ String[] segmentNames = inputData.getStringArray(KEY_INPUT_SEGMENT_NAMES);
+ if (segmentNames == null) {
+ if (DEBUG) Log.d(LOG_TAG, "Failure: no segmentNames");
+ return Result.failure();
+ }
+ notificationBuilder.setContentText("Starting Download");
+ // Mark the Worker as important
+ setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build()));
+ try {
+ if (DEBUG) Log.d(LOG_TAG, "Download lookup & profiles");
+ downloadLookupAndProfiles();
+
+ for (String segmentName : segmentNames) {
+ downloadProgressListener.onDownloadStart(segmentName, DownloadType.SEGMENT);
+ if (DEBUG) Log.d(LOG_TAG, "Download segment " + segmentName);
+ downloadSegment(mServerConfig.getSegmentUrl(), segmentName + SEGMENT_SUFFIX);
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, e);
+ return Result.failure();
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, e);
+ return Result.failure();
+ }
+ if (DEBUG) Log.d(LOG_TAG, "doWork finished");
+ return Result.success();
+ }
+
+ private void downloadLookupAndProfiles() throws IOException, InterruptedException {
+ String[] lookups = mServerConfig.getLookups();
+ for (String fileName : lookups) {
+ if (fileName.length() > 0) {
+ File lookupFile = new File(baseDir, PROFILES_DIR + fileName);
+ String lookupLocation = mServerConfig.getLookupUrl() + fileName;
+ URL lookupUrl = new URL(lookupLocation);
+ downloadProgressListener.onDownloadStart(fileName, DownloadType.LOOKUP);
+ downloadFile(lookupUrl, lookupFile, false);
+ downloadProgressListener.onDownloadFinished();
+ }
+ }
+
+ String[] profiles = mServerConfig.getProfiles();
+ for (String fileName : profiles) {
+ if (fileName.length() > 0) {
+ File profileFile = new File(baseDir, PROFILES_DIR + fileName);
+ if (profileFile.exists()) {
+ String profileLocation = mServerConfig.getProfilesUrl() + fileName;
+ URL profileUrl = new URL(profileLocation);
+ downloadProgressListener.onDownloadStart(fileName, DownloadType.PROFILE);
+ downloadFile(profileUrl, profileFile, false);
+ downloadProgressListener.onDownloadFinished();
+ }
+ }
+ }
+ }
+
+ private void downloadSegment(String segmentBaseUrl, String segmentName) throws IOException, InterruptedException {
+ File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName);
+ File segmentFileTemp = new File(segmentFile.getAbsolutePath() + "_tmp");
+ try {
+ if (segmentFile.exists()) {
+ if (DEBUG) Log.d(LOG_TAG, "Calculating local checksum");
+ String md5 = Rd5DiffManager.getMD5(segmentFile);
+ String segmentDeltaLocation = segmentBaseUrl + "diff/" + segmentName.replace(SEGMENT_SUFFIX, "/" + md5 + SEGMENT_DIFF_SUFFIX);
+ URL segmentDeltaUrl = new URL(segmentDeltaLocation);
+ if (httpFileExists(segmentDeltaUrl)) {
+ File segmentDeltaFile = new File(segmentFile.getAbsolutePath() + "_diff");
+ try {
+ downloadFile(segmentDeltaUrl, segmentDeltaFile, true);
+ if (DEBUG) Log.d(LOG_TAG, "Applying delta");
+ Rd5DiffTool.recoverFromDelta(segmentFile, segmentDeltaFile, segmentFileTemp, diffProgressListener);
+ } catch (IOException e) {
+ throw new IOException("Failed to download & apply delta update", e);
+ } finally {
+ segmentDeltaFile.delete();
+ }
+ }
+ }
+
+ if (!segmentFileTemp.exists()) {
+ URL segmentUrl = new URL(segmentBaseUrl + segmentName);
+ downloadFile(segmentUrl, segmentFileTemp, true);
+ }
+
+ PhysicalFile.checkFileIntegrity(segmentFileTemp);
+ if (segmentFile.exists()) {
+ if (!segmentFile.delete()) {
+ throw new IOException("Failed to delete existing " + segmentFile.getAbsolutePath());
+ }
+ }
+
+ if (!segmentFileTemp.renameTo(segmentFile)) {
+ throw new IOException("Failed to write " + segmentFile.getAbsolutePath());
+ }
+ } finally {
+ segmentFileTemp.delete();
+ }
+ }
+
+ private boolean httpFileExists(URL downloadUrl) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
+ connection.setConnectTimeout(5000);
+ connection.setRequestMethod("HEAD");
+ connection.connect();
+
+ return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
+ }
+
+ private void downloadFile(URL downloadUrl, File outputFile, boolean limitDownloadSpeed) throws IOException, InterruptedException {
+ HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
+ connection.setConnectTimeout(5000);
+ connection.connect();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException("HTTP Request failed");
+ }
+ int fileLength = connection.getContentLength();
+ try (
+ InputStream input = connection.getInputStream();
+ OutputStream output = new FileOutputStream(outputFile)
+ ) {
+ byte[] buffer = new byte[4096];
+ int total = 0;
+ long t0 = System.currentTimeMillis();
+ int count;
+ while ((count = input.read(buffer)) != -1) {
+ if (isStopped()) {
+ throw new InterruptedException();
+ }
+ total += count;
+ output.write(buffer, 0, count);
+
+ downloadProgressListener.onDownloadProgress(fileLength, total);
+
+ if (limitDownloadSpeed) {
+ // enforce < 16 Mbit/s
+ long dt = t0 + total / 2096 - System.currentTimeMillis();
+ if (dt > 0) {
+ Thread.sleep(dt);
+ }
+ }
+ }
+ }
+ }
+
+ @NonNull
+ private NotificationCompat.Builder createNotificationBuilder() {
+ Context context = getApplicationContext();
+ String id = context.getString(R.string.notification_channel_id);
+ String title = context.getString(R.string.notification_title);
+ String cancel = context.getString(R.string.cancel_download);
+ // This PendingIntent can be used to cancel the worker
+ PendingIntent intent = WorkManager.getInstance(context)
+ .createCancelPendingIntent(getId());
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createChannel();
+ }
+
+ return new NotificationCompat.Builder(context, id)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setOnlyAlertOnce(true)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setSmallIcon(android.R.drawable.stat_sys_download)
+ .setOngoing(true)
+ // Add the cancel action to the notification which can
+ // be used to cancel the worker
+ .addAction(android.R.drawable.ic_delete, cancel, intent);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private void createChannel() {
+ CharSequence name = getApplicationContext().getString(R.string.channel_name);
+ int importance = NotificationManager.IMPORTANCE_LOW;
+ NotificationChannel channel = new NotificationChannel(getApplicationContext().getString(R.string.notification_channel_id), name, importance);
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ notificationManager.createNotificationChannel(channel);
+ }
+
+ enum DownloadType {
+ LOOKUP,
+ PROFILE,
+ SEGMENT
+ }
+
+ interface DownloadProgressListener {
+ void onDownloadStart(String downloadName, DownloadType downloadType);
+
+ void onDownloadInfo(String info);
+
+ void onDownloadProgress(int max, int progress);
+
+ void onDownloadFinished();
+ }
+}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java b/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
deleted file mode 100644
index a97f296..0000000
--- a/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package btools.routingapp;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioAttributes;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.core.app.NotificationCompat;
-
-
-import static android.content.Context.NOTIFICATION_SERVICE;
-
-public class NotificationHelper {
-
- private static final boolean DEBUG = false;
-
- public static String BRouterNotificationChannel1 = "brouter_channel_01";
-
- private Context mContext;
- private int NOTIFICATION_ID = 111;
- private Notification mNotification;
- private NotificationManager mNotificationManager;
- private PendingIntent mContentIntent;
- private CharSequence mContentTitle;
-
- public NotificationHelper(Context context) {
- if (DEBUG) Log.d("NH", "init ");
- mContext = context;
- createNotificationChannels();
- }
-
- public void startNotification(Service service) {
- if (DEBUG) Log.d("NH", "startNotification ");
-
- mNotification = createNotification("BRouter Download", "Download some files");
-
- if (service != null) service.startForeground(NOTIFICATION_ID, mNotification);
-
- mNotificationManager.notify(NOTIFICATION_ID, mNotification);
-
- }
-
- public void progressUpdate(String text) {
- mNotification = createNotification("BRouter Download", text);
- mNotification.flags = Notification.FLAG_NO_CLEAR |
- Notification.FLAG_ONGOING_EVENT;
-
- mNotificationManager.notify(NOTIFICATION_ID, mNotification);
- }
-
-
- public Notification createNotification(String title, String desc) {
-
- Intent resultIntent = new Intent(mContext, BInstallerActivity.class);
-
- Intent notificationIntent = new Intent();
- mContentIntent = PendingIntent.getActivity(mContext, 0, resultIntent, PendingIntent.FLAG_IMMUTABLE);
-
- mNotificationManager = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-
-
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, BRouterNotificationChannel1);
- builder.setSmallIcon(android.R.drawable.stat_sys_download)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setContentTitle(title)
- .setContentText(desc)
- .setTicker(desc)
- .setOngoing(true)
- .setAutoCancel(true)
- .setOnlyAlertOnce(true)
- .setCategory(NotificationCompat.CATEGORY_SERVICE)
- .setContentIntent(mContentIntent);
-
- return builder.build();
-
- } else {
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
- builder.setSmallIcon(android.R.drawable.stat_sys_download)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setContentTitle(title)
- .setContentText(desc)
- .setOnlyAlertOnce(true)
- .setCategory(NotificationCompat.CATEGORY_SERVICE)
- .setContentIntent(mContentIntent);
-
- return builder.build();
- }
-
- }
-
- /**
- * create notification channels
- */
- public void createNotificationChannels() {
- if (DEBUG) Log.d("NH", "createNotificationChannels ");
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-
- NotificationManager sNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- // Sound channel
- CharSequence name = "BRouter Download";
- // The user-visible description of the channel.
- String description = "BRouter Download Channel"; //getString(R.string.channel_description);
-
- NotificationChannel channel = new NotificationChannel(BRouterNotificationChannel1, name, NotificationManager.IMPORTANCE_LOW);
- channel.setDescription(description);
- AudioAttributes att = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_UNKNOWN)
- .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
- .build();
- channel.setSound(null, null);
- channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
-
- sNotificationManager.createNotificationChannel(channel);
-
- }
- }
-
- public void stopNotification() {
- if (DEBUG) Log.d("NH", "stopNotification ");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- mNotificationManager.deleteNotificationChannel(BRouterNotificationChannel1);
- }
- mNotificationManager.cancel(NOTIFICATION_ID);
- }
-}
diff --git a/brouter-routing-app/src/main/res/layout/activity_binstaller.xml b/brouter-routing-app/src/main/res/layout/activity_binstaller.xml
new file mode 100644
index 0000000..5f0e7c1
--- /dev/null
+++ b/brouter-routing-app/src/main/res/layout/activity_binstaller.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/brouter-routing-app/src/main/res/values/strings.xml b/brouter-routing-app/src/main/res/values/strings.xml
index b74d0f0..6663c01 100644
--- a/brouter-routing-app/src/main/res/values/strings.xml
+++ b/brouter-routing-app/src/main/res/values/strings.xml
@@ -15,7 +15,22 @@
-->
+
+ - %d segment
+ - %d segments
+
BRouter
+ Cancel Download
Import Profile
filename.brf
+ Starting download…
+ Cancelling…
+ Download %s
+ Delete %s
+ Update %s
+ Select segments
+ Size=%s\nFree=%s
+ brouter_download
+ Download Segments
+ Downloads
diff --git a/brouter-routing-app/src/main/res/values/styles.xml b/brouter-routing-app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..2d2ee81
--- /dev/null
+++ b/brouter-routing-app/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/brouter-server/build.gradle b/brouter-server/build.gradle
index cb0c78e..69d5959 100644
--- a/brouter-server/build.gradle
+++ b/brouter-server/build.gradle
@@ -56,6 +56,7 @@ distributions {
exclude('**/softaccess.brf')
from ('../misc') {
include 'readmes/*'
+ include 'readmes/osmand/*'
include 'profiles2/*'
}
from ('../brouter-routing-app/build/outputs/apk/api19/release') {
diff --git a/brouter-server/src/main/java/btools/server/SuspectManager.java b/brouter-server/src/main/java/btools/server/SuspectManager.java
index f9a62f1..c0b552c 100644
--- a/brouter-server/src/main/java/btools/server/SuspectManager.java
+++ b/brouter-server/src/main/java/btools/server/SuspectManager.java
@@ -11,6 +11,8 @@ import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.TreeSet;
+import btools.router.SuspectInfo;
+
public class SuspectManager extends Thread
{
private static SimpleDateFormat dfTimestampZ = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" );
@@ -364,7 +366,7 @@ public class SuspectManager extends Thread
if ( "falsepositive".equals( command ) )
{
int wps = NearRecentWps.count( id );
- if ( wps < 8 )
+ if ( wps < 0 ) // FIXME
{
message = "marking false-positive requires at least 8 recent nearby waypoints from BRouter-Web, found: " + wps;
}
@@ -428,6 +430,12 @@ public class SuspectManager extends Thread
br.close();
}
+ // get triggers
+ int triggers = suspects.trigger4Id( id );
+ String triggerText = SuspectInfo.getTriggerText( triggers );
+
+
+
String url1 = "http://brouter.de/brouter-web/#map=18/" + dlat + "/" + dlon
+ "/OpenStreetMap&lonlats=" + dlon + "," + dlat + "&profile=" + profile;
@@ -456,6 +464,7 @@ public class SuspectManager extends Thread
{
bw.write( "" + message + "
\n" );
}
+ bw.write( "Trigger: " + triggerText + "
\n" );
bw.write( "Open in BRouter-Web
\n" );
bw.write( "Open in OpenStreetmap
\n" );
bw.write( "Open in JOSM (via remote control)
\n" );
@@ -585,6 +594,7 @@ public class SuspectManager extends Thread
int cnt;
long[] ids;
int[] prios;
+ int[] triggers;
boolean[] newOrConfirmed;
boolean[] falsePositive;
long timestamp;
@@ -594,10 +604,23 @@ public class SuspectManager extends Thread
cnt = count;
ids = new long[cnt];
prios = new int[cnt];
+ triggers = new int[cnt];
newOrConfirmed = new boolean[cnt];
falsePositive = new boolean[cnt];
timestamp = time;
}
+
+ int trigger4Id( long id )
+ {
+ for( int i = 0; i allSuspectsMap = new HashMap();
@@ -654,6 +677,7 @@ public class SuspectManager extends Thread
pointer = prioCount[nprio]++;
allSuspects.ids[pointer] = id;
allSuspects.prios[pointer] = prio;
+ allSuspects.triggers[pointer] = tk2.hasMoreTokens() ? Integer.parseInt( tk2.nextToken() ) : 0;
allSuspects.newOrConfirmed[pointer] = new File( "confirmednegatives/" + id ).exists() || !(new File( "suspectarchive/" + id ).exists() );
allSuspects.falsePositive[pointer] = new File( "falsepositives/" + id ).exists();
}
diff --git a/brouter-util/src/main/java/btools/util/ProgressListener.java b/brouter-util/src/main/java/btools/util/ProgressListener.java
index eda4efe..4a6d473 100644
--- a/brouter-util/src/main/java/btools/util/ProgressListener.java
+++ b/brouter-util/src/main/java/btools/util/ProgressListener.java
@@ -1,8 +1,9 @@
package btools.util;
+
public interface ProgressListener
{
- public void updateProgress( String progress );
+ public void updateProgress(String task, int progress);
public boolean isCanceled();
}
diff --git a/build.gradle b/build.gradle
index 7e9984f..3e0f897 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,7 +21,7 @@ allprojects {
// this file
// app: build.gradle (versionCode only)
// OsmTrack (version and versionDate)
- project.version "1.6.2"
+ project.version "1.6.3"
group 'org.btools'
repositories {
diff --git a/misc/pbfparser/OsmParser.java b/misc/pbfparser/OsmParser.java
index 58039b8..9c5ee42 100644
--- a/misc/pbfparser/OsmParser.java
+++ b/misc/pbfparser/OsmParser.java
@@ -140,6 +140,11 @@ public class OsmParser extends MapCreatorBase
rListener.nextRelation( r );
if ( fromWid == null || toWid == null || viaNid == null || viaNid.size() != 1 )
{
+ // dummy-TR for each viaNid
+ for( int vi = 0; vi < ( viaNid == null ? 0 : viaNid.size() ); vi++ )
+ {
+ rListener.nextRestriction( r, 0L, 0L, viaNid.get( vi ) );
+ }
return;
}
for( int fi = 0; fi < fromWid.size(); fi++ )
@@ -152,7 +157,7 @@ public class OsmParser extends MapCreatorBase
}
catch( Exception e )
{
- throw new RuntimeException( "error writing relation: " + e );
+ throw new RuntimeException( "error writing relation", e );
}
}
diff --git a/misc/profiles2/car-eco-de.brf b/misc/profiles2/car-eco-de.brf
deleted file mode 100644
index 136bce2..0000000
--- a/misc/profiles2/car-eco-de.brf
+++ /dev/null
@@ -1,182 +0,0 @@
-#
-# Car-Routing based on a kinematic model
-#
-# Depending on the vmax-parameter (target-speed)
-# this can be anything from least-time routing to eco-routing
-#
-#
----model:btools.router.KinematicModel
-
----context:global
-
-# kinematic parameters
-
-assign vmax = 90 # kmh
-assign recup_efficiency = 0.7 # (ratio)
-assign totalweight = 1640 # kg
-assign f_roll = 232 # Newton
-assign f_air = 0.4 # 0.5*cw*A*rho
-assign f_recup = 400 # Newton
-assign p_standby = 250 # Watt
-
-# technical parameters
-
-assign forceSecondaryData = true
-assign validForCars = true
-assign pass1coefficient = 1.3
-assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style
-
----context:way # following code refers to way-tags
-
-assign initialcost switch route=ferry 20000 0
-
-#
-# calculate logical car access
-#
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- switch highway=motorway|motorway_link 1
- switch highway=trunk|trunk_link 1
- switch highway=primary|primary_link 1
- switch highway=secondary|secondary_link 1
- switch highway=tertiary|tertiary_link 1
- switch highway=unclassified 1
- switch route=ferry 1
- switch highway=residential|living_street 1
- switch highway=service 1
- 0
- access=yes|permissive|designated|destination
- vehicle=yes|designated|destination
- motor_vehicle=yes|permissive|designated|destination
- motorcar=yes|permissive|designated|destination
-
-assign accessspeedlimit = if caraccess then 999 else 0
-
-assign isbadoneway = if reversedirection=yes then ( if oneway= then junction=roundabout else oneway=yes|true|1 ) else oneway=-1
-assign onewayspeedlimit = if isbadoneway then 0 else 999
-
-assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
-
-assign maxspeed_surface =
- switch or surface= surface=paved|asphalt|concrete 999
- switch surface=paving_stones|cobblestone 30
- 2
-
-assign maxspeed_tracktype =
- switch tracktype= 999
- switch tracktype=grade1 40
- switch tracktype=grade2 5
- 1
-
-assign maxspeed_implicit =
- switch highway=motorway 999
- switch highway=motorway_link 130
- switch highway=trunk 250
- switch highway=trunk_link 100
- switch highway=primary|primary_link 100
- switch highway=secondary|secondary_link 90
- switch highway=tertiary|tertiary_link 80
- switch highway=unclassified 50
- switch route=ferry 10
- switch highway=bridleway 10
- switch highway=residential|living_street 30
- switch highway=service 30
- switch highway=track|road|path switch tracktype=grade1 30 5
- 0
-
-assign maxspeed =
- min onewayspeedlimit
- min accessspeedlimit
- switch maxspeed=50 50
- switch maxspeed=30 30
- switch maxspeed=10 10
- switch maxspeed=20 20
- switch maxspeed=40 40
- switch maxspeed=60 60
- switch maxspeed=70 70
- switch maxspeed=80 80
- switch maxspeed=90 90
- switch maxspeed=100 100
- switch maxspeed=110 110
- switch maxspeed=120 120
- switch maxspeed=130 130
- switch maxspeed=urban 50
- switch maxspeed=rural 100
- min maxspeed_implicit
- min maxspeed_surface maxspeed_tracktype
-
-assign costfactor = if equal maxspeed 0 then 10000 else 0
-
-assign minspeed =
- switch highway=motorway|trunk 75 0
-
-# way priorities used for voice hint generation
-
-assign priorityclassifier =
-
- if ( highway=motorway ) then 30
- else if ( highway=motorway_link ) then 29
- else if ( highway=trunk ) then 28
- else if ( highway=trunk_link ) then 27
- else if ( highway=primary ) then 26
- else if ( highway=primary_link ) then 25
- else if ( highway=secondary ) then 24
- else if ( highway=secondary_link ) then 23
- else if ( highway=tertiary ) then 22
- else if ( highway=tertiary_link ) then 21
- else if ( highway=unclassified ) then 20
- else if ( highway=residential|living_street ) then 6
- else if ( highway=service ) then 6
- else if ( highway=track ) then if tracktype=grade1 then 4 else 2
- else if ( highway=bridleway|road ) then 2
- else 0
-
-# some more classifying bits used for voice hint generation...
-
-assign isgoodoneway = if reversedirection=yes then oneway=-1
- else if oneway= then junction=roundabout else oneway=yes|true|1
-assign isroundabout = junction=roundabout
-assign isgoodforcars = if greater priorityclassifier 6 then true
- else if highway=residential|living_street|service then true
- else if ( and highway=track tracktype=grade1 ) then true
- else false
-
-# ... encoded into a bitmask
-
-assign classifiermask add isbadoneway
- add multiply isgoodoneway 2
- add multiply isroundabout 4
- add multiply islinktype 8
- add multiply isgoodforcars 16
- multiply highway=residential|living_street 32
-
-
----context:node # following code refers to node tags
-
-#
-# calculate logical car access to nodes
-#
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- not barrier=gate|bollard|lift_gate|cycle_barrier
- access=yes|permissive|designated|destination
- vehicle=yes|permissive|designated|destination
- motor_vehicle=yes|permissive|designated|destination
- motorcar=yes|permissive|designated|destination
-
-assign initialcost =
-
- switch caraccess
- 0
- 1000000
-
-assign maxspeed =
-
- if or crossing=traffic_signals highway=traffic_signals then 0
- else 999
diff --git a/misc/profiles2/car-eco-suspect_scan.brf b/misc/profiles2/car-eco-suspect_scan.brf
deleted file mode 100644
index 802f394..0000000
--- a/misc/profiles2/car-eco-suspect_scan.brf
+++ /dev/null
@@ -1,214 +0,0 @@
-#
-# Car-Routing based on a kinematic model
-#
-# Depending on the vmax-parameter (target-speed)
-# this can be anything from least-time routing to eco-routing
-#
-#
----model:btools.router.KinematicModel
-
----context:global
-
-# kinematic parameters
-
-assign vmax = 90 # kmh
-assign recup_efficiency = 0.7 # (ratio)
-assign totalweight = 1640 # kg
-assign f_roll = 232 # Newton
-assign f_air = 0.4 # 0.5*cw*A*rho
-assign f_recup = 400 # Newton
-assign p_standby = 250 # Watt
-
-# technical parameters
-
-assign validForCars = true
-assign pass1coefficient = 1.3
-assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style
-
----context:way # following code refers to way-tags
-
-assign initialcost switch route=ferry 20000 0
-
-#
-# calculate logical car access
-#
-assign isresidentialorliving = or highway=residential|living_street living_street=yes
-
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- switch highway=motorway|motorway_link 1
- switch highway=trunk|trunk_link 1
- switch highway=primary|primary_link 1
- switch highway=secondary|secondary_link 1
- switch highway=tertiary|tertiary_link 1
- switch highway=unclassified 1
- switch route=ferry 1
- switch isresidentialorliving 1
- switch highway=service 1
- 0
- access=yes|permissive|designated|destination
- vehicle=yes|designated|destination
- motor_vehicle=yes|permissive|designated|destination
- motorcar=yes|permissive|designated|destination
-
-assign unknowncaraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- access=unknown
- vehicle=unknown
- motor_vehicle=unknown
- motorcar=unknown
-
-assign psvaccess
- or bus=yes|designated|urban
- or psv=yes|designated
- or hov=yes|designated
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- access=psv|hov
- vehicle=psv|hov
- motor_vehicle=psv|hov
- motorcar=psv|hov
-
-assign accessspeedlimit = if caraccess then 999 else 0
-
-assign isroundabout = junction=roundabout|circular
-assign implicitoneway = or isroundabout highway=motorway
-assign isbadoneway = if reversedirection=yes then ( if oneway= then implicitoneway else oneway=yes|true|1 ) else oneway=-1
-assign onewayspeedlimit = if isbadoneway then 0 else 999
-
-assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
-
-assign maxspeed_surface =
- switch or surface= surface=paved|asphalt|concrete 999
- switch surface=paving_stones|cobblestone 30
- 2
-
-assign maxspeed_tracktype =
- switch tracktype= 999
- switch tracktype=grade1 40
- switch tracktype=grade2 5
- 1
-
-assign maxspeed_implicit =
- switch highway=motorway 999
- switch highway=motorway_link 130
- switch highway=trunk 250
- switch highway=trunk_link 100
- switch highway=primary|primary_link 100
- switch highway=secondary|secondary_link 90
- switch highway=tertiary|tertiary_link 80
- switch highway=unclassified 50
- switch route=ferry 10
- switch highway=bridleway 10
- switch isresidentialorliving 30
- switch highway=service 30
- switch highway=track|road|path switch tracktype=grade1 30 5
- 0
-
-assign maxspeed =
- min onewayspeedlimit
- min accessspeedlimit
- switch maxspeed=50 50
- switch maxspeed=30 30
- switch maxspeed=10 10
- switch maxspeed=20 20
- switch maxspeed=40 40
- switch maxspeed=60 60
- switch maxspeed=70 70
- switch maxspeed=80 80
- switch maxspeed=90 90
- switch maxspeed=100 100
- switch maxspeed=110 110
- switch maxspeed=120 120
- switch maxspeed=130 130
- switch maxspeed=urban 50
- switch maxspeed=rural 100
- min maxspeed_implicit
- min maxspeed_surface maxspeed_tracktype
-
-assign minspeed =
- switch highway=motorway|trunk 75 0
-
-# way priorities used for voice hint generation
-
-assign priorityclassifier =
-
- if ( highway=motorway ) then 30
- else if ( highway=motorway_link ) then 29
- else if ( highway=trunk ) then 28
- else if ( highway=trunk_link ) then 27
- else if ( highway=primary ) then 26
- else if ( highway=primary_link ) then 25
- else if ( highway=secondary ) then 24
- else if ( highway=secondary_link ) then 23
- else if ( highway=tertiary ) then 22
- else if ( highway=tertiary_link ) then 21
- else if ( highway=unclassified ) then 20
- else if ( isresidentialorliving ) then 6
- else if ( highway=service ) then 6
- else if ( highway=track ) then if tracktype=grade1 then 4 else 2
- else if ( highway=bridleway|road ) then 2
- else 0
-
-assign costfactor = if equal maxspeed 0
- then ( if ( and equal accessspeedlimit 0 and greater onewayspeedlimit 0 and greater priorityclassifier 20 not psvaccess )
- then switch unknowncaraccess 9997 9998
- else 10000 )
- else 0
-
-# some more classifying bits used for voice hint generation...
-
-assign isgoodoneway = if reversedirection=yes then oneway=-1
- else if oneway= then implicitoneway else oneway=yes|true|1
-assign isgoodforcars = if greater priorityclassifier 6 then true
- else if or isresidentialorliving highway=service then true
- else if ( and highway=track tracktype=grade1 ) then true
- else false
-
-# ... encoded into a bitmask
-
-assign classifiermask add isbadoneway
- add multiply isgoodoneway 2
- add multiply isroundabout 4
- add multiply islinktype 8
- add multiply isgoodforcars 16
- multiply isresidentialorliving 32
-
-
----context:node # following code refers to node tags
-
-#
-# calculate logical car access to nodes
-#
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- not barrier=gate|bollard|lift_gate|cycle_barrier
- access=yes|permissive|designated|destination
- vehicle=yes|permissive|designated|destination
- motor_vehicle=yes|permissive|designated|destination
- motorcar=yes|permissive|designated|destination
-
-assign initialcost =
-
- switch caraccess
- 0
- 1000000
-
-assign maxspeed =
-
- if or crossing=traffic_signals highway=traffic_signals
- then
- switch greater way:priorityclassifier 24 5
- switch greater way:priorityclassifier 22 3
- switch greater way:priorityclassifier 20 1
- 0
- else 999
diff --git a/misc/profiles2/car-traffic_analysis.brf b/misc/profiles2/car-traffic_analysis.brf
deleted file mode 100644
index ba12476..0000000
--- a/misc/profiles2/car-traffic_analysis.brf
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# Car-Routing is experimantal !!!
-#
-# DO NOT USE FOR ACTUAL NAVIGATION
-#
-# Turn restrictions are missing, leading to wrong routes
-#
-
----context:global
-
-assign downhillcost 0
-assign downhillcutoff 0
-assign uphillcost 0
-assign uphillcutoff 0
-
-assign validForCars 1
-assign considerTurnRestrictions = false
-
-assign pass1coefficient 0.
-assign pass2coefficient -1
-
----context:way # following code refers to way-tags
-
-
-assign turncost = 0
-
-assign initialcost switch route=ferry 20000 0
-
-
-#
-# calculate logical car access
-#
-assign isresidentialorliving = or highway=residential|living_street living_street=yes
-
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- switch or highway=motorway highway=motorway_link 1
- switch or highway=trunk highway=trunk_link 1
- switch or highway=primary highway=primary_link 1
- switch or highway=secondary highway=secondary_link 1
- switch or highway=tertiary highway=tertiary_link 1
- switch highway=unclassified 1
- switch route=ferry 1
- switch isresidentialorliving 1
- switch highway=service 1
- 0
- or access=yes or access=permissive or access=designated access=destination
- or vehicle=yes or vehicle=designated vehicle=destination
- or motor_vehicle=yes or motor_vehicle=permissive or motor_vehicle=designated motor_vehicle=destination
- or motorcar=yes or motorcar=permissive or motorcar=designated motorcar=destination
-
-assign accesspenalty
- switch caraccess
- 0
- 10000
-
-assign onewaypenalty
- switch switch reversedirection=yes
- switch oneway=
- junction=roundabout
- or oneway=yes or oneway=true oneway=1
- oneway=-1
- 10000
- 0.0
-
-
-assign ispaved or surface=paved or surface=asphalt or surface=concrete surface=paving_stones
-
-
-assign costfactor
-
- add max onewaypenalty accesspenalty
-
- switch and highway= not route=ferry 10000
-
- switch or highway=motorway highway=motorway_link 1
- switch or highway=trunk highway=trunk_link 1
- switch or highway=primary highway=primary_link 1.2
- switch or highway=secondary highway=secondary_link 1.6
- switch or highway=tertiary highway=tertiary_link 2.0
- switch highway=unclassified 2.5
- switch route=ferry 5.67
- switch highway=bridleway 5
- switch isresidentialorliving 3.5
- switch highway=service 3.5
- switch or highway=track or highway=road highway=path
- switch tracktype=grade1 5
- switch ispaved 5
- 30
- 10000
-
-assign trafficsourcedensity =
- if isresidentialorliving then 1
- else if and maxspeed=30|50 highway=tertiary|secondary|primary then 1
- else 0
-
-assign istrafficbackbone
- if greater costfactor 9999 then false
- else if highway=motorway|motorway_link then true
- else false
-
----context:node # following code refers to node tags
-
-#
-# calculate logical car access to nodes
-#
-assign caraccess
- switch motorcar=
- switch motor_vehicle=
- switch vehicle=
- switch access=
- switch barrier=gate 0
- switch barrier=bollard 0
- switch barrier=lift_gate 0
- switch barrier=cycle_barrier 0
- 1
- or access=yes or access=permissive or access=designated access=destination
- or vehicle=yes or vehicle=permissive or vehicle=designated vehicle=destination
- or motor_vehicle=yes or motor_vehicle=permissive or motor_vehicle=designated motor_vehicle=destination
- or motorcar=yes or motorcar=permissive or motorcar=designated motorcar=destination
-
-assign initialcost
- switch caraccess
- 0
- 1000000
diff --git a/misc/profiles2/dummy.brf b/misc/profiles2/dummy.brf
index 3fb4ba1..1ed4e62 100644
--- a/misc/profiles2/dummy.brf
+++ b/misc/profiles2/dummy.brf
@@ -2,6 +2,7 @@
# this prevents suppression of unused tags, so they are visibly in the data tab
assign processUnusedTags = true
+assign validForFoot = true
---context:way # following code refers to way-tags
diff --git a/misc/profiles2/hiking-beta.brf b/misc/profiles2/hiking-beta.brf
deleted file mode 100644
index 3e0869c..0000000
--- a/misc/profiles2/hiking-beta.brf
+++ /dev/null
@@ -1,305 +0,0 @@
-# Profile for medium-level hiking
-#
-# See https://github.com/poutnikl/Brouter-profiles for
-# more variants and poutnik's profile collection
-
-# *** Based on Trekking profile, added flag driven customizations
-# 10/6/2015 v1.7.2 BETA - Fixed general way access penalties
-# See bottom for Changelog and verbose comments.
-
----context:global
-
-assign consider_elevation 1 # 0 as default, otherwise less interesting flat roads are chosen.
-assign shortest_way 0 # 0 as default
-
-assign iswet 0 # 0 as default, 1 tries to prevent muddy boots and wet buttocks
-
-assign prefer_hiking_routes 1 # 1 as default, gives penalty to way not being hiking route and cancels route turncost
-assign stick_to_hiking_routes 0 # 0 as default, as above, but gives higher penalty costfactor + way/node initial costs
-assign non_hiking_route_penalty 0.15 # 0.25 as default, used with prefer_hiking_routes
-assign non_sticky_route_penalty 0.5 # 1.0 as default, used stick_to_hiking_routes #v1.5
-
-#SAC - mountain hiking - see http://wiki.openstreetmap.org/wiki/Key:sac_scale
-assign SAC_scale_limit 3 # 0..6, 0 to avoid any SAC paths, 1 for T1 as maximum, 6 for T6 as maximum
- # all paths with sac_scale higher than SAC_scale_limit are forbidden.
-assign SAC_scale_preferred 1 # The same, but the preferred SAC scale level. Level below are slightly, above strongly penalized
-assign SAC_access_penalty 9000 # costfactor 9999 means the most horrible but allowed road.,
- # 100000=forbidden. This makes difference if forbidden way is the only option.
-
-
-#orientation/decision penalties, not used for preferred hiking routes
-assign turncost_value 0 # 20 as default, not used now
-assign initialcost_value 0 # 50 as default, not used now
-
-#less frequently changed flags
-assign allow_steps 1 # 1 as default
-assign allow_ferries 1 # 1 as default
-assign cost_of_unknown 2 # 2 as default
-#
-assign elevationpenaltybuffer 5 # 5 as default
-assign elevationmaxbuffer 10 # 10 as default
-assign elevationbufferreduce 0.0 # 0.0 as default
-
-# as initial point, considered flat speed 4 km/h, climbing speed 600 m /h
-assign uphillcostvalue 7
-assign uphillcutoffvalue 3
-assign downhillcutoffvalue 3
-assign downhillcostvalue 7
-
-assign downhillcost switch consider_elevation downhillcostvalue 0
-assign downhillcutoff switch consider_elevation downhillcutoffvalue 0
-assign uphillcost switch consider_elevation uphillcostvalue 0
-assign uphillcutoff switch consider_elevation uphillcutoffvalue 0
-
-assign validForFoot 1
-
-assign hr_preferred or prefer_hiking_routes stick_to_hiking_routes
-
-#Penalizing SAC routes below (K1) and above(K2) preferred SAC scale
-#see http://wiki.openstreetmap.org/wiki/Key:sac_scale
-
-assign SAC_K1 0.1 # Penalizing of SAC levels below preferred
-assign SAC_K2 0.6 # Penalizing of SAC levels above preferred
-
-#all the extra complexity of code below, with adding +/- 1.0
-#is to keep final penalties additive, even with multiplicative incremental penalty approach
-#code is run only once, being in global context
-
-assign SAC_K10 add SAC_K1 1.0
-assign SAC_K20 add SAC_K2 1.0
-assign SAC_K1_2 add multiply SAC_K10 SAC_K10 -1.0
-assign SAC_K2_2 add multiply SAC_K20 SAC_K20 -1.0
-assign SAC_K1_3 add ( multiply ( add SAC_K1_2 1.0 ) SAC_K10 ) -1.0
-assign SAC_K2_3 add ( multiply ( add SAC_K2_2 1.0 ) SAC_K20 ) -1.0
-assign SAC_K1_4 add ( multiply ( add SAC_K1_3 1.0 ) SAC_K10 ) -1.0
-assign SAC_K2_4 add ( multiply ( add SAC_K2_3 1.0 ) SAC_K20 ) -1.0
-assign SAC_K1_5 add ( multiply ( add SAC_K1_4 1.0 ) SAC_K10 ) -1.0
-assign SAC_K2_5 add ( multiply ( add SAC_K2_4 1.0 ) SAC_K20 ) -1.0
-assign SAC_K1_6 add ( multiply ( add SAC_K1_5 1.0 ) SAC_K10 ) -1.0
-assign SAC_K2_6 add ( multiply ( add SAC_K2_5 1.0 ) SAC_K20 ) -1.0
-
-
----context:way # following code refers to way-tags
-
-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 any_hiking_route or route=hiking or route_hiking_iwn=yes
- or route_hiking_nwn=yes or route_hiking_rwn=yes
- or route_hiking_lwn=yes or route_hiking_=yes
- or route_foot_=yes or route_foot_nwn=yes
- or route_foot_rwn=yes route_foot_lwn=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_ldhr and any_hiking_route hr_preferred
-assign nodeaccessgranted any_hiking_route
-
-# ismuddy addresses potentially bad surface conditions during wet weather ( mud, slickiness of grass)
-assign ismuddy and or isunpaved surface=
- and iswet
- not or surface=gravel surface=pebblestone
-
-assign issidewalk not or sidewalk= or sidewalk=none sidewalk=no
-
-assign turncost switch or shortest_way is_ldhr 0 turncost_value #v1.5
-
-assign initialclassifier =
- if route=ferry then 1
- else 0
-
-assign initialcost
- switch route=ferry 10000
-
- switch or shortest_way is_ldhr 0 switch stick_to_hiking_routes initialcost_value 0
-
-assign defaultaccess switch access= not motorroad=yes switch or access=private access=no 0 1
-
-
-assign footaccess or any_hiking_route
- or issidewalk
- switch foot= defaultaccess switch foot=private|no 0 1
-
-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 footaccess
- or and bikeaccess not foot=no
- or bicycle=dismount
- switch foot=
- defaultaccess
- not or foot=private foot=no
-
-assign accesspenalty switch footaccess 0 switch bikeaccess 4 100000
-
-#
-
-#SAC is estimated path difficulty,
-#integrating both MTB and SAC scales with estimated MTB/SAC difficulty matching
-#see http://wiki.openstreetmap.org/wiki/Key:mtb:scale
-# http://wiki.openstreetmap.org/wiki/Key:sac_scale
-
-assign SAC
-if sac_scale=difficult_alpine_hiking then 6
-else if or sac_scale=demanding_alpine_hiking mtb:scale=6 then 5
-else if or sac_scale=alpine_hiking mtb:scale=5 then 4
-else if or sac_scale=demanding_mountain_hiking mtb:scale=4 then 3
-else if or sac_scale=mountain_hiking mtb:scale=3|2+ then 2
-else if or sac_scale=hiking|T1-hiking|yes mtb:scale=2|1+|2- then 1
- else 0
-
-assign SAC_scale_access # if SAC_scale_limit < SAC then true else false
- if sac_scale= then true else equal ( max SAC_scale_limit SAC ) SAC_scale_limit
-
-assign SAC_scale_penalty
-
- if not SAC_scale_access then SAC_access_penalty # not allowed SAC scale
- else if equal SAC SAC_scale_preferred then 0.0
- else if equal ( add SAC 1 ) SAC_scale_preferred then SAC_K1
- else if equal ( add SAC 2 ) SAC_scale_preferred then SAC_K1_2
- else if equal ( add SAC 3 ) SAC_scale_preferred then SAC_K1_3
- else if equal ( add SAC 4 ) SAC_scale_preferred then SAC_K1_4
- else if equal ( add SAC 5 ) SAC_scale_preferred then SAC_K1_5
- else if equal ( add SAC 6 ) SAC_scale_preferred then SAC_K1_6
- else if equal ( add SAC_scale_preferred 1 ) SAC then SAC_K2
- else if equal ( add SAC_scale_preferred 2 ) SAC then SAC_K2_2
- else if equal ( add SAC_scale_preferred 3 ) SAC then SAC_K2_3
- else if equal ( add SAC_scale_preferred 4 ) SAC then SAC_K2_4
- else if equal ( add SAC_scale_preferred 5 ) SAC then SAC_K2_5
- else if equal ( add SAC_scale_preferred 6 ) SAC then SAC_K2_6
- else 1.0
-
-assign costfactor
-
- if shortest_way then ( add 1 accesspenalty ) else
-
- add ( switch is_ldhr 0.0 # hiking route, no non-hiking road penalty
- switch stick_to_hiking_routes non_sticky_route_penalty
- switch prefer_hiking_routes non_hiking_route_penalty
- 0.0 # no hiking route preference
- )
- add accesspenalty
- add SAC_scale_penalty
-
- switch ( and highway= not route=ferry ) 100000
- switch highway=steps ( switch allow_steps ( switch consider_elevation 1.0 3.0 ) 100000 )
- switch route=ferry switch allow_ferries 2.34 100000
-
-# iswet=1 in global context section means wet weather, increases penalty for eventually inconvenient ways
-# ismuddy boolean relates in wet weather to unpaved or unclassified surface, that can have mud or get slicky in wet weather.
-
- switch highway=pedestrian switch ismuddy 1.3 1.0
- switch highway=bridleway switch ismuddy 2.5 1.2
- switch highway=cycleway switch ismuddy 1.4
- switch iswet 1.0 1.2
- switch highway=residential|living_street
- switch ismuddy 1.5
- switch iswet 1.0 1.1
- switch highway=service switch ismuddy 1.5
- switch iswet 1.1 1.3
-
- switch highway=track|road|path|footway
- switch tracktype=grade1 switch ismuddy 1.1
- switch iswet 1.0 1.21
- switch tracktype=grade2 switch ismuddy 1.25
- switch iswet 1.11 1.12
- switch tracktype=grade3 switch ismuddy 1.4
- switch iswet 1.21 1.07
- switch tracktype=grade4 switch ismuddy 1.7
- switch iswet 1.31 1.05
- switch tracktype=grade5 switch ismuddy 2.0
- switch iswet 1.5 1.01
-
- switch ismuddy 2.01
- switch iswet 1.51 1.12
-
- switch highway=motorway|motorway_link 100000
- switch highway=proposed|abandoned switch ismuddy 3 1.5
- switch highway=construction switch ismuddy 10 2.5
-
- switch highway=trunk|trunk_link|primary|primary_link switch iswet switch issidewalk 2.5 5
- switch issidewalk 5 10
- switch highway=secondary|secondary_link switch iswet switch issidewalk 2.0 2.5
- switch issidewalk 2.5 4.0
- switch highway=tertiary|tertiary_link|unclassified switch ismuddy 2.0 switch iswet switch issidewalk 1.4 1.7
- switch issidewalk 1.7 2.0
-
- add cost_of_unknown ( switch ismuddy 0.5 0.0 )
-
-# include `smoothness=` tags in the response's WayTags for track analysis
-assign dummyUsage = smoothness=
-
----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 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 footaccess
- or bicycle=dismount
- switch foot=
- defaultaccess
- switch or foot=private foot=no
- 0
- 1
-
-assign initialcost switch or bikeaccess footaccess 0 1000000
-
-# changelog:
-# *** Based on Trekking profile, added flag driven customizations
-# Hiking Version 1.6 ALFA
-# 2014-10-10 (c) Poutnik
-# Route_foot related hints were improved with usage of Hiking.brf from below and by Arndt comment
-# http://pastebin.com/YLpkGVD4
-# http://forum.openstreetmap.org/viewtopic.php?pid=447507#p447507
-#
-# 2014-10-10 v1.1 - changed hiking route preference logic
-# 1.2 - fixed preference counting bug
-# 2014-10-11 1.3 - adding foot route to hiking routes,
-# fixed ferry cost to respect initial cost triggerring.
-# added bikeaccess, added shortest way mode, code cleanup
-# 2014-10-12: v1.4 - involving turncosts and way/node initial costs as orientation/decision penalties,
-# but turning them off for sticking to hiking routes as extra preference,
-# tweaking cost preferences
-# 2014-10-13 v1.5
-# redesigned route preference policy - turncost turned off even for nonsticky but preferred hiking routes
-# cost tweaking.
-# removed uniform cost=1 for sticky hiking routes, to distinguish quality
-# giving penalty 1 for non hiking routes at sticky hiking routes.
-# used not round costs to often trigger initial cost calculation
-#v1.51 - bugfix of redundant routing penalty
-#v 1.6 - initialcost penalty only for sticking routes, decreased way penalties for preferring routes
-# 31/5/2015 v 1.7 ALFA - sac_scale + sac_scale_limit implemented
-# 10/6/2015 v1.7.1 ALFA - sac_scale improved, MTB scale integrated to SAC scale
-# sac_scale_preferred implemented, with progressive penalizing for SAC <> SAC preferred
-# 10/6/2015 v1.7.2 BETA - Fixed general way access penalties
-
-
diff --git a/misc/profiles2/hiking-mountain.brf b/misc/profiles2/hiking-mountain.brf
new file mode 100644
index 0000000..9af93c6
--- /dev/null
+++ b/misc/profiles2/hiking-mountain.brf
@@ -0,0 +1,485 @@
+# Walking-Hiking-Mountain/Alpine Hiking profile TEMPLATE
+# 18/5/2016 v1.8.7 ! Fixed down/uphillcostfactors for shortest_way - to be really shortest
+#
+# SAC T3 - demanding_mountain_hiking - exposed sites may be secured, possible need of hands for balance, Partly exposed with fall hazard, Well sure-footed, Good hiking shoes, Basic alpine experience
+#
+# Legend above is placeholder for generated comments of final profile
+# See the profile bottom for changelogs and verbose *) comments
+# See also https://github.com/poutnikl/Brouter-profiles/wiki
+# and https://github.com/poutnikl/Hiking-Poutnik
+
+---context:global
+
+assign consider_elevation 1 # 0 as default, otherwise less interesting flat roads are chosen.
+assign shortest_way 0 # 0 as default, duplicate shortest standard profile, SAC access limit ignored for now
+
+assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style
+assign turnInstructionCatchingRange 20 # V1.8.5 / default=40, but foot paths may be more distingushed, especially in cities.
+
+assign iswet 0 # 0 as default, 1 tries to prevent muddy boots and wet buttocks
+assign hiking_routes_preference 0.20 # 0.10 as default, Increases cost of non hiking routes by multiplier 1 + hiking_routes_preference
+
+assign Offroad_factor 0.0 # default 0.0, see ****), see also https://github.com/poutnikl/Brouter-profiles/wiki/Trekking-MTB-Profiles---legend
+
+assign path_preference 0.0 # 0.0 as default, try 20.0 to penalize nonpath ways a/o paved ways
+
+#SAC - mountain hiking - see http://wiki.openstreetmap.org/wiki/Key:sac_scale
+
+assign SAC_scale_limit 3 # 0..6, 0 to avoid any SAC paths, 1 for T1 as maximum, 6 for T6 as maximum
+ # all paths with sac_scale higher than SAC_scale_limit are forbidden.
+assign SAC_scale_preferred 1 # The same, but the preferred SAC scale level. Level below are slightly, above strongly penalized
+assign SAC_access_penalty 9000 # costfactor 9999 means the most horrible but allowed road.,
+ # 100000=forbidden. This makes difference if forbidden way is the only option.
+assign SAC_K1 0.05 # Penalizing of SAC levels below preferred
+assign SAC_K2 0.6 # Penalizing of SAC levels above preferred
+
+#orientation/decision penalties, not used for preferred hiking routes
+assign turncost_value 0 # not used now
+assign initialcost_value 0 # not used now
+
+#less frequently changed flags
+assign allow_steps 1 # 1 as default
+assign allow_ferries 1 # 1 as default
+assign cost_of_unknown 2 # 2 as default
+#
+assign elevationpenaltybuffer 5 # 5 as default
+assign elevationmaxbuffer 10 # 10 as default
+assign elevationbufferreduce 1.0 # 0.0 as default
+
+# as initial point, considered flat speed 4 km/h, climbing speed 600 m /h
+assign uphillcostvalue 7
+assign uphillcutoffvalue 3
+assign downhillcutoffvalue 3
+assign downhillcostvalue 7
+
+#internal parameters
+assign Offroad_hillcostfactor multiply -0.3333 ( max -3.0 ( multiply -1.0 ( max 0.0 Offroad_factor ) ) )
+ # for Offroadfactor <=0 is 0, for Offroadfactor >=3 is 1, otherwise 0.3333 * Offroadfactor
+ # progressively decreases hillcosts to be 0.0 at Offroad_factor = 3.0
+ # if Offroad_factor = 1 , then downhillcost decreases e.g. from 60 to 40
+
+assign downhillcost if consider_elevation then
+ ( multiply ( add 1.0 ( multiply Offroad_hillcostfactor -1.0 ) ) downhillcostvalue ) else 0
+
+assign uphillcost if consider_elevation then
+ ( multiply ( add 1.0 ( multiply Offroad_hillcostfactor -1.0 ) ) uphillcostvalue ) else 0
+
+assign uphillcutoff if consider_elevation then uphillcutoffvalue else 1.5
+assign downhillcutoff if consider_elevation then downhillcutoffvalue else 1.5
+
+assign nonhiking_route_penalty add 1.0 max 0.0 hiking_routes_preference
+
+assign validForFoot 1
+
+#Penalizing SAC routes below (K1) and above(K2) preferred SAC scale
+#see http://wiki.openstreetmap.org/wiki/Key:sac_scale
+
+#SAC_scale_penalty:
+#Penalty is SAC_access_penalty for SAC > SAC_scale_limit
+#Penalty is 0.0 for SAC_scale_preferred = SAC, SAC <= SAC_scale_limit
+#Penalty is ( 1 + SAC_K1)^(SAC_scale_preferred - SAC) -1 for SAC_scale_preferred > SAC, SAC <= SAC_scale_limit
+#Penalty is ( 1 + SAC_K2)^(SAC - SAC_scale_preferred) -1 for SAC_scale_preferred < SAC, SAC <= SAC_scale_limit
+
+#extra complexity of code below, with adding +/- 1.0
+#is to keep final penalties additive, even with multiplicative incremental penalty approach
+#code is run only once, being in global context
+
+assign SAC_K10 add SAC_K1 1.0
+assign SAC_K20 add SAC_K2 1.0
+
+assign SAC_K1_2 add multiply SAC_K10 SAC_K10 -1.0
+assign SAC_K2_2 add multiply SAC_K20 SAC_K20 -1.0
+
+assign SAC_K1_3 add ( multiply ( add SAC_K1_2 1.0 ) SAC_K10 ) -1.0
+assign SAC_K2_3 add ( multiply ( add SAC_K2_2 1.0 ) SAC_K20 ) -1.0
+assign SAC_K1_4 add ( multiply ( add SAC_K1_3 1.0 ) SAC_K10 ) -1.0
+assign SAC_K2_4 add ( multiply ( add SAC_K2_3 1.0 ) SAC_K20 ) -1.0
+assign SAC_K1_5 add ( multiply ( add SAC_K1_4 1.0 ) SAC_K10 ) -1.0
+assign SAC_K2_5 add ( multiply ( add SAC_K2_4 1.0 ) SAC_K20 ) -1.0
+assign SAC_K1_6 add ( multiply ( add SAC_K1_5 1.0 ) SAC_K10 ) -1.0
+assign SAC_K2_6 add ( multiply ( add SAC_K2_5 1.0 ) SAC_K20 ) -1.0
+
+
+---context:way # following code refers to way-tags
+
+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 any_hiking_route or route=hiking or route_hiking_iwn=yes
+ or route_hiking_nwn=yes or route_hiking_rwn=yes
+ or route_hiking_lwn=yes or route_hiking_=yes
+ or route_foot_=yes or route_foot_nwn=yes
+ or route_foot_rwn=yes route_foot_lwn=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_ldhr and any_hiking_route not equal 0.0 hiking_routes_preference
+assign nodeaccessgranted any_hiking_route
+
+# ismuddy addresses potentially bad surface conditions during wet weather ( mud, slickiness of grass)
+assign ismuddy and or isunpaved surface=
+ and iswet
+ not or surface=gravel surface=pebblestone
+
+assign issidewalk sidewalk=left|right|both|yes
+
+assign istrack highway=track|road|path|footway
+assign ismainroad highway=motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link|unclassified
+
+#assign turncost switch or shortest_way is_ldhr 0 turncost_value #v1.5
+assign turncost 0 #v1.8.3
+
+assign initialcost
+ switch route=ferry 10000
+
+ switch or shortest_way is_ldhr 0 initialcost_value
+
+assign defaultaccess switch access= not motorroad=yes switch or access=private access=no 0 1
+
+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 footaccess or any_hiking_route
+ or issidewalk
+ or and bikeaccess not foot=no
+ or bicycle=dismount
+ switch foot= defaultaccess not foot=private|no
+
+assign accesspenalty switch footaccess 0 switch bikeaccess 4 100000
+
+assign badoneway = 0
+assign onewaypenalty = 0
+
+
+#SAC is estimated path difficulty,
+#integrating both MTB and SAC scales with estimated MTB/SAC difficulty matching
+#see http://wiki.openstreetmap.org/wiki/Key:mtb:scale
+# http://wiki.openstreetmap.org/wiki/Key:sac_scale
+
+assign SAC
+ if sac_scale= then (
+
+ if mtb:scale= then 0
+
+ else if mtb:scale=6|5 then 5
+ else if mtb:scale=4 then 4
+ else if mtb:scale=3 then 3
+ else if mtb:scale=2-|2|2+ then 2
+ else if mtb:scale=1+|1 then 1
+ else 0
+ )
+ else
+
+ if sac_scale=difficult_alpine_hiking then 6
+ else if sac_scale=demanding_alpine_hiking then 5
+ else if sac_scale=alpine_hiking then 4
+ else if sac_scale=demanding_mountain_hiking then 3
+ else if sac_scale=mountain_hiking then 2
+ else if sac_scale=hiking|T1-hiking|yes then 1
+ else 0
+
+assign SAC_scale_access # if SAC_scale_limit < SAC then true else false
+ if sac_scale= then true else equal ( max SAC_scale_limit SAC ) SAC_scale_limit
+
+assign SAC_scale_penalty
+
+ if not SAC_scale_access then SAC_access_penalty # not allowed SAC scale
+
+ else if equal SAC SAC_scale_preferred then 0.0
+ else if equal ( add SAC 1 ) SAC_scale_preferred then SAC_K1
+ else if equal ( add SAC 2 ) SAC_scale_preferred then SAC_K1_2
+ else if equal ( add SAC 3 ) SAC_scale_preferred then SAC_K1_3
+ else if equal ( add SAC 4 ) SAC_scale_preferred then SAC_K1_4
+ else if equal ( add SAC 5 ) SAC_scale_preferred then SAC_K1_5
+ else if equal ( add SAC 6 ) SAC_scale_preferred then SAC_K1_6
+ else if equal ( add SAC_scale_preferred 1 ) SAC then SAC_K2
+ else if equal ( add SAC_scale_preferred 2 ) SAC then SAC_K2_2
+ else if equal ( add SAC_scale_preferred 3 ) SAC then SAC_K2_3
+ else if equal ( add SAC_scale_preferred 4 ) SAC then SAC_K2_4
+ else if equal ( add SAC_scale_preferred 5 ) SAC then SAC_K2_5
+ else if equal ( add SAC_scale_preferred 6 ) SAC then SAC_K2_6
+ else 1.0
+
+assign tracktype_penalty (
+ if not istrack then 0.0 else if tracktype= then 0.0
+ else if tracktype=grade1 then 0.1 else if tracktype=grade2 then 0.05
+ else if tracktype=grade3 then 0.0 else if tracktype=grade4 then 0.0
+ else if tracktype=grade5 then 0.0 else 0.0 )
+
+assign surface_penalty (
+ if not istrack then 0.0
+ else if ispaved then 0.0
+ else if surface=cobblestone then 0.0
+ else if surface=fine_gravel|ground|earth|unpaved|grass|compacted then 0.0
+ else if surface=dirt|sand then 0.1
+ else if surface= then 0.0
+ else if surface=gravel|pebblestone then 0.2
+ else 0.0 )
+
+assign wet_penalty (
+ if not iswet then 0.0
+ else if ismainroad then -0.1
+ else if tracktype=grade1 then ( if ispaved then -0.2 else if ismuddy then 0.2 else 0.1 )
+ else if tracktype=grade2 then ( if ispaved then -0.1 else if ismuddy then 0.4 else 0.2 )
+ else if tracktype=grade3 then ( if ispaved then -0.0 else if ismuddy then 0.8 else 0.3 )
+ else if tracktype=grade4 then ( if ispaved then 0.1 else if ismuddy then 1.5 else 0.5 )
+ else if tracktype=grade5 then ( if ispaved then 0.2 else if ismuddy then 2.5 else 1.0 )
+ else ( if ispaved then -0.2 else if ismuddy then 2.5 else 1.0 )
+ )
+
+assign Offroad_factor_for_road
+ if ( equal Offroad_factor 0.0 ) then 0.0 else
+ (
+ if ismainroad then Offroad_factor
+ else if ( or ispaved highway=residential|living_street|service|pedestrian ) then ( multiply 0.33 Offroad_factor )
+ else if ( not isunpaved ) then ( multiply -0.33 Offroad_factor )
+ else ( multiply -1 multiply Offroad_factor ( add 1.0 ( multiply 0.33 SAC_scale_penalty ) ) )
+ )
+
+assign nonpath_penalty =
+if ( equal path_preference 0.0 ) then 0.0 # nonpath_penalty inactive
+else if not istrack then path_preference #istrack = highway=track/path/road/footway
+else if ispaved then ( multiply path_preference 0.5 )
+else if or ( and not isunpaved not highway=path )
+ ( tracktype=grade1|grade2 ) then ( multiply path_preference 0.25 )
+else if not ( and isunpaved
+ and highway=path
+ and tracktype=grade1|grade2
+ not surface=gravel|cobblestone|pebblestone )
+ then ( multiply path_preference 0.125 )
+else 0.0
+
+assign rawcostfactor # can be <1.0, is treated later
+
+ if shortest_way then ( add 1 accesspenalty ) else
+
+ add nonpath_penalty
+ add accesspenalty
+ (
+ if ( and highway= not route=ferry ) then 100000
+ else if highway=steps then ( switch allow_steps ( switch consider_elevation 1.0 3.0 ) 100000 )
+ else if route=ferry then ( if allow_ferries then 2.34 else 100000 )
+
+# iswet=1 in global context section means wet weather, increases penalty for eventually inconvenient ways
+# ismuddy boolean relates in wet weather to unpaved or unclassified surface, that can have mud or get slicky in wet weather.
+
+ else if highway=pedestrian then ( switch ismuddy 1.3 1.0 )
+ else if highway=bridleway then ( switch ismuddy 2.5 switch iswet 1.4 1.2 )
+ else if highway=cycleway then ( switch ismuddy 1.4 switch iswet 1.0 1.1 )
+ else if highway=residential|living_street
+ then ( switch ismuddy 1.5 switch iswet 1.0 1.1 )
+ else if highway=service then ( switch ismuddy 1.5 switch iswet 1.1 1.2 )
+
+ else if istrack then
+ ( add 1.0 add tracktype_penalty add surface_penalty add wet_penalty SAC_scale_penalty )
+
+ else if highway=motorway|motorway_link then 100000
+ else if highway=proposed|abandoned|construction then ( switch ismuddy 10 switch iswet 6 4 )
+
+ else if highway=trunk|trunk_link then
+ ( switch iswet ( switch issidewalk 1.5 10 ) ( switch issidewalk 2.0 20 ) )
+ else if highway=primary|primary_link then
+ ( switch iswet ( switch issidewalk 1.5 5 ) ( switch issidewalk 2.0 10 ) )
+ else if highway=secondary|secondary_link then
+ ( switch iswet ( switch issidewalk 1.2 2.5 ) ( switch issidewalk 1.5 4.0 ) )
+ else if highway=tertiary|tertiary_link then
+ ( if iswet then ( switch issidewalk 1.1 1.5 ) else ( switch issidewalk 1.2 2.5 ) )
+ else if highway=unclassified then
+ ( if ismuddy then 3.0 else if iswet then ( switch issidewalk 1.0 1.3 ) else ( switch issidewalk 1.1 1.5 ) )
+
+ else add cost_of_unknown switch ismuddy 0.5 0.0
+ )
+
+assign rawcostfactor2
+
+ if equal hiking_routes_preference 0.0 then rawcostfactor # Not preferring hiking routes
+ else if is_ldhr then rawcostfactor # is hiking route
+ else multiply rawcostfactor nonhiking_route_penalty
+
+assign costfactor if shortest_way then ( add 1 accesspenalty )
+ else max 1.0 add rawcostfactor2 Offroad_factor_for_road
+
+assign downhillcostfactor
+ if shortest_way then ( add 1 accesspenalty ) else
+ max 1.0
+ add rawcostfactor2
+ add Offroad_factor_for_road
+ if ismuddy then 0.5 # slicky
+ else if surface=grass then -0.2 # soft impact
+ else if ispaved then 0.2 # hard impact
+ else if surface=gravel|pebblestone|fine_gravel then 0.3 # slides
+ else 0.0
+
+assign uphillcostfactor
+ if shortest_way then ( add 1 accesspenalty ) else
+ max 1.0
+ add rawcostfactor2
+ add Offroad_factor_for_road
+ if ismuddy then 0.3 # slicky
+ else if surface=grass then 0.1 # unsure foot
+ else if ispaved then -0.1 # sure foot
+ else if surface=gravel|pebblestone|fine_gravel then 0.2 # slides
+ else 0.0
+
+# way priorities used for voice hint generation
+
+assign priorityclassifier =
+
+ if ( highway=motorway ) then 30
+ else if ( highway=motorway_link ) then 29
+ else if ( highway=trunk ) then 28
+ else if ( highway=trunk_link ) then 27
+ else if ( highway=primary ) then 26
+ else if ( highway=primary_link ) then 25
+ else if ( highway=secondary ) then 24
+ else if ( highway=secondary_link ) then 23
+ else if ( highway=tertiary ) then 22
+ else if ( highway=tertiary_link ) then 21
+ else if ( highway=unclassified ) then 20
+ else if ( highway=residential|living_street ) then 18
+ else if ( highway=steps|pedestrian ) then 16
+ else if ( highway=service|cycleway ) then if ( or tracktype=grade1 ispaved ) then 14 else 12
+ else if ( highway=track|road|bridleway ) then if ( or tracktype=grade1 ispaved ) then 10 else 8
+ else if ( highway=path|footway ) then ( if ( or tracktype=grade1 ispaved ) then 6
+ else if tracktype=grade2 then 4
+ else if not surface=grass|gravel then 3
+ else 2 )
+ else 0
+
+# some more classifying bits used for voice hint generation...
+
+assign isbadoneway = 0
+assign isgoodoneway = 0
+assign isroundabout = junction=roundabout
+assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
+assign isgoodforcars = if greater priorityclassifier 19 then true
+ else if highway=residential|living_street|service then true
+ else if ( and highway=track tracktype=grade1 ) then true
+ else false
+
+# ... encoded into a bitmask
+
+assign classifiermask add isbadoneway
+ add multiply isgoodoneway 2
+ add multiply isroundabout 4
+ add multiply islinktype 8
+ multiply isgoodforcars 16
+
+---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 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 footaccess
+ or bicycle=dismount
+ switch foot=
+ defaultaccess
+ switch or foot=private foot=no
+ 0
+ 1
+
+assign initialcost switch or bikeaccess footaccess 0 1000000
+
+#############################################################################################
+# ****) Offroad_factor ( ported MTB_factor from bicycle Trekking profile
+#############################################################################################
+#
+# MTB_factor tweaks/trims MTB approach of the profile by preferring/penalizing in progressive order
+# nonpaved - preferred
+# not paved - little preferred
+# paved - little penalized
+# mainroads - penalized
+#
+# MTB_factor can be used for one-time tweaking of routing profile for particular trip,
+# or trimming of the profile according to biker preferencing without need of profile deep insight
+#
+# Positive values progessively promote/penalize roads in favour of MTB riding.
+
+# Negative value has the opposite effect, preferring mainroads and penalizing unpaved roads.
+# This effect is somewhat similar to iswet=1 ( *) wet weather mode ),
+# but does not distinguish particular road classes / surfaces / smoothness,
+# aside of mentioned schema below.
+
+# The calculated values below is added to the costfactor.
+
+# + MTB_factor for main roads (tertiaries and better),
+# + 0.33 * MTB_factor for paved roads,
+# - 0.33 * MTB_factor for not paved/not unpaved roads,
+# - MTB_factor * ( 1 + 0.33 * smoothnesspenalty ) for unpaved roads. - at MTB_factor 3.0 smootheness is ignored
+#
+# Default is 0.0 = no effect.
+# Recommended -0.5 - +1.0
+# Reasonable -2.0 .. +3.0,
+#
+# Final costfactor is kept >= 1 for final costfacto values.
+#############################################################################################
+# changelog:
+# Feature is / + added / - removed / * changed / ! fixed
+
+# Hiking Version 1.6 ALFA
+# 2014-10-10 (c) Poutnik
+# Route_foot related hints were improved with usage of Hiking.brf from below and by Arndt comment
+# http://pastebin.com/YLpkGVD4
+# http://forum.openstreetmap.org/viewtopic.php?pid=447507#p447507
+#
+# 2014-10-10 v1.1 - changed hiking route preference logic
+# 1.2 - fixed preference counting bug
+# 2014-10-11 1.3 - adding foot route to hiking routes,
+# fixed ferry cost to respect initial cost triggerring.
+# added bikeaccess, added shortest way mode, code cleanup
+# 2014-10-12: v1.4 - involving turncosts and way/node initial costs as orientation/decision penalties,
+# but turning them off for sticking to hiking routes as extra preference,
+# tweaking cost preferences
+# 2014-10-13 v1.5
+# redesigned route preference policy - turncost turned off even for nonsticky but preferred hiking routes
+# cost tweaking.
+# removed uniform cost=1 for sticky hiking routes, to distinguish quality
+# giving penalty 1 for non hiking routes at sticky hiking routes.
+# used not round costs to often trigger initial cost calculation
+#v1.51 - bugfix of redundant routing penalty
+#v 1.6 - initialcost penalty only for sticking routes, decreased way penalties for preferring routes
+# 31/5/2015 v 1.7 ALFA + sac_scale + sac_scale_limit implemented
+# 10/6/2015 v1.7.1 ALFA * sac_scale improved
+# + MTB scale integrated to SAC scale
+# + sac_scale_preferred implemented, with progressive penalizing for SAC <> SAC preferred
+# 10/6/2015 v1.7.2 BETA ! Fixed general way access penalties
+# 15/6/2015 v1.7.3 BETA * SAC-MTB scale integration reevaluated, increased MTB scale penalty
+# * MTB scale penalty used as fallback if no SAC rating
+# 16/6/2015 v1.7.4 ALFA * Optimized SAC logic
+# 17/6/2015 v1.7.5 BETA + Track penalty system
+# 20/6/2015 v1.7.6 RELEASE * Modified and simplified route preferencing
+# 01/12/2015 v1.8.1 ALFA +ported MTB_factor from bicycle Trekking template, renamed for hiking context to Offroad_factor
+# 02/4/2016 v1.8.2 ALFA +implemented path preference factor , try 20.0. Default 0.0 has no effect
+# 3/5/2016 v1.8.3 BETA +implemented navigation hint support
+# 7/5/2016 v1.8.4 RELEASE +implemented navigation hint support ftom BRouter 1.4
+# 10/5/2016 v1.8.6 BETA * decreased turnInstructionCatchingRange from default 40 to 20
+# 18/5/2016 v1.8.7 BETA ! Fixed down/uphillcostfactors for shortest_way - to be really shortest
diff --git a/misc/profiles2/rail.brf b/misc/profiles2/rail.brf
index 3cb411d..c8ed079 100644
--- a/misc/profiles2/rail.brf
+++ b/misc/profiles2/rail.brf
@@ -13,8 +13,8 @@ assign turncost 0
assign initialcost 0
assign costfactor
- switch railway=rail 1
- switch railway=narrow_gauge 1
+ switch railway=rail|light_rail|narrow_gauge 1
+ switch railway=tram|subway 2
100000
---context:node # following code refers to node tags