Merge branch 'master' into docs
This commit is contained in:
commit
a75570a027
47 changed files with 1924 additions and 2342 deletions
12
.github/workflows/gradle-publish.yml
vendored
12
.github/workflows/gradle-publish.yml
vendored
|
@ -12,6 +12,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: BRouter
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
@ -27,10 +28,19 @@ jobs:
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
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
|
- 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
|
run: gradle build
|
||||||
|
|
||||||
|
|
||||||
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
||||||
# the publishing section of your build.gradle
|
# the publishing section of your build.gradle
|
||||||
- name: Publish to GitHub Packages
|
- name: Publish to GitHub Packages
|
||||||
|
|
12
.github/workflows/gradle.yml
vendored
12
.github/workflows/gradle.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: BRouter
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
|
@ -24,7 +24,17 @@ jobs:
|
||||||
cache: gradle
|
cache: gradle
|
||||||
- name: Create local.properties
|
- name: Create local.properties
|
||||||
run: touch 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
|
- 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
|
run: ./gradlew build
|
||||||
- name: Upload ZIP
|
- name: Upload ZIP
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
|
|
@ -98,7 +98,7 @@ Segments files from the whole planet are generated weekly at
|
||||||
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
||||||
|
|
||||||
You can download one or more segments files, covering the area of the planet
|
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
|
#### Generate your own segments files
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
private int latBase;
|
private int latBase;
|
||||||
private int cellsize;
|
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
|
super( databuffer ); // sets ab=databuffer, aboffset=0
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
return b;
|
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 );
|
super( null );
|
||||||
cellsize = 1000000 / divisor;
|
cellsize = 1000000 / divisor;
|
||||||
|
|
|
@ -255,8 +255,8 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
|
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
|
||||||
{
|
{
|
||||||
if ( rc.inverseDirection
|
if ( rc.inverseDirection
|
||||||
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, 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.carMode ) )
|
: TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode ) )
|
||||||
{
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -33,8 +33,8 @@ import btools.util.StringUtils;
|
||||||
|
|
||||||
public final class OsmTrack
|
public final class OsmTrack
|
||||||
{
|
{
|
||||||
final public static String version = "1.6.2";
|
final public static String version = "1.6.3";
|
||||||
final public static String versionDate = "10102021";
|
final public static String versionDate = "21122021";
|
||||||
|
|
||||||
// csv-header-line
|
// csv-header-line
|
||||||
private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
|
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 )
|
if ( turnInstructionMode == 2 )
|
||||||
{
|
{
|
||||||
sb.append( " <extensions><locus:rteComputeType>" ).append( "" + voiceHints.getLocusRouteType() ).append( "</locus:rteComputeType></extensions>\n" );
|
sb.append( " <extensions>\n" );
|
||||||
sb.append( " <extensions><locus:rteSimpleRoundabouts>1</locus:rteSimpleRoundabouts></extensions>\n" );
|
sb.append( " <locus:rteComputeType>" ).append( "" + voiceHints.getLocusRouteType() ).append( "</locus:rteComputeType>\n" );
|
||||||
|
sb.append( " <locus:rteSimpleRoundabouts>1</locus:rteSimpleRoundabouts>\n" );
|
||||||
|
sb.append( " </extensions>\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append( " <trkseg>\n" );
|
sb.append( " <trkseg>\n" );
|
||||||
|
|
|
@ -147,8 +147,8 @@ public final class RoutingContext
|
||||||
|
|
||||||
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
|
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
|
||||||
|
|
||||||
// turn-restrictions used per default for car profiles
|
// turn-restrictions not used per default for foot profiles
|
||||||
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", 1.f );
|
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)
|
// process tags not used in the profile (to have them in the data-tab)
|
||||||
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
|
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
|
||||||
|
|
66
brouter-core/src/main/java/btools/router/SuspectInfo.java
Normal file
66
brouter-core/src/main/java/btools/router/SuspectInfo.java
Normal file
|
@ -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<Long,SuspectInfo> 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -622,8 +622,8 @@ public abstract class BExpressionContext implements IByteArrayUnifier
|
||||||
if ( lookupData2 != null )
|
if ( lookupData2 != null )
|
||||||
{
|
{
|
||||||
// do not create unknown value for external data array,
|
// do not create unknown value for external data array,
|
||||||
// record as 'other' instead
|
// record as 'unknown' instead
|
||||||
lookupData2[inum] = 0;
|
lookupData2[inum] = 1; // 1 == unknown
|
||||||
if (bFoundAsterix) {
|
if (bFoundAsterix) {
|
||||||
// found value for lookup *
|
// found value for lookup *
|
||||||
//System.out.println( "add unknown " + name + " " + value );
|
//System.out.println( "add unknown " + name + " " + value );
|
||||||
|
|
|
@ -260,30 +260,11 @@ public class OsmCutter extends MapCreatorBase
|
||||||
@Override
|
@Override
|
||||||
public void nextRestriction( RelationData r, long fromWid, long toWid, long viaNid ) throws Exception
|
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" );
|
String type = r.getTag( "type" );
|
||||||
if ( type == null || !"restriction".equals( type ) )
|
if ( type == null || !"restriction".equals( type ) )
|
||||||
{
|
{
|
||||||
return;
|
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;
|
short exceptions = 0;
|
||||||
String except = r.getTag( "except" );
|
String except = r.getTag( "except" );
|
||||||
if ( except != null )
|
if ( except != null )
|
||||||
|
@ -296,9 +277,17 @@ public class OsmCutter extends MapCreatorBase
|
||||||
exceptions |= toBit( "hgv" , 4, except );
|
exceptions |= toBit( "hgv" , 4, except );
|
||||||
}
|
}
|
||||||
|
|
||||||
// System.out.println( "restriction id = " + r.rid + " isPositive=" + isPositive + " fromWid = " + fromWid + " toWid = " + toWid+ " viaNid = " + viaNid );
|
for( String restrictionKey : r.getTagsOrNull().keySet() )
|
||||||
|
{
|
||||||
|
if ( !( restrictionKey.equals( "restriction" ) || restrictionKey.startsWith( "restriction:" ) ) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String restriction = r.getTag( restrictionKey );
|
||||||
|
|
||||||
RestrictionData res = new RestrictionData();
|
RestrictionData res = new RestrictionData();
|
||||||
res.isPositive = isPositive;
|
res.restrictionKey = restrictionKey;
|
||||||
|
res.restriction = restriction;
|
||||||
res.exceptions = exceptions;
|
res.exceptions = exceptions;
|
||||||
res.fromWid = fromWid;
|
res.fromWid = fromWid;
|
||||||
res.toWid = toWid;
|
res.toWid = toWid;
|
||||||
|
@ -312,7 +301,7 @@ public class OsmCutter extends MapCreatorBase
|
||||||
{
|
{
|
||||||
restrictionCutter.nextRestriction( res );
|
restrictionCutter.nextRestriction( res );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static short toBit( String tag, int bitpos, String s )
|
private static short toBit( String tag, int bitpos, String s )
|
||||||
|
|
|
@ -175,11 +175,11 @@ public class OsmNodeP extends OsmLinkP
|
||||||
RestrictionData r = getFirstRestriction();
|
RestrictionData r = getFirstRestriction();
|
||||||
while( r != null )
|
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.writeBoolean( true ); // restriction follows
|
||||||
mc.writeShort( r.exceptions );
|
mc.writeShort( r.exceptions );
|
||||||
mc.writeBoolean( r.isPositive );
|
mc.writeBoolean( r.isPositive() );
|
||||||
mc.writeInt( r.fromLon );
|
mc.writeInt( r.fromLon );
|
||||||
mc.writeInt( r.fromLat );
|
mc.writeInt( r.fromLat );
|
||||||
mc.writeInt( r.toLon );
|
mc.writeInt( r.toLon );
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package btools.mapcreator;
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
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
|
* Container for a turn restriction
|
||||||
|
@ -12,26 +17,125 @@ import btools.util.LongList;
|
||||||
*/
|
*/
|
||||||
public class RestrictionData extends MapCreatorBase
|
public class RestrictionData extends MapCreatorBase
|
||||||
{
|
{
|
||||||
public boolean isPositive;
|
public String restrictionKey;
|
||||||
|
public String restriction;
|
||||||
public short exceptions;
|
public short exceptions;
|
||||||
public long fromWid;
|
public long fromWid;
|
||||||
public long toWid;
|
public long toWid;
|
||||||
public long viaNid;
|
public long viaNid;
|
||||||
public RestrictionData next;
|
public RestrictionData next;
|
||||||
|
|
||||||
|
public int viaLon;
|
||||||
|
public int viaLat;
|
||||||
|
|
||||||
public int fromLon;
|
public int fromLon;
|
||||||
public int fromLat;
|
public int fromLat;
|
||||||
|
|
||||||
public int toLon;
|
public int toLon;
|
||||||
public int toLat;
|
public int toLat;
|
||||||
|
|
||||||
|
public boolean badWayMatch;
|
||||||
|
|
||||||
|
private static HashMap<String,String> names = new HashMap<>();
|
||||||
|
private static TreeSet<Long> badTRs = new TreeSet<>();
|
||||||
|
|
||||||
public RestrictionData()
|
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
|
public RestrictionData( DataInputStream di ) throws Exception
|
||||||
{
|
{
|
||||||
isPositive = di.readBoolean();
|
restrictionKey = unifyName( di.readUTF() );
|
||||||
|
restriction = unifyName( di.readUTF() );
|
||||||
exceptions = di.readShort();
|
exceptions = di.readShort();
|
||||||
fromWid = readId( di );
|
fromWid = readId( di );
|
||||||
toWid = readId( di );
|
toWid = readId( di );
|
||||||
|
@ -40,7 +144,8 @@ public class RestrictionData extends MapCreatorBase
|
||||||
|
|
||||||
public void writeTo( DataOutputStream dos ) throws Exception
|
public void writeTo( DataOutputStream dos ) throws Exception
|
||||||
{
|
{
|
||||||
dos.writeBoolean( isPositive );
|
dos.writeUTF( restrictionKey );
|
||||||
|
dos.writeUTF( restriction );
|
||||||
dos.writeShort( exceptions );
|
dos.writeShort( exceptions );
|
||||||
writeId( dos, fromWid );
|
writeId( dos, fromWid );
|
||||||
writeId( dos, toWid );
|
writeId( dos, toWid );
|
||||||
|
|
|
@ -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(
|
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] );
|
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,
|
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 );
|
nodesMap.put( res.viaNid, n );
|
||||||
}
|
}
|
||||||
OsmNodePT nt = (OsmNodePT) n;
|
OsmNodePT nt = (OsmNodePT) n;
|
||||||
|
res.viaLon = nt.ilon;
|
||||||
|
res.viaLat = nt.ilat;
|
||||||
res.next = nt.firstRestriction;
|
res.next = nt.firstRestriction;
|
||||||
nt.firstRestriction = res;
|
nt.firstRestriction = res;
|
||||||
ntr++;
|
ntr++;
|
||||||
|
@ -351,37 +356,50 @@ public class WayLinker extends MapCreatorBase implements Runnable
|
||||||
// the leg according to the mapped direction
|
// the leg according to the mapped direction
|
||||||
private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w )
|
private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w )
|
||||||
{
|
{
|
||||||
checkRestriction( n1, n2, w.wid, true );
|
checkRestriction( n1, n2, w, true );
|
||||||
checkRestriction( n2, n1, w.wid, false );
|
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();
|
RestrictionData r = n2.getFirstRestriction();
|
||||||
while ( r != null )
|
while ( r != null )
|
||||||
{
|
{
|
||||||
if ( r.fromWid == wid )
|
if ( r.fromWid == w.wid )
|
||||||
{
|
{
|
||||||
if ( r.fromLon == 0 || checkFrom )
|
if ( r.fromLon == 0 || checkFrom )
|
||||||
{
|
{
|
||||||
r.fromLon = n1.ilon;
|
r.fromLon = n1.ilon;
|
||||||
r.fromLat = n1.ilat;
|
r.fromLat = n1.ilat;
|
||||||
n1.bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
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 )
|
if ( r.toLon == 0 || !checkFrom )
|
||||||
{
|
{
|
||||||
r.toLon = n1.ilon;
|
r.toLon = n1.ilon;
|
||||||
r.toLat = n1.ilat;
|
r.toLat = n1.ilat;
|
||||||
n1.bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
n1.bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
||||||
|
if ( !isEndNode( n2, w ) )
|
||||||
|
{
|
||||||
|
r.badWayMatch = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r = r.next;
|
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
|
@Override
|
||||||
public void nextWay( WayData way ) throws Exception
|
public void nextWay( WayData way ) throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@ public final class DirectWeaver extends ByteDataWriter
|
||||||
|
|
||||||
private int size = 0;
|
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 );
|
super( null );
|
||||||
int cellsize = 1000000 / divisor;
|
int cellsize = 1000000 / divisor;
|
||||||
|
|
|
@ -35,7 +35,7 @@ final class OsmFile
|
||||||
private int ncaches;
|
private int ncaches;
|
||||||
private int indexsize;
|
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.lonDegree = lonDegree;
|
||||||
this.latDegree = latDegree;
|
this.latDegree = latDegree;
|
||||||
|
@ -111,7 +111,7 @@ final class OsmFile
|
||||||
return idx == -1 ? indexsize : posIdx[idx];
|
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 startPos = getPosIdx( subIdx - 1 );
|
||||||
int endPos = getPosIdx( subIdx );
|
int endPos = getPosIdx( subIdx );
|
||||||
|
@ -128,7 +128,7 @@ final class OsmFile
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator,
|
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 );
|
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,12 @@ final public class PhysicalFile
|
||||||
{
|
{
|
||||||
MicroCache.debug = true;
|
MicroCache.debug = true;
|
||||||
|
|
||||||
String message = checkFileIntegrity( new File( args[0] ) );
|
try {
|
||||||
|
checkFileIntegrity( new File( args[0] ) );
|
||||||
if ( message != null )
|
} catch (IOException e) {
|
||||||
{
|
System.err.println( "************************************" );
|
||||||
System.out.println( "************************************" );
|
e.printStackTrace();
|
||||||
System.out.println( message );
|
System.err.println( "************************************" );
|
||||||
System.out.println( "************************************" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +45,7 @@ final public class PhysicalFile
|
||||||
*
|
*
|
||||||
* @return the error message if file corrupt, else null
|
* @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;
|
PhysicalFile pf = null;
|
||||||
try
|
try
|
||||||
|
@ -66,14 +65,6 @@ final public class PhysicalFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException iae)
|
|
||||||
{
|
|
||||||
return iae.getMessage();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return e.toString();
|
|
||||||
}
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if ( pf != null )
|
if ( pf != null )
|
||||||
|
@ -88,7 +79,7 @@ final public class PhysicalFile
|
||||||
return null;
|
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();
|
fileName = f.getName();
|
||||||
byte[] iobuffer = dataBuffers.iobuffer;
|
byte[] iobuffer = dataBuffers.iobuffer;
|
||||||
|
@ -102,7 +93,7 @@ final public class PhysicalFile
|
||||||
short readVersion = (short)(lv >> 48);
|
short readVersion = (short)(lv >> 48);
|
||||||
if ( i == 0 && lookupVersion != -1 && readVersion != lookupVersion )
|
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 );
|
+ lookupVersion + " " + f. getAbsolutePath() + "=" + readVersion );
|
||||||
}
|
}
|
||||||
fileIndex[i] = lv & 0xffffffffffffL;
|
fileIndex[i] = lv & 0xffffffffffffL;
|
||||||
|
|
|
@ -8,8 +8,10 @@ package btools.mapaccess;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
final public class Rd5DiffManager
|
final public class Rd5DiffManager
|
||||||
{
|
{
|
||||||
|
@ -93,17 +95,16 @@ final public class Rd5DiffManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMD5( File f ) throws Exception
|
public static String getMD5( File f ) throws IOException
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
|
||||||
DigestInputStream dis = new DigestInputStream(bis, md);
|
DigestInputStream dis = new DigestInputStream(bis, md);
|
||||||
byte[] buf = new byte[8192];
|
byte[] buf = new byte[8192];
|
||||||
for(;;)
|
for (; ; ) {
|
||||||
{
|
|
||||||
int len = dis.read(buf);
|
int len = dis.read(buf);
|
||||||
if ( len <= 0 )
|
if (len <= 0) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,12 +112,14 @@ final public class Rd5DiffManager
|
||||||
byte[] bytes = md.digest();
|
byte[] bytes = md.digest();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int j = 0; j < bytes.length; j++)
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
{
|
|
||||||
int v = bytes[j] & 0xff;
|
int v = bytes[j] & 0xff;
|
||||||
sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
|
sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IOException("MD5 algorithm not available", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char hexChar( int v )
|
private static char hexChar( int v )
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import btools.codec.DataBuffers;
|
import btools.codec.DataBuffers;
|
||||||
|
@ -49,9 +50,8 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProgress( String progress )
|
public void updateProgress(String task, int progress) {
|
||||||
{
|
System.out.println(task + ": " + progress + "%");
|
||||||
System.out.println( progress );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return false;
|
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];
|
long[] fileIndex = new long[25];
|
||||||
for( int i=0; i<25; i++ )
|
for( int i=0; i<25; i++ )
|
||||||
|
@ -85,7 +85,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return index[tileIndex];
|
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];
|
int[] posIndex = new int[1024];
|
||||||
for( int i=0; i<1024; i++ )
|
for( int i=0; i<1024; i++ )
|
||||||
|
@ -105,7 +105,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return idx == -1 ? 4096 : posIdx[idx];
|
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 )
|
if ( posIdx == null )
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return ab;
|
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 )
|
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 )
|
if ( f2.length() == 0L )
|
||||||
{
|
{
|
||||||
|
@ -341,7 +341,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
|
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
|
||||||
if ( pct != lastPct )
|
if ( pct != lastPct )
|
||||||
{
|
{
|
||||||
progress.updateProgress( "Applying delta: " + pct + "%" );
|
progress.updateProgress("Applying delta", pct);
|
||||||
lastPct = 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;
|
boolean canceled = false;
|
||||||
DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) );
|
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 );
|
int pct = (int)( (100. * sizeRead) / (sizeTotal+1) + 0.5 );
|
||||||
if ( pct != lastPct )
|
if ( pct != lastPct )
|
||||||
{
|
{
|
||||||
progress.updateProgress( "Copying: " + pct + "%" );
|
progress.updateProgress("Copying", pct);
|
||||||
lastPct = pct;
|
lastPct = pct;
|
||||||
}
|
}
|
||||||
int len = dis1.read( buf );
|
int len = dis1.read( buf );
|
||||||
|
@ -756,7 +756,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
this.dataBuffers = dataBuffers;
|
this.dataBuffers = dataBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache readMC() throws Exception
|
public MicroCache readMC() throws IOException
|
||||||
{
|
{
|
||||||
if (skips < 0 )
|
if (skips < 0 )
|
||||||
{
|
{
|
||||||
|
@ -775,7 +775,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return mc;
|
return mc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish() throws Exception
|
public void finish()
|
||||||
{
|
{
|
||||||
skips = -1;
|
skips = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,23 +5,25 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "btools.routingapp"
|
applicationId "btools.routingapp"
|
||||||
|
|
||||||
versionCode 42
|
versionCode 45
|
||||||
versionName project.version
|
versionName project.version
|
||||||
|
|
||||||
resValue('string', 'app_version', defaultConfig.versionName)
|
resValue('string', 'app_version', defaultConfig.versionName)
|
||||||
setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName)
|
setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName)
|
||||||
|
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets.main.assets.srcDirs += new File(project.buildDir, 'assets')
|
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 {
|
signingConfigs {
|
||||||
// this uses a file ~/.gradle/gradle.properties
|
// this uses a file ~/.gradle/gradle.properties
|
||||||
// with content:
|
// with content:
|
||||||
|
@ -49,7 +51,7 @@ android {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
debuggable false
|
debuggable false
|
||||||
if (project.hasProperty("RELEASE_STORE_FILE")) {
|
if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
@ -96,14 +98,21 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.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-mapaccess')
|
||||||
implementation project(':brouter-core')
|
implementation project(':brouter-core')
|
||||||
implementation project(':brouter-expressions')
|
implementation project(':brouter-expressions')
|
||||||
implementation project(':brouter-util')
|
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) {
|
task generateProfiles(type: Exec) {
|
||||||
|
|
|
@ -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<BRouterActivity> rule = new ActivityScenarioRule<>(BRouterActivity.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void storageDirectories() {
|
||||||
|
ActivityScenario<BRouterActivity> scenario = rule.getScenario();
|
||||||
|
scenario.onActivity(activity -> {
|
||||||
|
List<File> 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()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,9 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="btools.routingapp">
|
package="btools.routingapp">
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
@ -11,17 +13,16 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:preserveLegacyExternalStorage="true">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:preserveLegacyExternalStorage="true"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:theme="@style/Theme.App">
|
||||||
<activity
|
<activity
|
||||||
android:name=".BRouterActivity"
|
android:name=".BRouterActivity"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:screenOrientation="unspecified"
|
android:screenOrientation="unspecified">
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
@ -29,20 +30,18 @@
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".BInstallerActivity"
|
android:name=".BInstallerActivity"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:screenOrientation="landscape"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"></activity>
|
android:launchMode="singleTask"
|
||||||
|
android:screenOrientation="landscape" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".BImportActivity"
|
android:name=".BImportActivity"
|
||||||
android:label="Import Profile"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar">
|
android:label="Import Profile">
|
||||||
<!-- some apps (bluemail) do not recognize the .brf file extention, startactivity+intent is done for attachement with text/plain -->
|
<!-- some apps (bluemail) do not recognize the .brf file extension, startactivity+intent is done for attachment with text/plain -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
|
@ -88,15 +87,10 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:exported="true"
|
|
||||||
android:name=".BRouterService"
|
android:name=".BRouterService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
android:process=":brouter_service" />
|
android:process=":brouter_service" />
|
||||||
<service
|
|
||||||
android:name="btools.routingapp.DownloadService"
|
|
||||||
android:label="Download Service"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:enabled="true" />
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
Binary file not shown.
|
@ -13,4 +13,4 @@ profiles_url=https://brouter.de/brouter/profiles2/
|
||||||
# these are comma separated arrays
|
# these are comma separated arrays
|
||||||
|
|
||||||
check_lookup=lookups.dat
|
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
|
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
|
|
@ -1,6 +1,5 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -12,6 +11,7 @@ import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -20,7 +20,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
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
|
// profile size is generally < 30 kb, so set max size to 100 kb
|
||||||
private static final int MAX_PROFILE_SIZE = 100000;
|
private static final int MAX_PROFILE_SIZE = 100000;
|
||||||
private EditText mTextFilename;
|
private EditText mTextFilename;
|
||||||
|
@ -76,8 +76,8 @@ public class BImportActivity extends Activity {
|
||||||
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
|
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
|
||||||
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
|
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
filename = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
|
||||||
filesize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
|
filesize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// is the file extention ".brf" in the file name
|
// is the file extention ".brf" in the file name
|
||||||
|
|
|
@ -1,112 +1,208 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import static btools.routingapp.BInstallerView.MASK_CURRENT_RD5;
|
||||||
import java.util.Set;
|
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.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
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.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;
|
private static final int DIALOG_CONFIRM_DELETE_ID = 1;
|
||||||
|
public static boolean downloadCanceled = false;
|
||||||
|
private File mBaseDir;
|
||||||
private BInstallerView mBInstallerView;
|
private BInstallerView mBInstallerView;
|
||||||
private PowerManager mPowerManager;
|
private Button mButtonDownload;
|
||||||
private WakeLock mWakeLock;
|
private TextView mSummaryInfo;
|
||||||
private DownloadReceiver myReceiver;
|
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
|
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the activity is first created.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Get an instance of the PowerManager
|
|
||||||
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
|
||||||
|
|
||||||
// Create a bright wake lock
|
|
||||||
mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
|
|
||||||
.getName());
|
|
||||||
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||||
|
|
||||||
// instantiate our simulation view and set it as the activity's content
|
setContentView(R.layout.activity_binstaller);
|
||||||
mBInstallerView = new BInstallerView(this);
|
mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
|
||||||
setContentView(mBInstallerView);
|
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<Integer> selectedTilesDownload = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
|
||||||
|
final ArrayList<Integer> selectedTilesUpdate = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
|
||||||
|
final ArrayList<Integer> 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<Integer> downloadList) {
|
||||||
|
ArrayList<String> urlparts = new ArrayList<>();
|
||||||
|
for (Integer i : downloadList) {
|
||||||
|
urlparts.add(baseNameForTile(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadCanceled = false;
|
||||||
|
|
||||||
|
Data inputData = new Data.Builder()
|
||||||
|
.putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0]))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Constraints constraints = new Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
WorkRequest downloadWorkRequest =
|
||||||
|
new OneTimeWorkRequest.Builder(DownloadWorker.class)
|
||||||
|
.setInputData(inputData)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
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
|
@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) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
AlertDialog.Builder builder;
|
AlertDialog.Builder builder;
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
@ -116,7 +212,7 @@ public class BInstallerActivity extends Activity {
|
||||||
.setTitle("Confirm Delete")
|
.setTitle("Confirm Delete")
|
||||||
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
mBInstallerView.deleteSelectedTiles();
|
deleteSelectedTiles();
|
||||||
}
|
}
|
||||||
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
|
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
@ -129,29 +225,76 @@ public class BInstallerActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void showConfirmDelete() {
|
public void showConfirmDelete() {
|
||||||
showDialog(DIALOG_CONFIRM_DELETE_ID);
|
showDialog(DIALOG_CONFIRM_DELETE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Integer> dialogIds = new HashSet<Integer>();
|
private void scanExistingFiles() {
|
||||||
|
mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5);
|
||||||
|
|
||||||
private void showNewDialog(int id) {
|
scanExistingFiles(new File(mBaseDir, "brouter/segments4"));
|
||||||
if (dialogIds.contains(Integer.valueOf(id))) {
|
|
||||||
removeDialog(id);
|
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) {
|
long age = System.currentTimeMillis() - new File(dir, fileName).lastModified();
|
||||||
StatFs stat = new StatFs(baseDir);
|
if (age < 10800000) mBInstallerView.setTileStatus(tileIndex, MASK_CURRENT_RD5); // 3 hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
private void deleteSelectedTiles() {
|
||||||
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_DELETED_RD5);
|
||||||
} else {
|
for (int tileIndex : selectedTiles) {
|
||||||
return stat.getAvailableBlocks() * stat.getBlockSize();
|
new File(mBaseDir, "brouter/segments4/" + baseNameForTile(tileIndex) + ".rd5").delete();
|
||||||
}
|
}
|
||||||
|
scanExistingFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadSelectedTiles() {
|
||||||
|
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
|
||||||
|
downloadAll(selectedTiles);
|
||||||
|
mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadInstalledTiles() {
|
||||||
|
ArrayList<Integer> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import java.io.File;
|
import android.annotation.SuppressLint;
|
||||||
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.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
@ -20,262 +9,37 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.StatFs;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.view.GestureDetector;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import btools.mapaccess.PhysicalFile;
|
import java.io.IOException;
|
||||||
import btools.mapaccess.Rd5DiffManager;
|
import java.io.InputStream;
|
||||||
import btools.mapaccess.Rd5DiffTool;
|
import java.util.ArrayList;
|
||||||
import btools.router.RoutingHelper;
|
|
||||||
import btools.util.ProgressListener;
|
|
||||||
|
|
||||||
public class BInstallerView extends View {
|
public class BInstallerView extends View {
|
||||||
private static final int MASK_SELECTED_RD5 = 1;
|
public static final int MASK_SELECTED_RD5 = 1;
|
||||||
private static final int MASK_DELETED_RD5 = 2;
|
public static final int MASK_DELETED_RD5 = 2;
|
||||||
private static final int MASK_INSTALLED_RD5 = 4;
|
public static final int MASK_INSTALLED_RD5 = 4;
|
||||||
private static final int MASK_CURRENT_RD5 = 8;
|
public static final int MASK_CURRENT_RD5 = 8;
|
||||||
|
private static final float SCALE_GRID_VISIBLE = 3;
|
||||||
private int imgwOrig;
|
private final Bitmap bmp;
|
||||||
private int imghOrig;
|
private final float[] testVector = new float[2];
|
||||||
private float scaleOrig;
|
private final int[] tileStatus;
|
||||||
|
private final Matrix mat;
|
||||||
private int imgw;
|
private final GestureDetector mGestureDetector;
|
||||||
private int imgh;
|
private final ScaleGestureDetector mScaleGestureDetector;
|
||||||
|
Paint paintGrid = new Paint();
|
||||||
private float lastDownX;
|
Paint paintTiles = new Paint();
|
||||||
private float lastDownY;
|
|
||||||
|
|
||||||
private Bitmap bmp;
|
|
||||||
|
|
||||||
private float viewscale;
|
private float viewscale;
|
||||||
|
|
||||||
private float[] testVector = new float[2];
|
|
||||||
|
|
||||||
private int[] tileStatus;
|
|
||||||
|
|
||||||
private boolean tilesVisible = false;
|
private boolean tilesVisible = false;
|
||||||
|
private OnSelectListener mOnSelectListener;
|
||||||
|
|
||||||
private long availableSize;
|
public BInstallerView(Context context, AttributeSet attrs) {
|
||||||
private File baseDir;
|
super(context, attrs);
|
||||||
private File segmentDir;
|
|
||||||
|
|
||||||
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<Integer> 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<Integer> downloadList) {
|
|
||||||
ArrayList<String> 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 {
|
try {
|
||||||
AssetManager assetManager = getContext().getAssets();
|
AssetManager assetManager = getContext().getAssets();
|
||||||
InputStream istr = assetManager.open("world.png");
|
InputStream istr = assetManager.open("world.png");
|
||||||
|
@ -286,56 +50,111 @@ public class BInstallerView extends View {
|
||||||
}
|
}
|
||||||
|
|
||||||
tileStatus = new int[72 * 36];
|
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 = new Matrix();
|
||||||
mat.postScale(viewscale, viewscale);
|
mGestureDetector = new GestureDetector(context, new GestureListener());
|
||||||
tilesVisible = false;
|
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BInstallerView(Context context) {
|
public void setOnSelectListener(OnSelectListener listener) {
|
||||||
super(context);
|
mOnSelectListener = listener;
|
||||||
mActivity = (Activity) context;
|
}
|
||||||
|
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
private void setRatio(float ratio, float focusX, float focusY) {
|
||||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
if (currentScale() * ratio >= 1) {
|
||||||
imgwOrig = metrics.widthPixels;
|
mat.postScale(ratio, ratio, focusX, focusY);
|
||||||
imghOrig = metrics.heightPixels;
|
fitBounds();
|
||||||
int im = imgwOrig > imghOrig ? imgwOrig : imghOrig;
|
tilesVisible = currentScale() >= SCALE_GRID_VISIBLE;
|
||||||
|
|
||||||
scaleOrig = im / 480.f;
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
matText = new Matrix();
|
private void setScale(float scale, float focusX, float focusY) {
|
||||||
matText.preScale(scaleOrig, scaleOrig);
|
float ratio = scale / currentScale();
|
||||||
|
setRatio(ratio, focusX, focusY);
|
||||||
|
}
|
||||||
|
|
||||||
imgw = (int) (imgwOrig / scaleOrig);
|
public void setTileStatus(int tileIndex, int tileMask) {
|
||||||
imgh = (int) (imghOrig / scaleOrig);
|
tileStatus[tileIndex] |= tileMask;
|
||||||
|
if (mOnSelectListener != null) {
|
||||||
|
mOnSelectListener.onSelect();
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
totalSize = 0;
|
public void toggleTileStatus(int tileIndex, int tileMask) {
|
||||||
rd5Tiles = 0;
|
tileStatus[tileIndex] ^= tileMask;
|
||||||
delTiles = 0;
|
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<Integer> getSelectedTiles(int tileMask) {
|
||||||
|
ArrayList<Integer> 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
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
}
|
|
||||||
|
|
||||||
private void toast(String msg) {
|
int imgwOrig = getWidth();
|
||||||
Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
|
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
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
if (!isDownloading) {
|
|
||||||
canvas.setMatrix(mat);
|
canvas.setMatrix(mat);
|
||||||
canvas.drawBitmap(bmp, 0, 0, null);
|
canvas.drawBitmap(bmp, 0, 0, null);
|
||||||
}
|
|
||||||
// draw 5*5 lattice starting at scale=3
|
// draw 5*5 lattice starting at scale=3
|
||||||
|
|
||||||
int iw = bmp.getWidth();
|
int iw = bmp.getWidth();
|
||||||
|
@ -343,290 +162,164 @@ public class BInstallerView extends View {
|
||||||
float fw = iw / 72.f;
|
float fw = iw / 72.f;
|
||||||
float fh = ih / 36.f;
|
float fh = ih / 36.f;
|
||||||
|
|
||||||
boolean drawGrid = tilesVisible && !isDownloading;
|
if (tilesVisible) {
|
||||||
|
paintGrid.setColor(Color.GREEN);
|
||||||
if (drawGrid) {
|
paintGrid.setStyle(Paint.Style.STROKE);
|
||||||
|
for (int ix = 0; ix < 72; ix++) {
|
||||||
pnt_1.setColor(Color.GREEN);
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
|
int tidx = gridPos2Tileindex(ix, iy);
|
||||||
for (int ix = 1; ix < 72; ix++) {
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
||||||
float fx = fw * ix;
|
if (tilesize > 0) {
|
||||||
canvas.drawLine(fx, 0, fx, ih, pnt_1);
|
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 mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
|
||||||
int mask3 = mask2 | MASK_CURRENT_RD5;
|
int mask3 = mask2 | MASK_CURRENT_RD5;
|
||||||
|
|
||||||
pnt_2.setColor(Color.GRAY);
|
paintTiles.setStyle(Paint.Style.STROKE);
|
||||||
pnt_2.setStrokeWidth(1);
|
paintTiles.setColor(Color.GRAY);
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5, mask3, false, false, drawGrid);
|
paintTiles.setStrokeWidth(1);
|
||||||
pnt_2.setColor(Color.BLUE);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5, mask3);
|
||||||
pnt_2.setStrokeWidth(1);
|
paintTiles.setColor(Color.BLUE);
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3, false, false, drawGrid);
|
paintTiles.setStrokeWidth(1);
|
||||||
pnt_2.setColor(Color.GREEN);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3);
|
||||||
pnt_2.setStrokeWidth(2);
|
paintTiles.setColor(Color.GREEN);
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5, mask2, true, false, drawGrid);
|
paintTiles.setStrokeWidth(2);
|
||||||
pnt_2.setColor(Color.YELLOW);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5, mask2);
|
||||||
pnt_2.setStrokeWidth(2);
|
paintTiles.setColor(Color.YELLOW);
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2, true, false, drawGrid);
|
paintTiles.setStrokeWidth(2);
|
||||||
pnt_2.setColor(Color.RED);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2);
|
||||||
pnt_2.setStrokeWidth(2);
|
paintTiles.setColor(Color.RED);
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2, false, true, drawGrid);
|
paintTiles.setStrokeWidth(2);
|
||||||
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int btnh = 40;
|
private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask) {
|
||||||
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) {
|
|
||||||
for (int ix = 0; ix < 72; ix++)
|
for (int ix = 0; ix < 72; ix++)
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
int tidx = gridPos2Tileindex(ix, iy);
|
||||||
if ((tileStatus[tidx] & mask) == status) {
|
if ((tileStatus[tidx] & mask) == status) {
|
||||||
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
||||||
if (tilesize > 0) {
|
if (tilesize > 0) {
|
||||||
if (doCount) {
|
|
||||||
rd5Tiles++;
|
|
||||||
totalSize += BInstallerSizes.getRd5Size(tidx);
|
|
||||||
}
|
|
||||||
if (cntDel) {
|
|
||||||
delTiles++;
|
|
||||||
totalSize += BInstallerSizes.getRd5Size(tidx);
|
|
||||||
}
|
|
||||||
if (!doDraw)
|
|
||||||
continue;
|
|
||||||
// draw cross
|
// draw cross
|
||||||
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
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);
|
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
|
||||||
|
|
||||||
// draw frame
|
// draw frame
|
||||||
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * iy, pnt);
|
canvas.drawRect(fw * ix, fh * (iy + 1), 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSelectedTiles() {
|
private void fitBounds() {
|
||||||
for (int ix = 0; ix < 72; ix++) {
|
float[] srcPoints = new float[]{
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
0, 0,
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
bmp.getWidth(), bmp.getHeight()
|
||||||
if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
|
};
|
||||||
new File(baseDir, "brouter/segments4/" + baseNameForTile(tidx) + ".rd5").delete();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanExistingFiles();
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onDoubleTap(MotionEvent e) {
|
||||||
|
|
||||||
// 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) {
|
if (!tilesVisible) {
|
||||||
float scale = currentScale();
|
setScale(5, e.getX(), e.getY());
|
||||||
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 {
|
} else {
|
||||||
tileStatus[tidx] ^= MASK_SELECTED_RD5;
|
setScale(1, e.getX(), e.getY());
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = touchpoint[0];
|
@Override
|
||||||
ty = touchpoint[1];
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
}
|
mat.postTranslate(-distanceX, -distanceY);
|
||||||
|
fitBounds();
|
||||||
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_POINTER_UP:
|
|
||||||
case MotionEvent.ACTION_CANCEL: {
|
|
||||||
// TODO use data
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invalidate();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,24 @@
|
||||||
package btools.routingapp;
|
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.io.File;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -9,36 +26,9 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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;
|
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_SELECTPROFILE_ID = 1;
|
||||||
private static final int DIALOG_EXCEPTION_ID = 2;
|
private static final int DIALOG_EXCEPTION_ID = 2;
|
||||||
|
@ -129,11 +119,11 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
|
||||||
.setMessage(
|
.setMessage(
|
||||||
"*** Attention: ***\n\n" + "The Download Manager is used to download routing-data "
|
"*** 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! "
|
+ "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) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
|
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
// finish();
|
showNewDialog(DIALOG_MAINACTION_ID);
|
||||||
}
|
}
|
||||||
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
@ -391,39 +381,12 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
|
||||||
|
|
||||||
private String maptoolDirCandidate;
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
public void selectProfile(String[] items) {
|
public void selectProfile(String[] items) {
|
||||||
availableProfiles = items;
|
availableProfiles = items;
|
||||||
|
|
||||||
// if we have internet access, first show the main action dialog
|
// show main dialog
|
||||||
if (isOnline(this)) {
|
|
||||||
showDialog(DIALOG_MAINACTION_ID);
|
showDialog(DIALOG_MAINACTION_ID);
|
||||||
} else {
|
|
||||||
showDialog(DIALOG_SELECTPROFILE_ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@ -626,6 +589,7 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
if (requestCode == 0) {
|
if (requestCode == 0) {
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
mBRouterView.startSetup(null, true);
|
mBRouterView.startSetup(null, true);
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
package btools.routingapp;
|
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.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -12,36 +28,13 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
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.expressions.BExpressionMetaData;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.router.OsmNodeNamed;
|
import btools.router.OsmNodeNamed;
|
||||||
|
@ -108,11 +101,6 @@ public class BRouterView extends View {
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
try {
|
try {
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
|
||||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
|
||||||
imgw = metrics.widthPixels;
|
|
||||||
imgh = metrics.heightPixels;
|
|
||||||
|
|
||||||
// get base dir from private file
|
// get base dir from private file
|
||||||
File baseDir = ConfigHelper.getBaseDir(getContext());
|
File baseDir = ConfigHelper.getBaseDir(getContext());
|
||||||
// check if valid
|
// check if valid
|
||||||
|
@ -123,6 +111,15 @@ public class BRouterView extends View {
|
||||||
if (brd.isDirectory()) {
|
if (brd.isDirectory()) {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q &&
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q &&
|
||||||
!brd.getAbsolutePath().contains("/Android/media/btools.routingapp")) {
|
!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 )";
|
String message = "(previous basedir " + baseDir + " has to migrate )";
|
||||||
|
|
||||||
((BRouterActivity) getContext()).selectBasedir(((BRouterActivity) getContext()).getStorageDirectories(), guessBaseDir(), message);
|
((BRouterActivity) getContext()).selectBasedir(((BRouterActivity) getContext()).getStorageDirectories(), guessBaseDir(), message);
|
||||||
|
@ -204,6 +201,8 @@ public class BRouterView extends View {
|
||||||
|
|
||||||
// new init is done move old files
|
// new init is done move old files
|
||||||
if (waitingForMigration) {
|
if (waitingForMigration) {
|
||||||
|
Log.d("BR", "path " + oldMigrationPath + " " + basedir);
|
||||||
|
if (!oldMigrationPath.equals(basedir + "/brouter"))
|
||||||
moveFolders(oldMigrationPath, basedir + "/brouter");
|
moveFolders(oldMigrationPath, basedir + "/brouter");
|
||||||
waitingForMigration = false;
|
waitingForMigration = false;
|
||||||
}
|
}
|
||||||
|
@ -254,7 +253,7 @@ public class BRouterView extends View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tracksDir == null) {
|
if (tracksDir == null) {
|
||||||
tracksDir = new File(basedir, "router"); // fallback
|
tracksDir = new File(basedir, "brouter"); // fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] fileNames = profileDir.list();
|
String[] fileNames = profileDir.list();
|
||||||
|
@ -569,7 +568,7 @@ public class BRouterView extends View {
|
||||||
// for profile remote, use ref-track logic same as service interface
|
// for profile remote, use ref-track logic same as service interface
|
||||||
rc.rawTrackPath = rawTrackPath;
|
rc.rawTrackPath = rawTrackPath;
|
||||||
|
|
||||||
cr = new RoutingEngine(tracksDir + "/brouter", null, segmentDir, wpList, rc);
|
cr = new RoutingEngine(tracksDir.getAbsolutePath()+"/brouter", null, segmentDir, wpList, rc);
|
||||||
cr.start();
|
cr.start();
|
||||||
invalidate();
|
invalidate();
|
||||||
|
|
||||||
|
@ -606,6 +605,7 @@ public class BRouterView extends View {
|
||||||
break;
|
break;
|
||||||
String name = ze.getName();
|
String name = ze.getName();
|
||||||
File outfile = new File(path, name);
|
File outfile = new File(path, name);
|
||||||
|
if (!outfile.exists()) {
|
||||||
outfile.getParentFile().mkdirs();
|
outfile.getParentFile().mkdirs();
|
||||||
FileOutputStream fos = new FileOutputStream(outfile);
|
FileOutputStream fos = new FileOutputStream(outfile);
|
||||||
|
|
||||||
|
@ -617,6 +617,7 @@ public class BRouterView extends View {
|
||||||
}
|
}
|
||||||
fos.close();
|
fos.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
is.close();
|
is.close();
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException io) {
|
} catch (IOException io) {
|
||||||
|
@ -690,6 +691,8 @@ public class BRouterView extends View {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
imgw = w;
|
||||||
|
imgh = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toast(String msg) {
|
private void toast(String msg) {
|
||||||
|
|
|
@ -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<String> 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<String> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/view_segments"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<btools.routingapp.BInstallerView
|
||||||
|
android:id="@+id/BInstallerView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonDownload"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/action_select"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewSegmentSummary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:textColor="@android:color/primary_text_light"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progressDownload"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -15,7 +15,22 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
|
<plurals name="numberOfSegments">
|
||||||
|
<item quantity="one">%d segment</item>
|
||||||
|
<item quantity="other">%d segments</item>
|
||||||
|
</plurals>
|
||||||
<string name="app_name">BRouter</string>
|
<string name="app_name">BRouter</string>
|
||||||
|
<string name="cancel_download">Cancel Download</string>
|
||||||
<string name="import_profile">Import Profile</string>
|
<string name="import_profile">Import Profile</string>
|
||||||
<string name="profile_filename_example">filename.brf</string>
|
<string name="profile_filename_example">filename.brf</string>
|
||||||
|
<string name="download_info_start">Starting download…</string>
|
||||||
|
<string name="download_info_cancel">Cancelling…</string>
|
||||||
|
<string name="action_download">Download %s</string>
|
||||||
|
<string name="action_delete">Delete %s</string>
|
||||||
|
<string name="action_update">Update %s</string>
|
||||||
|
<string name="action_select">Select segments</string>
|
||||||
|
<string name="summary_segments">Size=%s\nFree=%s</string>
|
||||||
|
<string name="notification_channel_id">brouter_download</string>
|
||||||
|
<string name="notification_title">Download Segments</string>
|
||||||
|
<string name="channel_name">Downloads</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
10
brouter-routing-app/src/main/res/values/styles.xml
Normal file
10
brouter-routing-app/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.App" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="linearProgressIndicatorStyle">@style/Widget.App.LinearProgressIndicator</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.App.LinearProgressIndicator" parent="Widget.MaterialComponents.LinearProgressIndicator">
|
||||||
|
<item name="trackThickness">20dp</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
|
@ -56,6 +56,7 @@ distributions {
|
||||||
exclude('**/softaccess.brf')
|
exclude('**/softaccess.brf')
|
||||||
from ('../misc') {
|
from ('../misc') {
|
||||||
include 'readmes/*'
|
include 'readmes/*'
|
||||||
|
include 'readmes/osmand/*'
|
||||||
include 'profiles2/*'
|
include 'profiles2/*'
|
||||||
}
|
}
|
||||||
from ('../brouter-routing-app/build/outputs/apk/api19/release') {
|
from ('../brouter-routing-app/build/outputs/apk/api19/release') {
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.util.HashMap;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import btools.router.SuspectInfo;
|
||||||
|
|
||||||
public class SuspectManager extends Thread
|
public class SuspectManager extends Thread
|
||||||
{
|
{
|
||||||
private static SimpleDateFormat dfTimestampZ = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" );
|
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 ) )
|
if ( "falsepositive".equals( command ) )
|
||||||
{
|
{
|
||||||
int wps = NearRecentWps.count( id );
|
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;
|
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();
|
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
|
String url1 = "http://brouter.de/brouter-web/#map=18/" + dlat + "/" + dlon
|
||||||
+ "/OpenStreetMap&lonlats=" + dlon + "," + dlat + "&profile=" + profile;
|
+ "/OpenStreetMap&lonlats=" + dlon + "," + dlat + "&profile=" + profile;
|
||||||
|
|
||||||
|
@ -456,6 +464,7 @@ public class SuspectManager extends Thread
|
||||||
{
|
{
|
||||||
bw.write( "<strong>" + message + "</strong><br><br>\n" );
|
bw.write( "<strong>" + message + "</strong><br><br>\n" );
|
||||||
}
|
}
|
||||||
|
bw.write( "Trigger: " + triggerText + "<br><br>\n" );
|
||||||
bw.write( "<a href=\"" + url1 + "\">Open in BRouter-Web</a><br><br>\n" );
|
bw.write( "<a href=\"" + url1 + "\">Open in BRouter-Web</a><br><br>\n" );
|
||||||
bw.write( "<a href=\"" + url2 + "\">Open in OpenStreetmap</a><br><br>\n" );
|
bw.write( "<a href=\"" + url2 + "\">Open in OpenStreetmap</a><br><br>\n" );
|
||||||
bw.write( "<a href=\"" + url3 + "\">Open in JOSM (via remote control)</a><br><br>\n" );
|
bw.write( "<a href=\"" + url3 + "\">Open in JOSM (via remote control)</a><br><br>\n" );
|
||||||
|
@ -585,6 +594,7 @@ public class SuspectManager extends Thread
|
||||||
int cnt;
|
int cnt;
|
||||||
long[] ids;
|
long[] ids;
|
||||||
int[] prios;
|
int[] prios;
|
||||||
|
int[] triggers;
|
||||||
boolean[] newOrConfirmed;
|
boolean[] newOrConfirmed;
|
||||||
boolean[] falsePositive;
|
boolean[] falsePositive;
|
||||||
long timestamp;
|
long timestamp;
|
||||||
|
@ -594,10 +604,23 @@ public class SuspectManager extends Thread
|
||||||
cnt = count;
|
cnt = count;
|
||||||
ids = new long[cnt];
|
ids = new long[cnt];
|
||||||
prios = new int[cnt];
|
prios = new int[cnt];
|
||||||
|
triggers = new int[cnt];
|
||||||
newOrConfirmed = new boolean[cnt];
|
newOrConfirmed = new boolean[cnt];
|
||||||
falsePositive = new boolean[cnt];
|
falsePositive = new boolean[cnt];
|
||||||
timestamp = time;
|
timestamp = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int trigger4Id( long id )
|
||||||
|
{
|
||||||
|
for( int i = 0; i<cnt; i++ )
|
||||||
|
{
|
||||||
|
if ( id == ids[i] )
|
||||||
|
{
|
||||||
|
return triggers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashMap<String,SuspectList> allSuspectsMap = new HashMap<String,SuspectList>();
|
private static HashMap<String,SuspectList> allSuspectsMap = new HashMap<String,SuspectList>();
|
||||||
|
@ -654,6 +677,7 @@ public class SuspectManager extends Thread
|
||||||
pointer = prioCount[nprio]++;
|
pointer = prioCount[nprio]++;
|
||||||
allSuspects.ids[pointer] = id;
|
allSuspects.ids[pointer] = id;
|
||||||
allSuspects.prios[pointer] = prio;
|
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.newOrConfirmed[pointer] = new File( "confirmednegatives/" + id ).exists() || !(new File( "suspectarchive/" + id ).exists() );
|
||||||
allSuspects.falsePositive[pointer] = new File( "falsepositives/" + id ).exists();
|
allSuspects.falsePositive[pointer] = new File( "falsepositives/" + id ).exists();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package btools.util;
|
package btools.util;
|
||||||
|
|
||||||
|
|
||||||
public interface ProgressListener
|
public interface ProgressListener
|
||||||
{
|
{
|
||||||
public void updateProgress( String progress );
|
public void updateProgress(String task, int progress);
|
||||||
|
|
||||||
public boolean isCanceled();
|
public boolean isCanceled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ allprojects {
|
||||||
// this file
|
// this file
|
||||||
// app: build.gradle (versionCode only)
|
// app: build.gradle (versionCode only)
|
||||||
// OsmTrack (version and versionDate)
|
// OsmTrack (version and versionDate)
|
||||||
project.version "1.6.2"
|
project.version "1.6.3"
|
||||||
group 'org.btools'
|
group 'org.btools'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
|
@ -140,6 +140,11 @@ public class OsmParser extends MapCreatorBase
|
||||||
rListener.nextRelation( r );
|
rListener.nextRelation( r );
|
||||||
if ( fromWid == null || toWid == null || viaNid == null || viaNid.size() != 1 )
|
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;
|
return;
|
||||||
}
|
}
|
||||||
for( int fi = 0; fi < fromWid.size(); fi++ )
|
for( int fi = 0; fi < fromWid.size(); fi++ )
|
||||||
|
@ -152,7 +157,7 @@ public class OsmParser extends MapCreatorBase
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception e )
|
||||||
{
|
{
|
||||||
throw new RuntimeException( "error writing relation: " + e );
|
throw new RuntimeException( "error writing relation", e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
# this prevents suppression of unused tags, so they are visibly in the data tab
|
# this prevents suppression of unused tags, so they are visibly in the data tab
|
||||||
assign processUnusedTags = true
|
assign processUnusedTags = true
|
||||||
|
assign validForFoot = true
|
||||||
|
|
||||||
---context:way # following code refers to way-tags
|
---context:way # following code refers to way-tags
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
485
misc/profiles2/hiking-mountain.brf
Normal file
485
misc/profiles2/hiking-mountain.brf
Normal file
|
@ -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
|
|
@ -13,8 +13,8 @@ assign turncost 0
|
||||||
assign initialcost 0
|
assign initialcost 0
|
||||||
|
|
||||||
assign costfactor
|
assign costfactor
|
||||||
switch railway=rail 1
|
switch railway=rail|light_rail|narrow_gauge 1
|
||||||
switch railway=narrow_gauge 1
|
switch railway=tram|subway 2
|
||||||
100000
|
100000
|
||||||
|
|
||||||
---context:node # following code refers to node tags
|
---context:node # following code refers to node tags
|
||||||
|
|
Loading…
Reference in a new issue