diff --git a/README.md b/README.md
index 290c09f..9d4d9ec 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,129 @@
BRouter
=======
-BRouter is a configurable OSM offline router with elevation awareness, Java + Android. Designed to be multi-modal with a particular emphasis on bicycle routing.
+BRouter is a configurable OSM offline router with elevation awareness, Java +
+Android. Designed to be multi-modal with a particular emphasis on bicycle
+and energy-based car routing.
-For more infos see http://brouter.de/brouter
+For more infos see [http://brouter.de/brouter](http://brouter.de/brouter).
+
+
+## BRouter on Android
+
+You can install the BRouter app on your Android device from
+[F-Droid](https://f-droid.org/packages/btools.routingapp) or [Google Play
+Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
+can also [build BRouter](#build-and-install) yourself. You can find detailed
+documentation of the BRouter Android app in
+[`misc/readmes/readme.txt`](misc/readmes/readme.txt).
+### Android with Locus
-## Build and Install
+You can use BRouter as the offline routing engine for [Locus
+Map](https://www.locusmap.eu/) on your Android device. This is currently the
+most featureful and maintained solutions for using BRouter on your Android
+device.
-Compile with (Java 6!):
-
-> mvn clean install -Dandroid.sdk.path=
-
-To skip building for Android, add ``-pl '!brouter-routing-app'``.
-
-Next, download one or more [data file(s)](http://brouter.de/brouter/segments4/) (rd5) into the ``misc/segments4`` directory.
-
-## Run
-
-On Linux:
-> ./misc/scripts/standalone/server.sh
-
-On Windows (using Bash):
-> ./misc/scripts/standalone/server.sh
-
-On Windows (using CMD):
-> misc\scripts\standalone\server.cmd
+A full documentation on how to set this up is available at
+[https://www.locusmap.eu/locus-map-can-navigate-offline/](https://www.locusmap.eu/locus-map-can-navigate-offline/).
-Related Projects
-================
+### Android with OSMAnd
-* https://github.com/nrenner/brouter-web
-* https://github.com/poutnikl/Brouter-profiles/wiki
+Alternatively, you can also use BRouter as the offline routing engine for
+[OSMAnd](https://osmand.net/) on your Android device.
+
+A full documentation on how to set this up is available at
+[`misc/readmes/osmand/README.md`](misc/readmes/osmand/README.md).
+
+
+## BRouter on Windows/Linux/Mac OS
+
+### Build and Install
+
+To compile BRouter (including the BRouter Android app), use
+
+```
+mvn clean install -Dandroid.sdk.path=
+```
+
+If you only want to compile BRouter and the server part (skipping the Android
+app), use
+
+```
+mvn clean install -pl '!brouter-routing-app'
+```
+
+You can use `-Dmaven.javadoc.skip=true` to skip the JavaDoc processing and
+`-DskipTests` to skip running the unitary tests.
+
+
+### Get the required segments (data) files
+
+Routing data files are organised as 5*5 degree files,
+with the filename containing the south-west corner
+of the square, which means:
+
+- You want to route near West48/North37 -> you need `W50_N35.rd5`
+- You want to route near East7/North47 -> you need `E5_N45.rd5`
+
+These data files, called "segments" across BRouter, are generated from
+[OpenStreetMap](https://www.openstreetmap.org/) data and stored in a custom
+binary format (rd5) for improved efficiency of BRouter routing.
+
+
+#### Download them from brouter.de
+
+Segments files from the whole planet are generated weekly at
+[http://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
+
+You can download one or more segments files, covering the area of the planet
+your want to route, into the `misc/segments4` directory.
+
+#### Generate your own segments files
+
+You can also generate the segments files you need directly from a planet dump
+of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
+
+More documentation of this is available in the
+[`misc/readmes/mapcreation.md`](misc/readmes/mapcreation.md) file.
+
+
+### Run the BRouter HTTP server
+
+Helpers scripts are provided in `misc/scripts/standalone` to quickly spawn a
+BRouter HTTP server for various platforms.
+
+* Linux/Mac OS: `./misc/scripts/standalone/server.sh`
+* Windows (using Bash): `./misc/scripts/standalone/server.sh`
+* Windows (using CMD): `misc\scripts\standalone\server.cmd`
+
+The API endpoints exposed by this HTTP server are documented in the
+[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
+file.
+
+
+## Documentation
+
+More documentation is available in the [`misc/readmes`](misc/readmes) folder.
+
+
+## Related Projects
+
+* [nrenner/BRouter-web](https://github.com/nrenner/brouter-web), a web interface on
+ top of the BRouter HTTP server. An online instance is available at
+ [http://brouter.de/brouter-web/](http://brouter.de/brouter-web/).
+* [poutnikl/Brouter-profiles](https://github.com/poutnikl/Brouter-profiles/wiki),
+ a collection of BRouter profiles.
+* [Phyks/BRouterTesting](https://github.com/Phyks/BrouterTesting), a
+ collection of test cases for helping develop new BRouter profiles.
+
+
+## License
+
+BRouter is released under an [MIT License](LICENSE).
diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache.java b/brouter-codec/src/main/java/btools/codec/MicroCache.java
index 2cb4a8c..8c01988 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache.java
@@ -314,4 +314,56 @@ public class MicroCache extends ByteDataWriter
}
return null;
}
+
+ public void calcDelta( MicroCache mc1, MicroCache mc2 )
+ {
+ int idx1 = 0;
+ int idx2 = 0;
+
+ while( idx1 < mc1.size || idx2 < mc2.size )
+ {
+ int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
+ int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
+ int id;
+ if ( id1 >= id2 )
+ {
+ id = id2;
+ int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
+ int len2 = mc2.fapos[idx2++] - start2;
+
+ if ( id1 == id2 )
+ {
+ // id exists in both caches, compare data
+ int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
+ int len1 = mc1.fapos[idx1++] - start1;
+ if ( len1 == len2 )
+ {
+ int i = 0;
+ while( i do nothing
+ }
+ }
+ }
+ write( mc2.ab, start2, len2 );
+ }
+ else
+ {
+ idx1++;
+ id = id1; // deleted node
+ }
+ fapos[size] = aboffset;
+ faid[size] = id;
+ size++;
+ }
+ }
+
}
diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
index b4178fe..8cd8f30 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
@@ -87,10 +87,15 @@ public final class MicroCache2 extends MicroCache
// future escapes (turn restrictions?)
short trExceptions = 0;
- for(;;)
+ int featureId = bc.decodeVarBits();
+ if ( featureId == 13 )
+ {
+ fapos[n] = aboffset;
+ validBits[ n >> 5 ] |= 1 << n; // mark dummy-node valid
+ continue; // empty node escape (delta files only)
+ }
+ while( featureId != 0 )
{
- int featureId = bc.decodeVarBits();
- if ( featureId == 0 ) break;
int bitsize = bc.decodeNoisyNumber( 5 );
if ( featureId == 2 ) // exceptions to turn-restriction
@@ -113,6 +118,7 @@ public final class MicroCache2 extends MicroCache
{
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
}
+ featureId = bc.decodeVarBits();
}
writeBoolean( false );
@@ -147,7 +153,8 @@ public final class MicroCache2 extends MicroCache
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
- if ( wayTags != null )
+ boolean linkValid = wayTags != null || wayValidator == null;
+ if ( linkValid )
{
int startPointer = aboffset;
sizeoffset = writeSizePlaceHolder();
@@ -162,7 +169,7 @@ public final class MicroCache2 extends MicroCache
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
}
- writeModeAndDesc( isReverse, wayTags.data );
+ writeModeAndDesc( isReverse, wayTags == null ? null : wayTags.data );
}
if ( !isReverse ) // write geometry for forward links only
@@ -200,7 +207,7 @@ public final class MicroCache2 extends MicroCache
}
if ( matcher != null ) matcher.end();
}
- if ( wayTags != null )
+ if ( linkValid )
{
injectSize( sizeoffset );
}
@@ -375,6 +382,12 @@ public final class MicroCache2 extends MicroCache
int ilon = (int)(id64 >> 32);
int ilat = (int)(id64 & 0xffffffff);
+ if ( aboffset == aboffsetEnd )
+ {
+ bc.encodeVarBits( 13 ); // empty node escape (delta files only)
+ continue;
+ }
+
// write turn restrictions
while( readBoolean() )
{
@@ -430,7 +443,10 @@ public final class MicroCache2 extends MicroCache
readFully( description );
}
- boolean isInternal = isInternal( ilonlink, ilatlink );
+ long link64 = ((long)ilonlink)<<32 | ilatlink;
+ Integer idx = idMap.get( Long.valueOf( link64 ) );
+ boolean isInternal = idx != null;
+
if ( isReverse && isInternal )
{
if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
@@ -442,9 +458,6 @@ public final class MicroCache2 extends MicroCache
if ( isInternal )
{
- long link64 = ((long)ilonlink)<<32 | ilatlink;
- Integer idx = idMap.get( Long.valueOf( link64 ) );
- if ( idx == null ) throw new RuntimeException( "ups: internal not found?" );
int nodeIdx = idx.intValue();
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
diff --git a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
index a733085..1118000 100644
--- a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
+++ b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
@@ -62,6 +62,11 @@ public final class TagValueCoder
{
if ( ++pass == 3 )
{
+ if ( identityMap.size() == 0 )
+ {
+ TagValueSet dummy = new TagValueSet();
+ identityMap.put( dummy, dummy );
+ }
PriorityQueue queue = new PriorityQueue(2*identityMap.size(), new TagValueSet.FrequencyComparator());
queue.addAll(identityMap.values());
while (queue.size() > 1)
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
new file mode 100644
index 0000000..f6d5bee
--- /dev/null
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
@@ -0,0 +1,144 @@
+/**
+ * Proof of concept for delta rd5's
+ *
+ * @author ab
+ */
+package btools.mapaccess;
+
+import java.io.File;
+
+import btools.codec.DataBuffers;
+import btools.codec.MicroCache;
+import btools.codec.MicroCache2;
+import btools.codec.StatCoderContext;
+
+final public class Rd5DiffTool
+{
+ public static void main( String[] args ) throws Exception
+ {
+ diff2files( new File( args[0] ),new File( args[1] ) );
+ }
+
+ /**
+ * Compute the delta between 2 RD5 files and
+ * show statistics on the expected size of the delta file
+ */
+ public static void diff2files( File f1, File f2 ) throws Exception
+ {
+ byte[] abBuf1 = new byte[10 * 1024 * 1024];
+ byte[] abBuf2 = new byte[10 * 1024 * 1024];
+
+ int nodesTotal = 0;
+ int nodesDiff = 0;
+ int diffedTiles = 0;
+
+ long bytesDiff = 0L;
+ long diffedTileSize = 0L;
+
+ PhysicalFile pf1 = null;
+ PhysicalFile pf2 = null;
+ try
+ {
+ DataBuffers dataBuffers = new DataBuffers();
+ pf1 = new PhysicalFile( f1, dataBuffers, -1, -1 );
+ pf2 = new PhysicalFile( f2, dataBuffers, -1, -1 );
+ int div = pf1.divisor;
+ for ( int lonDegree = 0; lonDegree < 5; lonDegree++ ) // does'nt really matter..
+ {
+ for ( int latDegree = 0; latDegree < 5; latDegree++ ) // ..where on earth we are
+ {
+ OsmFile osmf1 = new OsmFile( pf1, lonDegree, latDegree, dataBuffers );
+ OsmFile osmf2 = new OsmFile( pf2, lonDegree, latDegree, dataBuffers );
+ for ( int lonIdx = 0; lonIdx < div; lonIdx++ )
+ {
+ for ( int latIdx = 0; latIdx < div; latIdx++ )
+ {
+ int lonIdxDiv = lonDegree * div + lonIdx;
+ int latIdxDiv = latDegree * div + latIdx;
+
+
+ MicroCache mc1 = osmf1.hasData() ?
+ osmf1.createMicroCache( lonIdxDiv, latIdxDiv, dataBuffers, null, null, true, null )
+ : MicroCache.emptyCache();
+ MicroCache mc2 = osmf2.hasData() ?
+ osmf2.createMicroCache( lonIdxDiv, latIdxDiv, dataBuffers, null, null, true, null )
+ : MicroCache.emptyCache();
+
+ MicroCache mc = new MicroCache2( mc1.getSize() + mc2.getSize(), abBuf2, lonIdxDiv, latIdxDiv, div );
+ mc.calcDelta( mc1, mc2 );
+
+ nodesTotal += mc2.getSize();
+
+ if ( mc.getSize() > 0 )
+ {
+ int len = mc.encodeMicroCache( abBuf1 );
+ byte[] bytes = new byte[len];
+ System.arraycopy( abBuf1, 0, bytes, 0, len );
+
+ bytesDiff += len;
+ nodesDiff += mc.getSize();
+ diffedTiles++;
+ diffedTileSize += mc2.size();
+
+ // cross-check the encoding: decode again
+ MicroCache mcCheck = new MicroCache2( new StatCoderContext( bytes ), new DataBuffers( null ), lonIdxDiv, latIdxDiv, div, null, null );
+
+ // due to link-order ambiguity, for decoded we can only compare node-count and datasize
+ if ( mc.size() != mcCheck.size() )
+ {
+ throw new IllegalArgumentException( "re-decoded data-size mismatch!" );
+ }
+ if ( mc.getSize() != mcCheck.getSize() )
+ {
+ throw new IllegalArgumentException( "re-decoded node-count mismatch!" );
+ }
+
+ // .... so re-encode again
+ int len2 = mcCheck.encodeMicroCache( abBuf1 );
+ byte[] bytes2 = new byte[len2];
+ System.arraycopy( abBuf1, 0, bytes2, 0, len2 );
+
+ // and here we can compare byte-by-byte
+ if ( len != len2 )
+ {
+ throw new IllegalArgumentException( "decoded size mismatch!" );
+ }
+ for( int i=0; i
+
+
+
+
+
+## Downloading BRouter segments
+
+Then, launch the BRouter app and choose "Download Manager". Zoom in and select
+the areas you want to route in. Then click "Start Download" and BRouter will
+start downloading the [segments](http://brouter.de/brouter/segments4/) files
+for the selected areas.
+
+
+
+
+
+Note that you will have to repeat this step periodically, whenever you want to have an
+updated version of the OSM data used for the routing.
+
+
+## Selecting profiles to use
+
+Once this is done, start again the BRouter app and choose the "BRouter App"
+entry on the main menu. Select the routing profile you want to use and click
+"Server-Mode". Then, tick the boxes for the routing modes you want to use this
+profile for. You can use two different profiles per transportation mode, which
+will be mapped to the "shortest" and "fastest" presets (these are just
+labelling) in OSMAnd.
+
+
+
+
+
+
+## Configure OSMAnd to make use of BRouter offline navigation
+
+You can now create an "Application profile" in OSMAnd which will be using
+BRouter for offline routing. Go to Settings -> Application profiles -> Add and
+create a new profile based on the base profile of your choice (cycling here,
+for bicycle routing), with a custom name of your choice ("BRouter" on the
+screenshot below) and making use of "BRouter (offline)" for navigation.
+
+
+
+The BRouter app should be launched before OSMAnd for this specific entry to
+appear in OSMAnd. Therefore, if you cannot find "BRouter (offline)" navigation
+option, you should force quit OSMAnd and restart it.
diff --git a/misc/readmes/osmand/brouter-grid.png b/misc/readmes/osmand/brouter-grid.png
new file mode 100644
index 0000000..0d97c68
Binary files /dev/null and b/misc/readmes/osmand/brouter-grid.png differ
diff --git a/misc/readmes/osmand/brouter-main.png b/misc/readmes/osmand/brouter-main.png
new file mode 100644
index 0000000..3541654
Binary files /dev/null and b/misc/readmes/osmand/brouter-main.png differ
diff --git a/misc/readmes/osmand/brouter-osmand.png b/misc/readmes/osmand/brouter-osmand.png
new file mode 100644
index 0000000..3586cc2
Binary files /dev/null and b/misc/readmes/osmand/brouter-osmand.png differ
diff --git a/misc/readmes/osmand/brouter-profiles-summary.png b/misc/readmes/osmand/brouter-profiles-summary.png
new file mode 100644
index 0000000..d849a9e
Binary files /dev/null and b/misc/readmes/osmand/brouter-profiles-summary.png differ
diff --git a/misc/readmes/osmand/brouter-profiles.png b/misc/readmes/osmand/brouter-profiles.png
new file mode 100644
index 0000000..e795b53
Binary files /dev/null and b/misc/readmes/osmand/brouter-profiles.png differ
diff --git a/misc/scripts/mapcreation/process_pbf_planet.sh b/misc/scripts/mapcreation/process_pbf_planet.sh
index 36b635e..dd6d97d 100755
--- a/misc/scripts/mapcreation/process_pbf_planet.sh
+++ b/misc/scripts/mapcreation/process_pbf_planet.sh
@@ -1,6 +1,19 @@
#!/bin/bash
set -e
-wget -N http://planet.openstreetmap.org/pbf/planet-latest.osm.pbf
+cd "$(dirname "$0")"
+
+# Fetch OSM planet dump if no planet file is specified
+if [ -z "$PLANET_FILE" ]; then
+ if [ -x "$(command -v osmupdate)" ] && [[ -f "./planet-latest.osm.pbf" ]]; then
+ # Prefer running osmupdate to update the planet file if available
+ mv "./planet-latest.osm.pbf" "./planet-latest.old.osm.pbf"
+ osmupdate "planet-latest.old.osm.pbf" "./planet-latest.osm.pbf"
+ rm "./planet-latest.old.osm.pbf"
+ else
+ # Otherwise, download it again
+ wget -N http://planet.openstreetmap.org/pbf/planet-latest.osm.pbf
+ fi
+fi
if test lastmaprun.date -nt planet-latest.osm.pbf; then
echo "no osm update, exiting"
@@ -20,7 +33,7 @@ OSMOSIS_JAR=$(realpath "../../pbfparser/osmosis.jar")
PROTOBUF_JAR=$(realpath "../../pbfparser/protobuf.jar")
PBFPARSER_JAR=$(realpath "../../pbfparser/pbfparser.jar")
-PLANET_FILE=$(realpath "./planet-latest.osm.pbf")
+PLANET_FILE=${PLANET_FILE:-$(realpath "./planet-latest.osm.pbf")}
# Download SRTM zip files from
# https://cgiarcsi.community/data/srtm-90m-digital-elevation-database-v4-1/
# (use the "ArcInfo ASCII" version) and put the ZIP files directly in this