diff --git a/brouter-codec/src/main/java/btools/codec/DataBuffers.java b/brouter-codec/src/main/java/btools/codec/DataBuffers.java
index ebe7f6e..e8c8a9e 100644
--- a/brouter-codec/src/main/java/btools/codec/DataBuffers.java
+++ b/brouter-codec/src/main/java/btools/codec/DataBuffers.java
@@ -5,11 +5,10 @@ import btools.util.BitCoderContext;
/**
* Container for some re-usable databuffers for the decoder
*/
-public final class DataBuffers
-{
+public final class DataBuffers {
public byte[] iobuffer;
public byte[] tagbuf1 = new byte[256];
- public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
+ public BitCoderContext bctx1 = new BitCoderContext(tagbuf1);
public byte[] bbuf1 = new byte[65636];
public int[] ibuf1 = new int[4096];
public int[] ibuf2 = new int[2048];
@@ -17,17 +16,15 @@ public final class DataBuffers
public int[] alon = new int[2048];
public int[] alat = new int[2048];
- public DataBuffers()
- {
- this( new byte[65636] );
+ public DataBuffers() {
+ this(new byte[65636]);
}
/**
* construct a set of databuffers except
* for 'iobuffer', where the given array is used
*/
- public DataBuffers( byte[] iobuffer )
- {
+ public DataBuffers(byte[] iobuffer) {
this.iobuffer = iobuffer;
}
diff --git a/brouter-codec/src/main/java/btools/codec/IntegerFifo3Pass.java b/brouter-codec/src/main/java/btools/codec/IntegerFifo3Pass.java
index cdb186b..c25e304 100644
--- a/brouter-codec/src/main/java/btools/codec/IntegerFifo3Pass.java
+++ b/brouter-codec/src/main/java/btools/codec/IntegerFifo3Pass.java
@@ -3,16 +3,14 @@ package btools.codec;
/**
* Special integer fifo suitable for 3-pass encoding
*/
-public class IntegerFifo3Pass
-{
+public class IntegerFifo3Pass {
private int[] a;
private int size;
private int pos;
private int pass;
- public IntegerFifo3Pass( int capacity )
- {
+ public IntegerFifo3Pass(int capacity) {
a = capacity < 4 ? new int[4] : new int[capacity];
}
@@ -20,8 +18,7 @@ public class IntegerFifo3Pass
* Starts a new encoding pass and resets the reading pointer
* from the stats collected in pass2 and writes that to the given context
*/
- public void init()
- {
+ public void init() {
pass++;
pos = 0;
}
@@ -29,14 +26,11 @@ public class IntegerFifo3Pass
/**
* writes to the fifo in pass2
*/
- public void add( int value )
- {
- if ( pass == 2 )
- {
- if ( size == a.length )
- {
+ public void add(int value) {
+ if (pass == 2) {
+ if (size == a.length) {
int[] aa = new int[2 * size];
- System.arraycopy( a, 0, aa, 0, size );
+ System.arraycopy(a, 0, aa, 0, size);
a = aa;
}
a[size++] = value;
@@ -46,16 +40,13 @@ public class IntegerFifo3Pass
/**
* reads from the fifo in pass3 (in pass1/2 returns just 1)
*/
- public int getNext()
- {
- return pass == 3 ? get( pos++ ) : 1;
+ public int getNext() {
+ return pass == 3 ? get(pos++) : 1;
}
- private int get( int idx )
- {
- if ( idx >= size )
- {
- throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx );
+ private int get(int idx) {
+ if (idx >= size) {
+ throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx);
}
return a[idx];
}
diff --git a/brouter-codec/src/main/java/btools/codec/LinkedListContainer.java b/brouter-codec/src/main/java/btools/codec/LinkedListContainer.java
index 4004444..72f7c6f 100644
--- a/brouter-codec/src/main/java/btools/codec/LinkedListContainer.java
+++ b/brouter-codec/src/main/java/btools/codec/LinkedListContainer.java
@@ -3,8 +3,7 @@ package btools.codec;
/**
* Simple container for a list of lists of integers
*/
-public class LinkedListContainer
-{
+public class LinkedListContainer {
private int[] ia; // prev, data, prev, data, ...
private int size;
private int[] startpointer; // 0=void, odd=head-data-cell
@@ -12,49 +11,44 @@ public class LinkedListContainer
/**
* Construct a container for the given number of lists
- *
+ *
* If no default-buffer is given, an int[nlists*4] is constructed,
* able to hold 2 entries per list on average
*
- * @param nlists the number of lists
+ * @param nlists the number of lists
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
*/
- public LinkedListContainer( int nlists, int[] defaultbuffer )
- {
- ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
- startpointer = new int[nlists];
+ public LinkedListContainer(int nlists, int[] defaultbuffer) {
+ ia = defaultbuffer == null ? new int[nlists * 4] : defaultbuffer;
+ startpointer = new int[nlists];
}
-
+
/**
* Add a data element to the given list
*
* @param listNr the list to add the data to
- * @param data the data value
+ * @param data the data value
*/
- public void addDataElement( int listNr, int data )
- {
- if ( size + 2 > ia.length )
- {
+ public void addDataElement(int listNr, int data) {
+ if (size + 2 > ia.length) {
resize();
}
- ia[size++] = startpointer[ listNr ];
- startpointer[ listNr ] = size;
+ ia[size++] = startpointer[listNr];
+ startpointer[listNr] = size;
ia[size++] = data;
}
-
+
/**
* Initialize a list for reading
*
* @param listNr the list to initialize
* @return the number of entries in that list
*/
- public int initList( int listNr )
- {
+ public int initList(int listNr) {
int cnt = 0;
- int lp = listpointer = startpointer[ listNr ];
- while( lp != 0 )
- {
- lp = ia[ lp-1 ];
+ int lp = listpointer = startpointer[listNr];
+ while (lp != 0) {
+ lp = ia[lp - 1];
cnt++;
}
return cnt;
@@ -67,21 +61,18 @@ public class LinkedListContainer
* @return the data element
* @throws IllegalArgumentException if no more element
*/
- public int getDataElement()
- {
- if ( listpointer == 0 )
- {
- throw new IllegalArgumentException( "no more element!" );
+ public int getDataElement() {
+ if (listpointer == 0) {
+ throw new IllegalArgumentException("no more element!");
}
- int data = ia[ listpointer ];
- listpointer = ia[ listpointer-1 ];
+ int data = ia[listpointer];
+ listpointer = ia[listpointer - 1];
return data;
}
-
- private void resize()
- {
- int[] ia2 = new int[2*ia.length];
- System.arraycopy( ia, 0, ia2, 0, ia.length );
+
+ private void resize() {
+ int[] ia2 = new int[2 * ia.length];
+ System.arraycopy(ia, 0, ia2, 0, ia.length);
ia = ia2;
}
}
diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache.java b/brouter-codec/src/main/java/btools/codec/MicroCache.java
index 7e22e3b..a73893e 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache.java
@@ -5,21 +5,20 @@ import btools.util.ByteDataWriter;
/**
* a micro-cache is a data cache for an area of some square kilometers or some
* hundreds or thousands nodes
- *
+ *
* This is the basic io-unit: always a full microcache is loaded from the
* data-file if a node is requested at a position not yet covered by the caches
* already loaded
- *
+ *
* The nodes are represented in a compact way (typical 20-50 bytes per node),
* but in a way that they do not depend on each other, and garbage collection is
* supported to remove the nodes already consumed from the cache.
- *
+ *
* The cache-internal data representation is different from that in the
* data-files, where a cache is encoded as a whole, allowing more
* redundancy-removal for a more compact encoding
*/
-public class MicroCache extends ByteDataWriter
-{
+public class MicroCache extends ByteDataWriter {
protected int[] faid;
protected int[] fapos;
protected int size = 0;
@@ -35,25 +34,21 @@ public class MicroCache extends ByteDataWriter
public static boolean debug = false;
- protected MicroCache( byte[] ab )
- {
- super( ab );
+ protected MicroCache(byte[] ab) {
+ super(ab);
}
- public final static MicroCache emptyNonVirgin = new MicroCache( null );
+ public final static MicroCache emptyNonVirgin = new MicroCache(null);
- static
- {
+ static {
emptyNonVirgin.virgin = false;
}
- public static MicroCache emptyCache()
- {
- return new MicroCache( null ); // TODO: singleton?
+ public static MicroCache emptyCache() {
+ return new MicroCache(null); // TODO: singleton?
}
- protected void init( int size )
- {
+ protected void init(int size) {
this.size = size;
delcount = 0;
delbytes = 0;
@@ -62,35 +57,31 @@ public class MicroCache extends ByteDataWriter
p2size >>= 1;
}
- public final void finishNode( long id )
- {
+ public final void finishNode(long id) {
fapos[size] = aboffset;
- faid[size] = shrinkId( id );
+ faid[size] = shrinkId(id);
size++;
}
- public final void discardNode()
- {
- aboffset = startPos( size );
+ public final void discardNode() {
+ aboffset = startPos(size);
}
- public final int getSize()
- {
+ public final int getSize() {
return size;
}
- public final int getDataSize()
- {
+ public final int getDataSize() {
return ab == null ? 0 : ab.length;
}
/**
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
- *
+ *
* If a node is not found in an empty cache, this is usually an edge-effect
* (data-file does not exist or neighboured data-files of differnt age),
* but is can as well be a symptom of a node-identity breaking bug.
- *
+ *
* Current implementation always returns false for not-found, however, for
* regression testing, at least for the case that is most likely a bug
* (node found but marked as deleted = ready for garbage collection
@@ -98,38 +89,31 @@ public class MicroCache extends ByteDataWriter
*
* @return true if id was found
*/
- public final boolean getAndClear( long id64 )
- {
- if ( size == 0 )
- {
+ public final boolean getAndClear(long id64) {
+ if (size == 0) {
return false;
}
- int id = shrinkId( id64 );
+ int id = shrinkId(id64);
int[] a = faid;
int offset = p2size;
int n = 0;
- while (offset > 0)
- {
+ while (offset > 0) {
int nn = n + offset;
- if ( nn < size && a[nn] <= id )
- {
+ if (nn < size && a[nn] <= id) {
n = nn;
}
offset >>= 1;
}
- if ( a[n] == id )
- {
- if ( ( fapos[n] & 0x80000000 ) == 0 )
- {
- aboffset = startPos( n );
+ if (a[n] == id) {
+ if ((fapos[n] & 0x80000000) == 0) {
+ aboffset = startPos(n);
aboffsetEnd = fapos[n];
fapos[n] |= 0x80000000; // mark deleted
delbytes += aboffsetEnd - aboffset;
delcount++;
return true;
- }
- else // .. marked as deleted
+ } else // .. marked as deleted
{
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
}
@@ -137,43 +121,35 @@ public class MicroCache extends ByteDataWriter
return false;
}
- protected final int startPos( int n )
- {
+ protected final int startPos(int n) {
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
}
- public final int collect( int threshold )
- {
- if ( delcount <= threshold )
- {
+ public final int collect(int threshold) {
+ if (delcount <= threshold) {
return 0;
}
virgin = false;
int nsize = size - delcount;
- if ( nsize == 0 )
- {
+ if (nsize == 0) {
faid = null;
fapos = null;
- }
- else
- {
+ } else {
int[] nfaid = new int[nsize];
int[] nfapos = new int[nsize];
int idx = 0;
byte[] nab = new byte[ab.length - delbytes];
int nab_off = 0;
- for ( int i = 0; i < size; i++ )
- {
+ for (int i = 0; i < size; i++) {
int pos = fapos[i];
- if ( ( pos & 0x80000000 ) == 0 )
- {
- int start = startPos( i );
+ if ((pos & 0x80000000) == 0) {
+ int start = startPos(i);
int end = fapos[i];
int len = end - start;
- System.arraycopy( ab, start, nab, nab_off, len );
+ System.arraycopy(ab, start, nab, nab_off, len);
nfaid[idx] = faid[i];
nab_off += len;
nfapos[idx] = nab_off;
@@ -185,17 +161,15 @@ public class MicroCache extends ByteDataWriter
ab = nab;
}
int deleted = delbytes;
- init( nsize );
+ init(nsize);
return deleted;
}
- public final void unGhost()
- {
+ public final void unGhost() {
ghost = false;
delcount = 0;
delbytes = 0;
- for ( int i = 0; i < size; i++ )
- {
+ for (int i = 0; i < size; i++) {
fapos[i] &= 0x7fffffff; // clear deleted flags
}
}
@@ -203,201 +177,168 @@ public class MicroCache extends ByteDataWriter
/**
* @return the 64-bit global id for the given cache-position
*/
- public final long getIdForIndex( int i )
- {
+ public final long getIdForIndex(int i) {
int id32 = faid[i];
- return expandId( id32 );
+ return expandId(id32);
}
/**
* expand a 32-bit micro-cache-internal id into a 64-bit (lon|lat) global-id
- *
+ *
* @see #shrinkId
*/
- public long expandId( int id32 )
- {
- throw new IllegalArgumentException( "expandId for empty cache" );
+ public long expandId(int id32) {
+ throw new IllegalArgumentException("expandId for empty cache");
}
/**
* shrink a 64-bit (lon|lat) global-id into a a 32-bit micro-cache-internal id
- *
+ *
* @see #expandId
*/
- public int shrinkId( long id64 )
- {
- throw new IllegalArgumentException( "shrinkId for empty cache" );
+ public int shrinkId(long id64) {
+ throw new IllegalArgumentException("shrinkId for empty cache");
}
/**
* @return true if the given lon/lat position is internal for that micro-cache
*/
- public boolean isInternal( int ilon, int ilat )
- {
- throw new IllegalArgumentException( "isInternal for empty cache" );
+ public boolean isInternal(int ilon, int ilat) {
+ throw new IllegalArgumentException("isInternal for empty cache");
}
/**
* (stasticially) encode the micro-cache into the format used in the datafiles
- *
- * @param buffer
- * byte array to encode into (considered big enough)
+ *
+ * @param buffer byte array to encode into (considered big enough)
* @return the size of the encoded data
*/
- public int encodeMicroCache( byte[] buffer )
- {
- throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
+ public int encodeMicroCache(byte[] buffer) {
+ throw new IllegalArgumentException("encodeMicroCache for empty cache");
}
/**
* Compare the content of this microcache to another
- *
+ *
* @return null if equals, else a diff-report
*/
- public String compareWith( MicroCache mc )
- {
- String msg = _compareWith( mc );
- if ( msg != null )
- {
- StringBuilder sb = new StringBuilder( msg );
- sb.append( "\nencode cache:\n" ).append( summary() );
- sb.append( "\ndecode cache:\n" ).append( mc.summary() );
+ public String compareWith(MicroCache mc) {
+ String msg = _compareWith(mc);
+ if (msg != null) {
+ StringBuilder sb = new StringBuilder(msg);
+ sb.append("\nencode cache:\n").append(summary());
+ sb.append("\ndecode cache:\n").append(mc.summary());
return sb.toString();
}
return null;
}
- private String summary()
- {
- StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
- for ( int i = 0; i < size; i++ )
- {
- sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
+ private String summary() {
+ StringBuilder sb = new StringBuilder("size=" + size + " aboffset=" + aboffset);
+ for (int i = 0; i < size; i++) {
+ sb.append("\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i]);
}
return sb.toString();
}
- private String _compareWith( MicroCache mc )
- {
- if ( size != mc.size )
- {
+ private String _compareWith(MicroCache mc) {
+ if (size != mc.size) {
return "size missmatch: " + size + "->" + mc.size;
}
- for ( int i = 0; i < size; i++ )
- {
- if ( faid[i] != mc.faid[i] )
- {
+ for (int i = 0; i < size; i++) {
+ if (faid[i] != mc.faid[i]) {
return "faid missmatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
}
int start = i > 0 ? fapos[i - 1] : 0;
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
int len = end - start;
- for ( int offset = 0; offset < len; offset++ )
- {
- if ( mc.ab.length <= start + offset )
- {
+ for (int offset = 0; offset < len; offset++) {
+ if (mc.ab.length <= start + offset) {
return "data buffer too small";
}
- if ( ab[start + offset] != mc.ab[start + offset] )
- {
+ if (ab[start + offset] != mc.ab[start + offset]) {
return "data missmatch at index " + i + " offset=" + offset;
}
}
- if ( fapos[i] != mc.fapos[i] )
- {
+ if (fapos[i] != mc.fapos[i]) {
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
}
}
- if ( aboffset != mc.aboffset )
- {
+ if (aboffset != mc.aboffset) {
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
}
return null;
}
- public void calcDelta( MicroCache mc1, MicroCache mc2 )
- {
- int idx1 = 0;
- int idx2 = 0;
+ 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;
+ 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++;
- }
+ 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 < len1) {
+ if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) {
+ break;
+ }
+ i++;
+ }
+ if (i == len1) {
+ continue; // same data -> do nothing
+ }
+ }
+ }
+ write(mc2.ab, start2, len2);
+ } else {
+ idx1++;
+ id = id1; // deleted node
+ }
+ fapos[size] = aboffset;
+ faid[size] = id;
+ size++;
+ }
}
- public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
- {
- int idx1 = 0;
- int idx2 = 0;
+ public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) {
+ 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;
- if ( id1 >= id2 ) // data from diff file wins
- {
- int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
- int len2 = mc2.fapos[idx2++] - start2;
- if ( keepEmptyNodes || len2 > 0 )
- {
- write( mc2.ab, start2, len2 );
- fapos[size] = aboffset;
- faid[size++] = id2;
- }
- if ( id1 == id2 ) // // id exists in both caches
- {
- idx1++;
- }
- }
- else // use data from base file
- {
- int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
- int len1 = mc1.fapos[idx1++] - start1;
- write( mc1.ab, start1, len1 );
- fapos[size] = aboffset;
- faid[size++] = id1;
- }
- }
+ 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;
+ if (id1 >= id2) // data from diff file wins
+ {
+ int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
+ int len2 = mc2.fapos[idx2++] - start2;
+ if (keepEmptyNodes || len2 > 0) {
+ write(mc2.ab, start2, len2);
+ fapos[size] = aboffset;
+ faid[size++] = id2;
+ }
+ if (id1 == id2) // // id exists in both caches
+ {
+ idx1++;
+ }
+ } else // use data from base file
+ {
+ int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
+ int len1 = mc1.fapos[idx1++] - start1;
+ write(mc1.ab, start1, len1);
+ fapos[size] = aboffset;
+ faid[size++] = id1;
+ }
+ }
}
}
diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
index 16223a1..5ad7872 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
@@ -9,222 +9,204 @@ import btools.util.IByteArrayUnifier;
* MicroCache2 is the new format that uses statistical encoding and
* is able to do access filtering and waypoint matching during encoding
*/
-public final class MicroCache2 extends MicroCache
-{
+public final class MicroCache2 extends MicroCache {
private int lonBase;
private int latBase;
private int cellsize;
- public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor )
- {
- super( databuffer ); // sets ab=databuffer, aboffset=0
+ public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) {
+ super(databuffer); // sets ab=databuffer, aboffset=0
faid = new int[size];
fapos = new int[size];
this.size = 0;
cellsize = 1000000 / divisor;
- lonBase = lonIdx*cellsize;
- latBase = latIdx*cellsize;
- }
-
- public byte[] readUnified( int len, IByteArrayUnifier u )
- {
- byte[] b = u.unify( ab, aboffset, len );
- aboffset += len;
- return b;
+ lonBase = lonIdx * cellsize;
+ latBase = latIdx * cellsize;
}
- public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher )
- {
- super( null );
+ public byte[] readUnified(int len, IByteArrayUnifier u) {
+ byte[] b = u.unify(ab, aboffset, len);
+ aboffset += len;
+ return b;
+ }
+
+ public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) {
+ super(null);
cellsize = 1000000 / divisor;
- lonBase = lonIdx*cellsize;
- latBase = latIdx*cellsize;
+ lonBase = lonIdx * cellsize;
+ latBase = latIdx * cellsize;
- TagValueCoder wayTagCoder = new TagValueCoder( bc, dataBuffers, wayValidator );
- TagValueCoder nodeTagCoder = new TagValueCoder( bc, dataBuffers, null );
- NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder( bc );
- NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder( bc );
+ TagValueCoder wayTagCoder = new TagValueCoder(bc, dataBuffers, wayValidator);
+ TagValueCoder nodeTagCoder = new TagValueCoder(bc, dataBuffers, null);
+ NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder(bc);
+ NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder extLonDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder extLatDiff = new NoisyDiffCoder(bc);
- NoisyDiffCoder transEleDiff = new NoisyDiffCoder( bc );
+ NoisyDiffCoder transEleDiff = new NoisyDiffCoder(bc);
- size = bc.decodeNoisyNumber( 5 );
+ size = bc.decodeNoisyNumber(5);
faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2;
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
-
-
-
- int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
- int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
- if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
- bc.decodeSortedArray( faid, 0, size, 29, 0 );
-
- for( int n = 0; n> 32);
- alat[n] = (int)(id64 & 0xffffffff);
+ int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
+ int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
+
+ if (debug)
+ System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
+
+ bc.decodeSortedArray(faid, 0, size, 29, 0);
+
+ for (int n = 0; n < size; n++) {
+ long id64 = expandId(faid[n]);
+ alon[n] = (int) (id64 >> 32);
+ alat[n] = (int) (id64 & 0xffffffff);
}
-
- int netdatasize = bc.decodeNoisyNumber( 10 );
+
+ int netdatasize = bc.decodeNoisyNumber(10);
ab = netdatasize > dataBuffers.bbuf1.length ? new byte[netdatasize] : dataBuffers.bbuf1;
aboffset = 0;
- int[] validBits = new int[(size+31)>>5];
+ int[] validBits = new int[(size + 31) >> 5];
int finaldatasize = 0;
- LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
+ LinkedListContainer reverseLinks = new LinkedListContainer(size, dataBuffers.ibuf1);
int selev = 0;
- for( int n=0; n> 5 ] |= 1 << n; // mark dummy-node valid
+ validBits[n >> 5] |= 1 << n; // mark dummy-node valid
continue; // empty node escape (delta files only)
}
- while( featureId != 0 )
- {
- int bitsize = bc.decodeNoisyNumber( 5 );
+ while (featureId != 0) {
+ int bitsize = bc.decodeNoisyNumber(5);
- if ( featureId == 2 ) // exceptions to turn-restriction
+ if (featureId == 2) // exceptions to turn-restriction
{
- trExceptions = (short)bc.decodeBounded( 1023 );
- }
- else if ( featureId == 1 ) // turn-restriction
+ trExceptions = (short) bc.decodeBounded(1023);
+ } else if (featureId == 1) // turn-restriction
{
- writeBoolean( true );
- writeShort( trExceptions ); // exceptions from previous feature
+ writeBoolean(true);
+ writeShort(trExceptions); // exceptions from previous feature
trExceptions = 0;
- writeBoolean( bc.decodeBit() ); // isPositive
- writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // fromLon
- writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
- writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
- writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
- }
- else
- {
- for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
+ writeBoolean(bc.decodeBit()); // isPositive
+ writeInt(ilon + bc.decodeNoisyDiff(10)); // fromLon
+ writeInt(ilat + bc.decodeNoisyDiff(10)); // fromLat
+ writeInt(ilon + bc.decodeNoisyDiff(10)); // toLon
+ writeInt(ilat + bc.decodeNoisyDiff(10)); // toLat
+ } else {
+ for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip
}
featureId = bc.decodeVarBits();
}
- writeBoolean( false );
+ writeBoolean(false);
selev += nodeEleDiff.decodeSignedValue();
- writeShort( (short) selev );
+ writeShort((short) selev);
TagValueWrapper nodeTags = nodeTagCoder.decodeTagValueSet();
- writeVarBytes( nodeTags == null ? null : nodeTags.data );
+ writeVarBytes(nodeTags == null ? null : nodeTags.data);
- int links = bc.decodeNoisyNumber( 1 );
- if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
- for( int li=0; li>= 2;
}
int lon32 = lonBase + dlon;
int lat32 = latBase + dlat;
- return ((long)lon32)<<32 | lat32;
+ return ((long) lon32) << 32 | lat32;
}
@Override
- public int shrinkId( long id64 )
- {
- int lon32 = (int)(id64 >> 32);
- int lat32 = (int)(id64 & 0xffffffff);
+ public int shrinkId(long id64) {
+ int lon32 = (int) (id64 >> 32);
+ int lat32 = (int) (id64 & 0xffffffff);
int dlon = lon32 - lonBase;
int dlat = lat32 - latBase;
int id32 = 0;
- for( int bm = 0x4000; bm > 0; bm >>= 1 )
- {
+ for (int bm = 0x4000; bm > 0; bm >>= 1) {
id32 <<= 2;
- if ( ( dlon & bm ) != 0 ) id32 |= 1;
- if ( ( dlat & bm ) != 0 ) id32 |= 2;
+ if ((dlon & bm) != 0) id32 |= 1;
+ if ((dlat & bm) != 0) id32 |= 2;
}
return id32;
}
@Override
- public boolean isInternal( int ilon, int ilat )
- {
+ public boolean isInternal(int ilon, int ilat) {
return ilon >= lonBase && ilon < lonBase + cellsize
- && ilat >= latBase && ilat < latBase + cellsize;
+ && ilat >= latBase && ilat < latBase + cellsize;
}
@Override
- public int encodeMicroCache( byte[] buffer )
- {
- HashMap idMap = new HashMap();
- for( int n=0; n idMap = new HashMap();
+ for (int n = 0; n < size; n++) // loop over nodes
{
- idMap.put( Long.valueOf( expandId( faid[n] ) ), Integer.valueOf( n ) );
+ idMap.put(Long.valueOf(expandId(faid[n])), Integer.valueOf(n));
}
- IntegerFifo3Pass linkCounts = new IntegerFifo3Pass( 256 );
- IntegerFifo3Pass transCounts = new IntegerFifo3Pass( 256 );
- IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass( 16 );
+ IntegerFifo3Pass linkCounts = new IntegerFifo3Pass(256);
+ IntegerFifo3Pass transCounts = new IntegerFifo3Pass(256);
+ IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass(16);
TagValueCoder wayTagCoder = new TagValueCoder();
TagValueCoder nodeTagCoder = new TagValueCoder();
@@ -337,182 +310,173 @@ public final class MicroCache2 extends MicroCache
NoisyDiffCoder extLonDiff = new NoisyDiffCoder();
NoisyDiffCoder extLatDiff = new NoisyDiffCoder();
NoisyDiffCoder transEleDiff = new NoisyDiffCoder();
-
+
int netdatasize = 0;
- for(int pass=1;; pass++) // 3 passes: counters, stat-collection, encoding
+ for (int pass = 1; ; pass++) // 3 passes: counters, stat-collection, encoding
{
boolean dostats = pass == 3;
boolean dodebug = debug && pass == 3;
-
- if ( pass < 3 ) netdatasize = fapos[size-1];
-
- StatCoderContext bc = new StatCoderContext( buffer );
+
+ if (pass < 3) netdatasize = fapos[size - 1];
+
+ StatCoderContext bc = new StatCoderContext(buffer);
linkCounts.init();
transCounts.init();
restrictionBits.init();
- wayTagCoder.encodeDictionary( bc );
- if ( dostats ) bc.assignBits( "wayTagDictionary" );
- nodeTagCoder.encodeDictionary( bc );
- if ( dostats ) bc.assignBits( "nodeTagDictionary" );
- nodeIdxDiff.encodeDictionary( bc );
- nodeEleDiff.encodeDictionary( bc );
- extLonDiff.encodeDictionary( bc );
- extLatDiff.encodeDictionary( bc );
- transEleDiff.encodeDictionary( bc );
- if ( dostats ) bc.assignBits( "noisebits" );
- bc.encodeNoisyNumber( size, 5 );
- if ( dostats ) bc.assignBits( "nodecount" );
- bc.encodeSortedArray( faid, 0, size, 0x20000000, 0 );
- if ( dostats ) bc.assignBits( "node-positions" );
- bc.encodeNoisyNumber( netdatasize, 10 ); // net-size
- if ( dostats ) bc.assignBits( "netdatasize" );
- if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
+ wayTagCoder.encodeDictionary(bc);
+ if (dostats) bc.assignBits("wayTagDictionary");
+ nodeTagCoder.encodeDictionary(bc);
+ if (dostats) bc.assignBits("nodeTagDictionary");
+ nodeIdxDiff.encodeDictionary(bc);
+ nodeEleDiff.encodeDictionary(bc);
+ extLonDiff.encodeDictionary(bc);
+ extLatDiff.encodeDictionary(bc);
+ transEleDiff.encodeDictionary(bc);
+ if (dostats) bc.assignBits("noisebits");
+ bc.encodeNoisyNumber(size, 5);
+ if (dostats) bc.assignBits("nodecount");
+ bc.encodeSortedArray(faid, 0, size, 0x20000000, 0);
+ if (dostats) bc.assignBits("node-positions");
+ bc.encodeNoisyNumber(netdatasize, 10); // net-size
+ if (dostats) bc.assignBits("netdatasize");
+ if (dodebug) System.out.println("*** encoding cache of size=" + size);
int lastSelev = 0;
-
- for( int n=0; n> 32);
- int ilat = (int)(id64 & 0xffffffff);
+ long id64 = expandId(faid[n]);
+ int ilon = (int) (id64 >> 32);
+ int ilat = (int) (id64 & 0xffffffff);
- if ( aboffset == aboffsetEnd )
- {
- bc.encodeVarBits( 13 ); // empty node escape (delta files only)
+ if (aboffset == aboffsetEnd) {
+ bc.encodeVarBits(13); // empty node escape (delta files only)
continue;
}
// write turn restrictions
- while( readBoolean() )
- {
+ while (readBoolean()) {
short exceptions = readShort(); // except bikes, psv, ...
- if ( exceptions != 0 )
- {
- bc.encodeVarBits( 2 ); // 2 = tr exceptions
- bc.encodeNoisyNumber( 10 , 5 ); // bit-count
- bc.encodeBounded( 1023 , exceptions & 1023 );
+ if (exceptions != 0) {
+ bc.encodeVarBits(2); // 2 = tr exceptions
+ bc.encodeNoisyNumber(10, 5); // bit-count
+ bc.encodeBounded(1023, exceptions & 1023);
}
- bc.encodeVarBits( 1 ); // 1 = turn restriction
- bc.encodeNoisyNumber( restrictionBits.getNext(), 5 ); // bit-count using look-ahead fifo
+ bc.encodeVarBits(1); // 1 = turn restriction
+ bc.encodeNoisyNumber(restrictionBits.getNext(), 5); // bit-count using look-ahead fifo
long b0 = bc.getWritingBitPosition();
- bc.encodeBit( readBoolean() ); // isPositive
- bc.encodeNoisyDiff( readInt() - ilon, 10 ); // fromLon
- bc.encodeNoisyDiff( readInt() - ilat, 10 ); // fromLat
- bc.encodeNoisyDiff( readInt() - ilon, 10 ); // toLon
- bc.encodeNoisyDiff( readInt() - ilat, 10 ); // toLat
- restrictionBits.add( (int)( bc.getWritingBitPosition() - b0 ) );
+ bc.encodeBit(readBoolean()); // isPositive
+ bc.encodeNoisyDiff(readInt() - ilon, 10); // fromLon
+ bc.encodeNoisyDiff(readInt() - ilat, 10); // fromLat
+ bc.encodeNoisyDiff(readInt() - ilon, 10); // toLon
+ bc.encodeNoisyDiff(readInt() - ilat, 10); // toLat
+ restrictionBits.add((int) (bc.getWritingBitPosition() - b0));
}
- bc.encodeVarBits( 0 ); // end of extra data
+ bc.encodeVarBits(0); // end of extra data
- if ( dostats ) bc.assignBits( "extradata" );
+ if (dostats) bc.assignBits("extradata");
int selev = readShort();
- nodeEleDiff.encodeSignedValue( selev - lastSelev );
- if ( dostats ) bc.assignBits( "nodeele" );
+ nodeEleDiff.encodeSignedValue(selev - lastSelev);
+ if (dostats) bc.assignBits("nodeele");
lastSelev = selev;
- nodeTagCoder.encodeTagValueSet( readVarBytes() );
- if ( dostats ) bc.assignBits( "nodeTagIdx" );
+ nodeTagCoder.encodeTagValueSet(readVarBytes());
+ if (dostats) bc.assignBits("nodeTagIdx");
int nlinks = linkCounts.getNext();
- if ( dodebug ) System.out.println( "*** nlinks=" + nlinks );
- bc.encodeNoisyNumber( nlinks, 1 );
- if ( dostats ) bc.assignBits( "link-counts" );
-
+ if (dodebug) System.out.println("*** nlinks=" + nlinks);
+ bc.encodeNoisyNumber(nlinks, 1);
+ if (dostats) bc.assignBits("link-counts");
+
nlinks = 0;
- while( hasMoreData() ) // loop over links
+ while (hasMoreData()) // loop over links
{
// read link data
int startPointer = aboffset;
int endPointer = getEndPointer();
-
+
int ilonlink = ilon + readVarLengthSigned();
int ilatlink = ilat + readVarLengthSigned();
-
+
int sizecode = readVarLengthUnsigned();
- boolean isReverse = ( sizecode & 1 ) != 0;
+ boolean isReverse = (sizecode & 1) != 0;
int descSize = sizecode >> 1;
byte[] description = null;
- if ( descSize > 0 )
- {
+ if (descSize > 0) {
description = new byte[descSize];
- readFully( description );
+ readFully(description);
}
-
- long link64 = ((long)ilonlink)<<32 | ilatlink;
- Integer idx = idMap.get( Long.valueOf( link64 ) );
+
+ 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 );
- netdatasize -= aboffset-startPointer;
+ if (isReverse && isInternal) {
+ if (dodebug)
+ System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal);
+ netdatasize -= aboffset - startPointer;
continue; // do not encode internal reverse links
}
- if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
+ if (dodebug)
+ System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
nlinks++;
-
- if ( isInternal )
- {
+
+ if (isInternal) {
int nodeIdx = idx.intValue();
- if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
- if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
- nodeIdxDiff.encodeSignedValue( nodeIdx - n );
- if ( dostats ) bc.assignBits( "nodeIdx" );
+ if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx);
+ if (nodeIdx == n) throw new RuntimeException("ups: self ref?");
+ nodeIdxDiff.encodeSignedValue(nodeIdx - n);
+ if (dostats) bc.assignBits("nodeIdx");
+ } else {
+ nodeIdxDiff.encodeSignedValue(0);
+ bc.encodeBit(isReverse);
+ extLonDiff.encodeSignedValue(ilonlink - ilon);
+ extLatDiff.encodeSignedValue(ilatlink - ilat);
+ if (dostats) bc.assignBits("externalNode");
}
- else
- {
- nodeIdxDiff.encodeSignedValue( 0 );
- bc.encodeBit( isReverse );
- extLonDiff.encodeSignedValue( ilonlink - ilon );
- extLatDiff.encodeSignedValue( ilatlink - ilat );
- if ( dostats ) bc.assignBits( "externalNode" );
- }
- wayTagCoder.encodeTagValueSet( description );
- if ( dostats ) bc.assignBits( "wayDescIdx" );
-
- if ( !isReverse )
- {
- byte[] geometry = readDataUntil( endPointer );
+ wayTagCoder.encodeTagValueSet(description);
+ if (dostats) bc.assignBits("wayDescIdx");
+
+ if (!isReverse) {
+ byte[] geometry = readDataUntil(endPointer);
// write transition nodes
int count = transCounts.getNext();
- if ( dodebug ) System.out.println( "*** encoding geometry with count=" + count );
- bc.encodeVarBits( count++ );
- if ( dostats ) bc.assignBits( "transcount" );
+ if (dodebug) System.out.println("*** encoding geometry with count=" + count);
+ bc.encodeVarBits(count++);
+ if (dostats) bc.assignBits("transcount");
int transcount = 0;
- if ( geometry != null )
- {
+ if (geometry != null) {
int dlon_remaining = ilonlink - ilon;
int dlat_remaining = ilatlink - ilat;
-
- ByteDataReader r = new ByteDataReader( geometry );
- while ( r.hasMoreData() )
- {
+
+ ByteDataReader r = new ByteDataReader(geometry);
+ while (r.hasMoreData()) {
transcount++;
-
+
int dlon = r.readVarLengthSigned();
int dlat = r.readVarLengthSigned();
- bc.encodePredictedValue( dlon, dlon_remaining/count );
- bc.encodePredictedValue( dlat, dlat_remaining/count );
+ bc.encodePredictedValue(dlon, dlon_remaining / count);
+ bc.encodePredictedValue(dlat, dlat_remaining / count);
dlon_remaining -= dlon;
dlat_remaining -= dlat;
- if ( count > 1 ) count--;
- if ( dostats ) bc.assignBits( "transpos" );
- transEleDiff.encodeSignedValue( r.readVarLengthSigned() );
- if ( dostats ) bc.assignBits( "transele" );
+ if (count > 1) count--;
+ if (dostats) bc.assignBits("transpos");
+ transEleDiff.encodeSignedValue(r.readVarLengthSigned());
+ if (dostats) bc.assignBits("transele");
}
}
- transCounts.add( transcount );
+ transCounts.add(transcount);
}
}
- linkCounts.add( nlinks );
+ linkCounts.add(nlinks);
}
- if ( pass == 3 )
- {
+ if (pass == 3) {
return bc.closeAndGetEncodedLength();
}
}
diff --git a/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java b/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java
index 1fffa4c..eb53eab 100644
--- a/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java
+++ b/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java
@@ -4,12 +4,11 @@ package btools.codec;
* Encoder/Decoder for signed integers that automatically detects the typical
* range of these numbers to determine a noisy-bit count as a very simple
* dictionary
- *
+ *
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
* but doesn't do anything at pass1
*/
-public final class NoisyDiffCoder
-{
+public final class NoisyDiffCoder {
private int tot;
private int[] freqs;
private int noisybits;
@@ -19,8 +18,7 @@ public final class NoisyDiffCoder
/**
* Create a decoder and read the noisy-bit count from the gibe context
*/
- public NoisyDiffCoder( StatCoderContext bc )
- {
+ public NoisyDiffCoder(StatCoderContext bc) {
noisybits = bc.decodeVarBits();
this.bc = bc;
}
@@ -28,60 +26,49 @@ public final class NoisyDiffCoder
/**
* Create an encoder for 3-pass-encoding
*/
- public NoisyDiffCoder()
- {
+ public NoisyDiffCoder() {
}
/**
* encodes a signed int (pass3 only, stats collection in pass2)
*/
- public void encodeSignedValue( int value )
- {
- if ( pass == 3 )
- {
- bc.encodeNoisyDiff( value, noisybits );
- }
- else if ( pass == 2 )
- {
- count( value < 0 ? -value : value );
+ public void encodeSignedValue(int value) {
+ if (pass == 3) {
+ bc.encodeNoisyDiff(value, noisybits);
+ } else if (pass == 2) {
+ count(value < 0 ? -value : value);
}
}
/**
* decodes a signed int
*/
- public int decodeSignedValue()
- {
- return bc.decodeNoisyDiff( noisybits );
+ public int decodeSignedValue() {
+ return bc.decodeNoisyDiff(noisybits);
}
/**
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
* from the stats collected in pass2 and writes that to the given context
*/
- public void encodeDictionary( StatCoderContext bc )
- {
- if ( ++pass == 3 )
- {
+ public void encodeDictionary(StatCoderContext bc) {
+ if (++pass == 3) {
// how many noisy bits?
- for ( noisybits = 0; noisybits < 14 && tot > 0; noisybits++ )
- {
- if ( freqs[noisybits] < ( tot >> 1 ) )
+ for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
+ if (freqs[noisybits] < (tot >> 1))
break;
}
- bc.encodeVarBits( noisybits );
+ bc.encodeVarBits(noisybits);
}
this.bc = bc;
}
- private void count( int value )
- {
- if ( freqs == null )
+ private void count(int value) {
+ if (freqs == null)
freqs = new int[14];
int bm = 1;
- for ( int i = 0; i < 14; i++ )
- {
- if ( value < bm )
+ for (int i = 0; i < 14; i++) {
+ if (value < bm)
break;
else
freqs[i]++;
diff --git a/brouter-codec/src/main/java/btools/codec/StatCoderContext.java b/brouter-codec/src/main/java/btools/codec/StatCoderContext.java
index 4b67a54..772dcd2 100644
--- a/brouter-codec/src/main/java/btools/codec/StatCoderContext.java
+++ b/brouter-codec/src/main/java/btools/codec/StatCoderContext.java
@@ -4,23 +4,19 @@ import java.util.TreeMap;
import btools.util.BitCoderContext;
-public final class StatCoderContext extends BitCoderContext
-{
+public final class StatCoderContext extends BitCoderContext {
private static TreeMap statsPerName;
private long lastbitpos = 0;
private static final int[] noisy_bits = new int[1024];
- static
- {
+ static {
// noisybits lookup
- for( int i=0; i<1024; i++ )
- {
+ for (int i = 0; i < 1024; i++) {
int p = i;
int noisybits = 0;
- while (p > 2)
- {
+ while (p > 2) {
noisybits++;
p >>= 1;
}
@@ -29,29 +25,25 @@ public final class StatCoderContext extends BitCoderContext
}
- public StatCoderContext( byte[] ab )
- {
- super( ab );
+ public StatCoderContext(byte[] ab) {
+ super(ab);
}
/**
* assign the de-/encoded bits since the last call assignBits to the given
* name. Used for encoding statistics
- *
+ *
* @see #getBitReport
*/
- public void assignBits( String name )
- {
+ public void assignBits(String name) {
long bitpos = getWritingBitPosition();
- if ( statsPerName == null )
- {
+ if (statsPerName == null) {
statsPerName = new TreeMap();
}
- long[] stats = statsPerName.get( name );
- if ( stats == null )
- {
+ long[] stats = statsPerName.get(name);
+ if (stats == null) {
stats = new long[2];
- statsPerName.put( name, stats );
+ statsPerName.put(name, stats);
}
stats[0] += bitpos - lastbitpos;
stats[1] += 1;
@@ -60,20 +52,17 @@ public final class StatCoderContext extends BitCoderContext
/**
* Get a textual report on the bit-statistics
- *
+ *
* @see #assignBits
*/
- public static String getBitReport()
- {
- if ( statsPerName == null )
- {
+ public static String getBitReport() {
+ if (statsPerName == null) {
return "";
}
StringBuilder sb = new StringBuilder();
- for ( String name : statsPerName.keySet() )
- {
- long[] stats = statsPerName.get( name );
- sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
+ for (String name : statsPerName.keySet()) {
+ long[] stats = statsPerName.get(name);
+ sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
}
statsPerName = null;
return sb.toString();
@@ -82,76 +71,65 @@ public final class StatCoderContext extends BitCoderContext
/**
* encode an unsigned integer with some of of least significant bits
* considered noisy
- *
+ *
* @see #decodeNoisyNumber
*/
- public void encodeNoisyNumber( int value, int noisybits )
- {
- if ( value < 0 )
- {
- throw new IllegalArgumentException( "encodeVarBits expects positive value" );
+ public void encodeNoisyNumber(int value, int noisybits) {
+ if (value < 0) {
+ throw new IllegalArgumentException("encodeVarBits expects positive value");
}
- if ( noisybits > 0 )
- {
- int mask = 0xffffffff >>> ( 32 - noisybits );
- encodeBounded( mask, value & mask );
+ if (noisybits > 0) {
+ int mask = 0xffffffff >>> (32 - noisybits);
+ encodeBounded(mask, value & mask);
value >>= noisybits;
}
- encodeVarBits( value );
+ encodeVarBits(value);
}
/**
* decode an unsigned integer with some of of least significant bits
* considered noisy
- *
+ *
* @see #encodeNoisyNumber
*/
- public int decodeNoisyNumber( int noisybits )
- {
- int value = decodeBits( noisybits );
- return value | ( decodeVarBits() << noisybits );
+ public int decodeNoisyNumber(int noisybits) {
+ int value = decodeBits(noisybits);
+ return value | (decodeVarBits() << noisybits);
}
/**
* encode a signed integer with some of of least significant bits considered
* noisy
- *
+ *
* @see #decodeNoisyDiff
*/
- public void encodeNoisyDiff( int value, int noisybits )
- {
- if ( noisybits > 0 )
- {
- value += 1 << ( noisybits - 1 );
- int mask = 0xffffffff >>> ( 32 - noisybits );
- encodeBounded( mask, value & mask );
+ public void encodeNoisyDiff(int value, int noisybits) {
+ if (noisybits > 0) {
+ value += 1 << (noisybits - 1);
+ int mask = 0xffffffff >>> (32 - noisybits);
+ encodeBounded(mask, value & mask);
value >>= noisybits;
}
- encodeVarBits( value < 0 ? -value : value );
- if ( value != 0 )
- {
- encodeBit( value < 0 );
+ encodeVarBits(value < 0 ? -value : value);
+ if (value != 0) {
+ encodeBit(value < 0);
}
}
/**
* decode a signed integer with some of of least significant bits considered
* noisy
- *
+ *
* @see #encodeNoisyDiff
*/
- public int decodeNoisyDiff( int noisybits )
- {
+ public int decodeNoisyDiff(int noisybits) {
int value = 0;
- if ( noisybits > 0 )
- {
- value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
+ if (noisybits > 0) {
+ value = decodeBits(noisybits) - (1 << (noisybits - 1));
}
int val2 = decodeVarBits() << noisybits;
- if ( val2 != 0 )
- {
- if ( decodeBit() )
- {
+ if (val2 != 0) {
+ if (decodeBit()) {
val2 = -val2;
}
}
@@ -161,38 +139,34 @@ public final class StatCoderContext extends BitCoderContext
/**
* encode a signed integer with the typical range and median taken from the
* predicted value
- *
+ *
* @see #decodePredictedValue
*/
- public void encodePredictedValue( int value, int predictor )
- {
+ public void encodePredictedValue(int value, int predictor) {
int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0;
- while (p > 2)
- {
+ while (p > 2) {
noisybits++;
p >>= 1;
}
- encodeNoisyDiff( value - predictor, noisybits );
+ encodeNoisyDiff(value - predictor, noisybits);
}
/**
* decode a signed integer with the typical range and median taken from the
* predicted value
- *
+ *
* @see #encodePredictedValue
*/
- public int decodePredictedValue( int predictor )
- {
+ public int decodePredictedValue(int predictor) {
int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0;
- while (p > 1023)
- {
+ while (p > 1023) {
noisybits++;
p >>= 1;
}
- return predictor + decodeNoisyDiff( noisybits + noisy_bits[p] );
+ return predictor + decodeNoisyDiff(noisybits + noisy_bits[p]);
}
/**
@@ -201,30 +175,22 @@ public final class StatCoderContext extends BitCoderContext
* number of values with the current bit being 0. This yields an number of
* bits per value that only depends on the typical distance between subsequent
* values and also benefits
- *
- * @param values
- * the array to encode
- * @param offset
- * position in this array where to start
- * @param subsize
- * number of values to encode
- * @param nextbit
- * bitmask with the most significant bit set to 1
- * @param mask
- * should be 0
+ *
+ * @param values the array to encode
+ * @param offset position in this array where to start
+ * @param subsize number of values to encode
+ * @param nextbit bitmask with the most significant bit set to 1
+ * @param mask should be 0
*/
- public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
- {
- if ( subsize == 1 ) // last-choice shortcut
+ public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
+ if (subsize == 1) // last-choice shortcut
{
- while (nextbit != 0)
- {
- encodeBit( ( values[offset] & nextbit ) != 0 );
+ while (nextbit != 0) {
+ encodeBit((values[offset] & nextbit) != 0);
nextbit >>= 1;
}
}
- if ( nextbit == 0 )
- {
+ if (nextbit == 0) {
return;
}
@@ -234,71 +200,55 @@ public final class StatCoderContext extends BitCoderContext
// count 0-bit-fraction
int i = offset;
int end = subsize + offset;
- for ( ; i < end; i++ )
- {
- if ( ( values[i] & mask ) != data )
- {
+ for (; i < end; i++) {
+ if ((values[i] & mask) != data) {
break;
}
}
int size1 = i - offset;
int size2 = subsize - size1;
- encodeBounded( subsize, size1 );
- if ( size1 > 0 )
- {
- encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
+ encodeBounded(subsize, size1);
+ if (size1 > 0) {
+ encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
}
- if ( size2 > 0 )
- {
- encodeSortedArray( values, i, size2, nextbit >> 1, mask );
+ if (size2 > 0) {
+ encodeSortedArray(values, i, size2, nextbit >> 1, mask);
}
}
/**
+ * @param values the array to encode
+ * @param offset position in this array where to start
+ * @param subsize number of values to encode
+ * @param nextbit bitmask with the most significant bit set to 1
+ * @param value should be 0
* @see #encodeSortedArray
- *
- * @param values
- * the array to encode
- * @param offset
- * position in this array where to start
- * @param subsize
- * number of values to encode
- * @param nextbit
- * bitmask with the most significant bit set to 1
- * @param value
- * should be 0
*/
- public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
- {
- if ( subsize == 1 ) // last-choice shortcut
+ public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
+ if (subsize == 1) // last-choice shortcut
{
- if ( nextbitpos >= 0 )
- {
- value |= decodeBitsReverse( nextbitpos+1 );
+ if (nextbitpos >= 0) {
+ value |= decodeBitsReverse(nextbitpos + 1);
}
values[offset] = value;
return;
}
- if ( nextbitpos < 0 )
- {
- while (subsize-- > 0)
- {
+ if (nextbitpos < 0) {
+ while (subsize-- > 0) {
values[offset++] = value;
}
return;
}
- int size1 = decodeBounded( subsize );
+ int size1 = decodeBounded(subsize);
int size2 = subsize - size1;
- if ( size1 > 0 )
- {
- decodeSortedArray( values, offset, size1, nextbitpos-1, value );
+ if (size1 > 0) {
+ decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
}
- if ( size2 > 0 )
- {
- decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
+ if (size2 > 0) {
+ decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
}
}
diff --git a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
index 72b61c7..f307b2e 100644
--- a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
+++ b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java
@@ -8,51 +8,42 @@ import btools.util.BitCoderContext;
/**
* Encoder/Decoder for way-/node-descriptions
- *
+ *
* It detects identical descriptions and sorts them
* into a huffman-tree according to their frequencies
- *
+ *
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
* but doesn't do anything at pass1
*/
-public final class TagValueCoder
-{
+public final class TagValueCoder {
private HashMap identityMap;
private Object tree;
private BitCoderContext bc;
private int pass;
private int nextTagValueSetId;
- public void encodeTagValueSet( byte[] data )
- {
- if ( pass == 1 )
- {
+ public void encodeTagValueSet(byte[] data) {
+ if (pass == 1) {
return;
}
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
tvsProbe.data = data;
- TagValueSet tvs = identityMap.get( tvsProbe );
- if ( pass == 3 )
- {
- bc.encodeBounded( tvs.range - 1, tvs.code );
- }
- else if ( pass == 2 )
- {
- if ( tvs == null )
- {
+ TagValueSet tvs = identityMap.get(tvsProbe);
+ if (pass == 3) {
+ bc.encodeBounded(tvs.range - 1, tvs.code);
+ } else if (pass == 2) {
+ if (tvs == null) {
tvs = tvsProbe;
nextTagValueSetId++;
- identityMap.put( tvs, tvs );
+ identityMap.put(tvs, tvs);
}
tvs.frequency++;
}
}
- public TagValueWrapper decodeTagValueSet()
- {
+ public TagValueWrapper decodeTagValueSet() {
Object node = tree;
- while (node instanceof TreeNode)
- {
+ while (node instanceof TreeNode) {
TreeNode tn = (TreeNode) node;
boolean nextBit = bc.decodeBit();
node = nextBit ? tn.child2 : tn.child1;
@@ -60,104 +51,87 @@ public final class TagValueCoder
return (TagValueWrapper) node;
}
- public void encodeDictionary( BitCoderContext bc )
- {
- if ( ++pass == 3 )
- {
- if ( identityMap.size() == 0 )
- {
+ public void encodeDictionary(BitCoderContext bc) {
+ if (++pass == 3) {
+ if (identityMap.size() == 0) {
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
- identityMap.put( dummy, dummy );
+ identityMap.put(dummy, dummy);
}
- PriorityQueue queue = new PriorityQueue(2*identityMap.size(), new TagValueSet.FrequencyComparator());
+ PriorityQueue queue = new PriorityQueue(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
queue.addAll(identityMap.values());
- while (queue.size() > 1)
- {
+ while (queue.size() > 1) {
TagValueSet node = new TagValueSet(nextTagValueSetId++);
node.child1 = queue.poll();
node.child2 = queue.poll();
node.frequency = node.child1.frequency + node.child2.frequency;
- queue.add( node );
+ queue.add(node);
}
TagValueSet root = queue.poll();
- root.encode( bc, 1, 0 );
+ root.encode(bc, 1, 0);
}
this.bc = bc;
}
- public TagValueCoder( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
- {
- tree = decodeTree( bc, buffers, validator );
+ public TagValueCoder(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
+ tree = decodeTree(bc, buffers, validator);
this.bc = bc;
}
- public TagValueCoder()
- {
+ public TagValueCoder() {
identityMap = new HashMap();
}
- private Object decodeTree( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
- {
+ private Object decodeTree(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
boolean isNode = bc.decodeBit();
- if ( isNode )
- {
+ if (isNode) {
TreeNode node = new TreeNode();
- node.child1 = decodeTree( bc, buffers, validator );
- node.child2 = decodeTree( bc, buffers, validator );
+ node.child1 = decodeTree(bc, buffers, validator);
+ node.child2 = decodeTree(bc, buffers, validator);
return node;
}
byte[] buffer = buffers.tagbuf1;
- BitCoderContext ctx = buffers.bctx1;
- ctx.reset( buffer );
+ BitCoderContext ctx = buffers.bctx1;
+ ctx.reset(buffer);
int inum = 0;
int lastEncodedInum = 0;
boolean hasdata = false;
- for ( ;; )
- {
+ for (; ; ) {
int delta = bc.decodeVarBits();
- if ( !hasdata )
- {
- if ( delta == 0 )
- {
+ if (!hasdata) {
+ if (delta == 0) {
return null;
}
}
- if ( delta == 0 )
- {
- ctx.encodeVarBits( 0 );
+ if (delta == 0) {
+ ctx.encodeVarBits(0);
break;
}
inum += delta;
int data = bc.decodeVarBits();
- if ( validator == null || validator.isLookupIdxUsed( inum ) )
- {
+ if (validator == null || validator.isLookupIdxUsed(inum)) {
hasdata = true;
- ctx.encodeVarBits( inum - lastEncodedInum );
- ctx.encodeVarBits( data );
+ ctx.encodeVarBits(inum - lastEncodedInum);
+ ctx.encodeVarBits(data);
lastEncodedInum = inum;
}
}
byte[] res;
int len = ctx.closeAndGetEncodedLength();
- if ( validator == null )
- {
+ if (validator == null) {
res = new byte[len];
- System.arraycopy( buffer, 0, res, 0, len );
- }
- else
- {
- res = validator.unify( buffer, 0, len );
+ System.arraycopy(buffer, 0, res, 0, len);
+ } else {
+ res = validator.unify(buffer, 0, len);
}
- int accessType = validator == null ? 2 : validator.accessType( res );
- if ( accessType > 0 )
- {
+ int accessType = validator == null ? 2 : validator.accessType(res);
+ if (accessType > 0) {
TagValueWrapper w = new TagValueWrapper();
w.data = res;
w.accessType = accessType;
@@ -166,14 +140,12 @@ public final class TagValueCoder
return null;
}
- public static final class TreeNode
- {
+ public static final class TreeNode {
public Object child1;
public Object child2;
}
- public static final class TagValueSet
- {
+ public static final class TagValueSet {
public byte[] data;
public int frequency;
public int code;
@@ -182,66 +154,51 @@ public final class TagValueCoder
public TagValueSet child2;
private int id; // serial number to make the comparator well defined in case of equal frequencies
- public TagValueSet( int id )
- {
+ public TagValueSet(int id) {
this.id = id;
}
- public void encode( BitCoderContext bc, int range, int code )
- {
+ public void encode(BitCoderContext bc, int range, int code) {
this.range = range;
this.code = code;
boolean isNode = child1 != null;
- bc.encodeBit( isNode );
- if ( isNode )
- {
- child1.encode( bc, range << 1, code );
- child2.encode( bc, range << 1, code + range );
- }
- else
- {
- if ( data == null )
- {
- bc.encodeVarBits( 0 );
+ bc.encodeBit(isNode);
+ if (isNode) {
+ child1.encode(bc, range << 1, code);
+ child2.encode(bc, range << 1, code + range);
+ } else {
+ if (data == null) {
+ bc.encodeVarBits(0);
return;
}
- BitCoderContext src = new BitCoderContext( data );
- for ( ;; )
- {
+ BitCoderContext src = new BitCoderContext(data);
+ for (; ; ) {
int delta = src.decodeVarBits();
- bc.encodeVarBits( delta );
- if ( delta == 0 )
- {
+ bc.encodeVarBits(delta);
+ if (delta == 0) {
break;
}
int data = src.decodeVarBits();
- bc.encodeVarBits( data );
+ bc.encodeVarBits(data);
}
}
}
@Override
- public boolean equals( Object o )
- {
- if ( o instanceof TagValueSet )
- {
+ public boolean equals(Object o) {
+ if (o instanceof TagValueSet) {
TagValueSet tvs = (TagValueSet) o;
- if ( data == null )
- {
+ if (data == null) {
return tvs.data == null;
}
- if ( tvs.data == null )
- {
+ if (tvs.data == null) {
return data == null;
}
- if ( data.length != tvs.data.length )
- {
+ if (data.length != tvs.data.length) {
return false;
}
- for ( int i = 0; i < data.length; i++ )
- {
- if ( data[i] != tvs.data[i] )
- {
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] != tvs.data[i]) {
return false;
}
}
@@ -251,39 +208,34 @@ public final class TagValueCoder
}
@Override
- public int hashCode()
- {
- if ( data == null )
- {
+ public int hashCode() {
+ if (data == null) {
return 0;
}
int h = 17;
- for ( int i = 0; i < data.length; i++ )
- {
- h = ( h << 8 ) + data[i];
+ for (int i = 0; i < data.length; i++) {
+ h = (h << 8) + data[i];
}
return h;
}
- public static class FrequencyComparator implements Comparator
- {
+ public static class FrequencyComparator implements Comparator {
@Override
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
- if ( tvs1.frequency < tvs2.frequency )
+ if (tvs1.frequency < tvs2.frequency)
return -1;
- if ( tvs1.frequency > tvs2.frequency )
+ if (tvs1.frequency > tvs2.frequency)
return 1;
// to avoid ordering instability, decide on the id if frequency is equal
- if ( tvs1.id < tvs2.id )
+ if (tvs1.id < tvs2.id)
return -1;
- if ( tvs1.id > tvs2.id )
+ if (tvs1.id > tvs2.id)
return 1;
- if ( tvs1 != tvs2 )
- {
- throw new RuntimeException( "identity corruption!" );
+ if (tvs1 != tvs2) {
+ throw new RuntimeException("identity corruption!");
}
return 0;
}
diff --git a/brouter-codec/src/main/java/btools/codec/TagValueValidator.java b/brouter-codec/src/main/java/btools/codec/TagValueValidator.java
index 6cfb54c..4b88dfc 100644
--- a/brouter-codec/src/main/java/btools/codec/TagValueValidator.java
+++ b/brouter-codec/src/main/java/btools/codec/TagValueValidator.java
@@ -1,17 +1,16 @@
package btools.codec;
-public interface TagValueValidator
-{
+public interface TagValueValidator {
/**
* @param tagValueSet the way description to check
* @return 0 = nothing, 1=no matching, 2=normal
*/
- public int accessType( byte[] tagValueSet );
+ public int accessType(byte[] tagValueSet);
- public byte[] unify( byte[] tagValueSet, int offset, int len );
+ public byte[] unify(byte[] tagValueSet, int offset, int len);
- public boolean isLookupIdxUsed( int idx );
+ public boolean isLookupIdxUsed(int idx);
- public void setDecodeForbidden( boolean decodeForbidden );
+ public void setDecodeForbidden(boolean decodeForbidden);
}
diff --git a/brouter-codec/src/main/java/btools/codec/TagValueWrapper.java b/brouter-codec/src/main/java/btools/codec/TagValueWrapper.java
index 4839922..2ee4659 100644
--- a/brouter-codec/src/main/java/btools/codec/TagValueWrapper.java
+++ b/brouter-codec/src/main/java/btools/codec/TagValueWrapper.java
@@ -5,8 +5,7 @@ package btools.codec;
* TagValueWrapper wrapps a description bitmap
* to add the access-type
*/
-public final class TagValueWrapper
-{
+public final class TagValueWrapper {
public byte[] data;
public int accessType;
}
diff --git a/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java b/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java
index f7770e0..c086124 100644
--- a/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java
+++ b/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java
@@ -5,9 +5,10 @@ package btools.codec;
* from the decoder to find the closest
* matches to the waypoints
*/
-public interface WaypointMatcher
-{
- boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget );
- void transferNode( int ilon, int ilat );
+public interface WaypointMatcher {
+ boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget);
+
+ void transferNode(int ilon, int ilat);
+
void end();
}
diff --git a/brouter-codec/src/test/java/btools/codec/LinkedListContainerTest.java b/brouter-codec/src/test/java/btools/codec/LinkedListContainerTest.java
index 8f8f623..1e221ea 100644
--- a/brouter-codec/src/test/java/btools/codec/LinkedListContainerTest.java
+++ b/brouter-codec/src/test/java/btools/codec/LinkedListContainerTest.java
@@ -3,50 +3,39 @@ package btools.codec;
import org.junit.Assert;
import org.junit.Test;
-public class LinkedListContainerTest
-{
+public class LinkedListContainerTest {
@Test
- public void linkedListTest1()
- {
+ public void linkedListTest1() {
int nlists = 553;
- LinkedListContainer llc = new LinkedListContainer( nlists, null );
+ LinkedListContainer llc = new LinkedListContainer(nlists, null);
- for ( int ln = 0; ln < nlists; ln++ )
- {
- for ( int i = 0; i < 10; i++ )
- {
- llc.addDataElement( ln, ln * i );
+ for (int ln = 0; ln < nlists; ln++) {
+ for (int i = 0; i < 10; i++) {
+ llc.addDataElement(ln, ln * i);
}
}
- for ( int i = 0; i < 10; i++ )
- {
- for ( int ln = 0; ln < nlists; ln++ )
- {
- llc.addDataElement( ln, ln * i );
+ for (int i = 0; i < 10; i++) {
+ for (int ln = 0; ln < nlists; ln++) {
+ llc.addDataElement(ln, ln * i);
}
}
- for ( int ln = 0; ln < nlists; ln++ )
- {
- int cnt = llc.initList( ln );
- Assert.assertTrue( "list size test", cnt == 20 );
+ for (int ln = 0; ln < nlists; ln++) {
+ int cnt = llc.initList(ln);
+ Assert.assertTrue("list size test", cnt == 20);
- for ( int i = 19; i >= 0; i-- )
- {
+ for (int i = 19; i >= 0; i--) {
int data = llc.getDataElement();
- Assert.assertTrue( "data value test", data == ln * ( i % 10 ) );
+ Assert.assertTrue("data value test", data == ln * (i % 10));
}
}
- try
- {
+ try {
llc.getDataElement();
- Assert.fail( "no more elements expected" );
- }
- catch (IllegalArgumentException e)
- {
+ Assert.fail("no more elements expected");
+ } catch (IllegalArgumentException e) {
}
}
}
diff --git a/brouter-codec/src/test/java/btools/codec/StatCoderContextTest.java b/brouter-codec/src/test/java/btools/codec/StatCoderContextTest.java
index b24253b..184c69c 100644
--- a/brouter-codec/src/test/java/btools/codec/StatCoderContextTest.java
+++ b/brouter-codec/src/test/java/btools/codec/StatCoderContextTest.java
@@ -6,100 +6,79 @@ import java.util.Random;
import org.junit.Assert;
import org.junit.Test;
-public class StatCoderContextTest
-{
+public class StatCoderContextTest {
@Test
- public void noisyVarBitsEncodeDecodeTest()
- {
+ public void noisyVarBitsEncodeDecodeTest() {
byte[] ab = new byte[40000];
- StatCoderContext ctx = new StatCoderContext( ab );
- for ( int noisybits = 1; noisybits < 12; noisybits++ )
- {
- for ( int i = 0; i < 1000; i++ )
- {
- ctx.encodeNoisyNumber( i, noisybits );
+ StatCoderContext ctx = new StatCoderContext(ab);
+ for (int noisybits = 1; noisybits < 12; noisybits++) {
+ for (int i = 0; i < 1000; i++) {
+ ctx.encodeNoisyNumber(i, noisybits);
}
}
ctx.closeAndGetEncodedLength();
- ctx = new StatCoderContext( ab );
+ ctx = new StatCoderContext(ab);
- for ( int noisybits = 1; noisybits < 12; noisybits++ )
- {
- for ( int i = 0; i < 1000; i++ )
- {
- int value = ctx.decodeNoisyNumber( noisybits );
- if ( value != i )
- {
- Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
+ for (int noisybits = 1; noisybits < 12; noisybits++) {
+ for (int i = 0; i < 1000; i++) {
+ int value = ctx.decodeNoisyNumber(noisybits);
+ if (value != i) {
+ Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
}
}
}
}
@Test
- public void noisySignedVarBitsEncodeDecodeTest()
- {
+ public void noisySignedVarBitsEncodeDecodeTest() {
byte[] ab = new byte[80000];
- StatCoderContext ctx = new StatCoderContext( ab );
- for ( int noisybits = 0; noisybits < 12; noisybits++ )
- {
- for ( int i = -1000; i < 1000; i++ )
- {
- ctx.encodeNoisyDiff( i, noisybits );
+ StatCoderContext ctx = new StatCoderContext(ab);
+ for (int noisybits = 0; noisybits < 12; noisybits++) {
+ for (int i = -1000; i < 1000; i++) {
+ ctx.encodeNoisyDiff(i, noisybits);
}
}
ctx.closeAndGetEncodedLength();
- ctx = new StatCoderContext( ab );
+ ctx = new StatCoderContext(ab);
- for ( int noisybits = 0; noisybits < 12; noisybits++ )
- {
- for ( int i = -1000; i < 1000; i++ )
- {
- int value = ctx.decodeNoisyDiff( noisybits );
- if ( value != i )
- {
- Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
+ for (int noisybits = 0; noisybits < 12; noisybits++) {
+ for (int i = -1000; i < 1000; i++) {
+ int value = ctx.decodeNoisyDiff(noisybits);
+ if (value != i) {
+ Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
}
}
}
}
@Test
- public void predictedValueEncodeDecodeTest()
- {
+ public void predictedValueEncodeDecodeTest() {
byte[] ab = new byte[80000];
- StatCoderContext ctx = new StatCoderContext( ab );
- for ( int value = -100; value < 100; value += 5 )
- {
- for ( int predictor = -200; predictor < 200; predictor += 7 )
- {
- ctx.encodePredictedValue( value, predictor );
+ StatCoderContext ctx = new StatCoderContext(ab);
+ for (int value = -100; value < 100; value += 5) {
+ for (int predictor = -200; predictor < 200; predictor += 7) {
+ ctx.encodePredictedValue(value, predictor);
}
}
ctx.closeAndGetEncodedLength();
- ctx = new StatCoderContext( ab );
+ ctx = new StatCoderContext(ab);
- for ( int value = -100; value < 100; value += 5 )
- {
- for ( int predictor = -200; predictor < 200; predictor += 7 )
- {
- int decodedValue = ctx.decodePredictedValue( predictor );
- if ( value != decodedValue )
- {
- Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
+ for (int value = -100; value < 100; value += 5) {
+ for (int predictor = -200; predictor < 200; predictor += 7) {
+ int decodedValue = ctx.decodePredictedValue(predictor);
+ if (value != decodedValue) {
+ Assert.fail("value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue);
}
}
}
}
@Test
- public void sortedArrayEncodeDecodeTest()
- {
+ public void sortedArrayEncodeDecodeTest() {
Random rand = new Random();
int size = 1000000;
int[] values = new int[size];
- for ( int i = 0; i < size; i++ )
- {
+ for (int i = 0; i < size; i++) {
values[i] = rand.nextInt() & 0x0fffffff;
}
values[5] = 175384; // force collision
@@ -108,23 +87,21 @@ public class StatCoderContextTest
values[15] = 275384; // force neighbours
values[18] = 275385;
- Arrays.sort( values );
+ Arrays.sort(values);
byte[] ab = new byte[3000000];
- StatCoderContext ctx = new StatCoderContext( ab );
- ctx.encodeSortedArray( values, 0, size, 0x08000000, 0 );
+ StatCoderContext ctx = new StatCoderContext(ab);
+ ctx.encodeSortedArray(values, 0, size, 0x08000000, 0);
ctx.closeAndGetEncodedLength();
- ctx = new StatCoderContext( ab );
+ ctx = new StatCoderContext(ab);
int[] decodedValues = new int[size];
- ctx.decodeSortedArray( decodedValues, 0, size, 27, 0 );
+ ctx.decodeSortedArray(decodedValues, 0, size, 27, 0);
- for ( int i = 0; i < size; i++ )
- {
- if ( values[i] != decodedValues[i] )
- {
- Assert.fail( "mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i] );
+ for (int i = 0; i < size; i++) {
+ if (values[i] != decodedValues[i]) {
+ Assert.fail("mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i]);
}
}
}
diff --git a/brouter-core/src/main/java/btools/router/KinematicModel.java b/brouter-core/src/main/java/btools/router/KinematicModel.java
index 0df2621..b6b9b9c 100644
--- a/brouter-core/src/main/java/btools/router/KinematicModel.java
+++ b/brouter-core/src/main/java/btools/router/KinematicModel.java
@@ -11,15 +11,12 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
-final class KinematicModel extends OsmPathModel
-{
- public OsmPrePath createPrePath()
- {
+final class KinematicModel extends OsmPathModel {
+ public OsmPrePath createPrePath() {
return new KinematicPrePath();
}
- public OsmPath createPath()
- {
+ public OsmPath createPath() {
return new KinematicPath();
}
@@ -38,7 +35,7 @@ final class KinematicModel extends OsmPathModel
// derived values
public double pw; // balance power
public double cost0; // minimum possible cost per meter
-
+
private int wayIdxMaxspeed;
private int wayIdxMaxspeedExplicit;
private int wayIdxMinspeed;
@@ -47,7 +44,7 @@ final class KinematicModel extends OsmPathModel
protected BExpressionContextWay ctxWay;
protected BExpressionContextNode ctxNode;
- protected Map params;
+ protected Map params;
private boolean initDone = false;
@@ -55,77 +52,67 @@ final class KinematicModel extends OsmPathModel
private double lastBreakingSpeed;
@Override
- public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map extraParams )
- {
- if ( !initDone )
- {
+ public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map extraParams) {
+ if (!initDone) {
ctxWay = expctxWay;
ctxNode = expctxNode;
- wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
- wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex( "maxspeed_explicit", false );
- wayIdxMinspeed = ctxWay.getOutputVariableIndex( "minspeed", false );
- nodeIdxMaxspeed = ctxNode.getOutputVariableIndex( "maxspeed", false );
+ wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
+ wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex("maxspeed_explicit", false);
+ wayIdxMinspeed = ctxWay.getOutputVariableIndex("minspeed", false);
+ nodeIdxMaxspeed = ctxNode.getOutputVariableIndex("maxspeed", false);
initDone = true;
}
params = extraParams;
-
- turnAngleDecayTime = getParam( "turnAngleDecayTime", 5.f );
- f_roll = getParam( "f_roll", 232.f );
- f_air = getParam( "f_air", 0.4f );
- f_recup = getParam( "f_recup", 400.f );
- p_standby = getParam( "p_standby", 250.f );
- outside_temp = getParam( "outside_temp", 20.f );
- recup_efficiency = getParam( "recup_efficiency", 0.7f );
- totalweight = getParam( "totalweight", 1640.f );
- vmax = getParam( "vmax", 80.f ) / 3.6;
- leftWaySpeed = getParam( "leftWaySpeed", 12.f ) / 3.6;
- rightWaySpeed = getParam( "rightWaySpeed", 12.f ) / 3.6;
+
+ turnAngleDecayTime = getParam("turnAngleDecayTime", 5.f);
+ f_roll = getParam("f_roll", 232.f);
+ f_air = getParam("f_air", 0.4f);
+ f_recup = getParam("f_recup", 400.f);
+ p_standby = getParam("p_standby", 250.f);
+ outside_temp = getParam("outside_temp", 20.f);
+ recup_efficiency = getParam("recup_efficiency", 0.7f);
+ totalweight = getParam("totalweight", 1640.f);
+ vmax = getParam("vmax", 80.f) / 3.6;
+ leftWaySpeed = getParam("leftWaySpeed", 12.f) / 3.6;
+ rightWaySpeed = getParam("rightWaySpeed", 12.f) / 3.6;
pw = 2. * f_air * vmax * vmax * vmax - p_standby;
- cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
+ cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
}
- protected float getParam( String name, float defaultValue )
- {
- String sval = params == null ? null : params.get( name );
- if ( sval != null )
- {
- return Float.parseFloat( sval );
+ protected float getParam(String name, float defaultValue) {
+ String sval = params == null ? null : params.get(name);
+ if (sval != null) {
+ return Float.parseFloat(sval);
}
- float v = ctxWay.getVariableValue( name, defaultValue );
- if ( params != null )
- {
- params.put( name, "" + v );
+ float v = ctxWay.getVariableValue(name, defaultValue);
+ if (params != null) {
+ params.put(name, "" + v);
}
return v;
}
-
- public float getWayMaxspeed()
- {
- return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
+
+ public float getWayMaxspeed() {
+ return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
}
- public float getWayMaxspeedExplicit()
- {
- return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
+ public float getWayMaxspeedExplicit() {
+ return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
}
- public float getWayMinspeed()
- {
- return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
+ public float getWayMinspeed() {
+ return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
}
- public float getNodeMaxspeed()
- {
- return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
+ public float getNodeMaxspeed() {
+ return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
}
- /**
- * get the effective speed limit from the way-limit and vmax/vmin
- */
- public double getEffectiveSpeedLimit( )
- {
+ /**
+ * get the effective speed limit from the way-limit and vmax/vmin
+ */
+ public double getEffectiveSpeedLimit() {
// performance related inline coding
double minspeed = getWayMinspeed();
double espeed = minspeed > vmax ? minspeed : vmax;
@@ -133,30 +120,27 @@ final class KinematicModel extends OsmPathModel
return maxspeed < espeed ? maxspeed : espeed;
}
- /**
- * get the breaking speed for current balance-power (pw) and effective speed limit (vl)
- */
- public double getBreakingSpeed( double vl )
- {
- if ( vl == lastEffectiveLimit )
- {
+ /**
+ * get the breaking speed for current balance-power (pw) and effective speed limit (vl)
+ */
+ public double getBreakingSpeed(double vl) {
+ if (vl == lastEffectiveLimit) {
return lastBreakingSpeed;
}
- double v = vl*0.8;
- double pw2 = pw+p_standby;
+ double v = vl * 0.8;
+ double pw2 = pw + p_standby;
double e = recup_efficiency;
- double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
- for(int i=0;i<5;i++)
- {
- double v2 = v*v;
- double x = pw2/v+f_air*e*v2 - x0;
- double dx = 2.*e*f_air*v - pw2/v2;
- v -= x/dx;
+ double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
+ for (int i = 0; i < 5; i++) {
+ double v2 = v * v;
+ double x = pw2 / v + f_air * e * v2 - x0;
+ double dx = 2. * e * f_air * v - pw2 / v2;
+ v -= x / dx;
}
lastEffectiveLimit = vl;
lastBreakingSpeed = v;
-
+
return v;
}
diff --git a/brouter-core/src/main/java/btools/router/KinematicPath.java b/brouter-core/src/main/java/btools/router/KinematicPath.java
index 54a88e2..bcb4318 100644
--- a/brouter-core/src/main/java/btools/router/KinematicPath.java
+++ b/brouter-core/src/main/java/btools/router/KinematicPath.java
@@ -8,8 +8,7 @@ package btools.router;
import btools.util.FastMath;
-final class KinematicPath extends OsmPath
-{
+final class KinematicPath extends OsmPath {
private double ekin; // kinetic energy (Joule)
private double totalTime; // travel time (seconds)
private double totalEnergy; // total route energy (Joule)
@@ -17,9 +16,8 @@ final class KinematicPath extends OsmPath
private float floatingAngleRight; // sliding average right bend (degree)
@Override
- protected void init( OsmPath orig )
- {
- KinematicPath origin = (KinematicPath)orig;
+ protected void init(OsmPath orig) {
+ KinematicPath origin = (KinematicPath) orig;
ekin = origin.ekin;
totalTime = origin.totalTime;
totalEnergy = origin.totalEnergy;
@@ -29,8 +27,7 @@ final class KinematicPath extends OsmPath
}
@Override
- protected void resetState()
- {
+ protected void resetState() {
ekin = 0.;
totalTime = 0.;
totalEnergy = 0.;
@@ -39,267 +36,237 @@ final class KinematicPath extends OsmPath
}
@Override
- protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
- {
- KinematicModel km = (KinematicModel)rc.pm;
+ protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
+ KinematicModel km = (KinematicModel) rc.pm;
double cost = 0.;
double extraTime = 0.;
- if ( isStartpoint )
- {
+ if (isStartpoint) {
// for forward direction, we start with target speed
- if ( !rc.inverseDirection )
- {
- extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
+ if (!rc.inverseDirection) {
+ extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
}
- }
- else
- {
+ } else {
double turnspeed = 999.; // just high
- if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
+ if (km.turnAngleDecayTime != 0.) // process turn-angle slowdown
{
- if ( angle < 0 ) floatingAngleLeft -= (float)angle;
- else floatingAngleRight += (float)angle;
- float aa = Math.max( floatingAngleLeft, floatingAngleRight );
+ if (angle < 0) floatingAngleLeft -= (float) angle;
+ else floatingAngleRight += (float) angle;
+ float aa = Math.max(floatingAngleLeft, floatingAngleRight);
- double curveSpeed = aa > 10. ? 200. / aa : 20.;
+ double curveSpeed = aa > 10. ? 200. / aa : 20.;
double distanceTime = dist / curveSpeed;
- double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
- floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
- floatingAngleRight = (float)( floatingAngleRight * decayFactor );
+ double decayFactor = FastMath.exp(-distanceTime / km.turnAngleDecayTime);
+ floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
+ floatingAngleRight = (float) (floatingAngleRight * decayFactor);
- if ( curveSpeed < 20. )
- {
+ if (curveSpeed < 20.) {
turnspeed = curveSpeed;
}
}
- if ( nsection == 0 ) // process slowdown by crossing geometry
+ if (nsection == 0) // process slowdown by crossing geometry
{
double junctionspeed = 999.; // just high
- int classifiermask = (int)rc.expctxWay.getClassifierMask();
+ int classifiermask = (int) rc.expctxWay.getClassifierMask();
// penalty for equal priority crossing
boolean hasLeftWay = false;
boolean hasRightWay = false;
boolean hasResidential = false;
- for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
- {
- KinematicPrePath pp = (KinematicPrePath)prePath;
+ for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
+ KinematicPrePath pp = (KinematicPrePath) prePath;
- if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
+ if (((pp.classifiermask ^ classifiermask) & 8) != 0) // exactly one is linktype
{
continue;
}
- if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
+ if ((pp.classifiermask & 32) != 0) // touching a residential?
{
hasResidential = true;
}
- if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
- {
+ if (pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20) {
double diff = pp.angle - angle;
- if ( diff < -40. && diff > -140.) hasLeftWay = true;
- if ( diff > 40. && diff < 140. ) hasRightWay = true;
+ if (diff < -40. && diff > -140.) hasLeftWay = true;
+ if (diff > 40. && diff < 140.) hasRightWay = true;
}
}
double residentialSpeed = 13.;
- if ( hasLeftWay && junctionspeed > km.leftWaySpeed ) junctionspeed = km.leftWaySpeed;
- if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
- if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
+ if (hasLeftWay && junctionspeed > km.leftWaySpeed) junctionspeed = km.leftWaySpeed;
+ if (hasRightWay && junctionspeed > km.rightWaySpeed) junctionspeed = km.rightWaySpeed;
+ if (hasResidential && junctionspeed > residentialSpeed) junctionspeed = residentialSpeed;
- if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
- {
+ if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
extraTime += 10.;
junctionspeed = 0; // full stop for entering or leaving road network
}
- if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
- {
+ if (lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0) {
extraTime += 2.; // two seconds for entering a link-type
}
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
- if ( message != null )
- {
- message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
+ if (message != null) {
+ message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
}
}
- cutEkin( km.totalweight, turnspeed ); // apply turnspeed
+ cutEkin(km.totalweight, turnspeed); // apply turnspeed
}
// linear temperature correction
- double tcorr = (20.-km.outside_temp)*0.0035;
+ double tcorr = (20. - km.outside_temp) * 0.0035;
// air_pressure down 1mb/8m
double ecorr = 0.0001375 * (elevation - 100.);
- double f_air = km.f_air * ( 1. + tcorr - ecorr );
+ double f_air = km.f_air * (1. + tcorr - ecorr);
- double distanceCost = evolveDistance( km, dist, delta_h, f_air );
+ double distanceCost = evolveDistance(km, dist, delta_h, f_air);
- if ( message != null )
- {
- message.costfactor = (float)(distanceCost/dist);
- message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
- message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
- message.vmin = (int) ( km.getWayMinspeed() * 3.6 + 0.5 );
- message.extraTime = (int)(extraTime*1000);
+ if (message != null) {
+ message.costfactor = (float) (distanceCost / dist);
+ message.vmax = (int) (km.getWayMaxspeed() * 3.6 + 0.5);
+ message.vmaxExplicit = (int) (km.getWayMaxspeedExplicit() * 3.6 + 0.5);
+ message.vmin = (int) (km.getWayMinspeed() * 3.6 + 0.5);
+ message.extraTime = (int) (extraTime * 1000);
}
- cost += extraTime * km.pw / km.cost0;
+ cost += extraTime * km.pw / km.cost0;
totalTime += extraTime;
return cost + distanceCost;
}
- protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
- {
+ protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
// elevation force
double fh = delta_h * km.totalweight * 9.81 / dist;
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
- double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
- if ( emax <= 0. )
- {
+ double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
+ if (emax <= 0.) {
return -1.;
}
- double vb = km.getBreakingSpeed( effectiveSpeedLimit );
- double elow = 0.5*km.totalweight*vb*vb;
+ double vb = km.getBreakingSpeed(effectiveSpeedLimit);
+ double elow = 0.5 * km.totalweight * vb * vb;
double elapsedTime = 0.;
double dissipatedEnergy = 0.;
- double v = Math.sqrt( 2. * ekin / km.totalweight );
+ double v = Math.sqrt(2. * ekin / km.totalweight);
double d = dist;
- while( d > 0. )
- {
+ while (d > 0.) {
boolean slow = ekin < elow;
boolean fast = ekin >= emax;
double etarget = slow ? elow : emax;
- double f = km.f_roll + f_air*v*v + fh;
- double f_recup = Math.max( 0., fast ? -f : (slow ? km.f_recup :0 ) -fh ); // additional recup for slow part
+ double f = km.f_roll + f_air * v * v + fh;
+ double f_recup = Math.max(0., fast ? -f : (slow ? km.f_recup : 0) - fh); // additional recup for slow part
f += f_recup;
double delta_ekin;
double timeStep;
double x;
- if ( fast )
- {
+ if (fast) {
x = d;
- delta_ekin = x*f;
- timeStep = x/v;
+ delta_ekin = x * f;
+ timeStep = x / v;
ekin = etarget;
- }
- else
- {
- delta_ekin = etarget-ekin;
- double b = 2.*f_air / km.totalweight;
- double x0 = delta_ekin/f;
- double x0b = x0*b;
- x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
- double maxstep = Math.min( 50., d );
- if ( x >= maxstep )
- {
+ } else {
+ delta_ekin = etarget - ekin;
+ double b = 2. * f_air / km.totalweight;
+ double x0 = delta_ekin / f;
+ double x0b = x0 * b;
+ x = x0 * (1. - x0b * (0.5 + x0b * (0.333333333 - x0b * 0.25))); // = ln( delta_ekin*b/f + 1.) / b;
+ double maxstep = Math.min(50., d);
+ if (x >= maxstep) {
x = maxstep;
- double xb = x*b;
- delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
+ double xb = x * b;
+ delta_ekin = x * f * (1. + xb * (0.5 + xb * (0.166666667 + xb * 0.0416666667))); // = f/b* exp(xb-1)
ekin += delta_ekin;
- }
- else
- {
+ } else {
ekin = etarget;
}
- double v2 = Math.sqrt( 2. * ekin / km.totalweight );
+ double v2 = Math.sqrt(2. * ekin / km.totalweight);
double a = f / km.totalweight; // TODO: average force?
- timeStep = (v2-v)/a;
+ timeStep = (v2 - v) / a;
v = v2;
}
d -= x;
elapsedTime += timeStep;
// dissipated energy does not contain elevation and efficient recup
- dissipatedEnergy += delta_ekin - x*(fh + f_recup*km.recup_efficiency);
+ dissipatedEnergy += delta_ekin - x * (fh + f_recup * km.recup_efficiency);
// correction: inefficient recup going into heating is half efficient
- double ieRecup = x*f_recup*(1.-km.recup_efficiency);
- double eaux = timeStep*km.p_standby;
- dissipatedEnergy -= Math.max( ieRecup, eaux ) * 0.5;
+ double ieRecup = x * f_recup * (1. - km.recup_efficiency);
+ double eaux = timeStep * km.p_standby;
+ dissipatedEnergy -= Math.max(ieRecup, eaux) * 0.5;
}
dissipatedEnergy += elapsedTime * km.p_standby;
totalTime += elapsedTime;
- totalEnergy += dissipatedEnergy + dist*fh;
+ totalEnergy += dissipatedEnergy + dist * fh;
- return (km.pw * elapsedTime + dissipatedEnergy)/km.cost0; // =cost
+ return (km.pw * elapsedTime + dissipatedEnergy) / km.cost0; // =cost
}
@Override
- protected double processTargetNode( RoutingContext rc )
- {
- KinematicModel km = (KinematicModel)rc.pm;
+ protected double processTargetNode(RoutingContext rc) {
+ KinematicModel km = (KinematicModel) rc.pm;
// finally add node-costs for target node
- if ( targetNode.nodeDescription != null )
- {
- rc.expctxNode.evaluate( false , targetNode.nodeDescription );
+ if (targetNode.nodeDescription != null) {
+ rc.expctxNode.evaluate(false, targetNode.nodeDescription);
float initialcost = rc.expctxNode.getInitialcost();
- if ( initialcost >= 1000000. )
- {
+ if (initialcost >= 1000000.) {
return -1.;
}
- cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
+ cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
- if ( message != null )
- {
- message.linknodecost += (int)initialcost;
- message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
+ if (message != null) {
+ message.linknodecost += (int) initialcost;
+ message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
- message.vnode1 = (int) ( km.getNodeMaxspeed() * 3.6 + 0.5 );
+ message.vnode1 = (int) (km.getNodeMaxspeed() * 3.6 + 0.5);
}
return initialcost;
}
return 0.;
}
- private void cutEkin( double weight, double speed )
- {
- double e = 0.5*weight*speed*speed;
- if ( ekin > e ) ekin = e;
+ private void cutEkin(double weight, double speed) {
+ double e = 0.5 * weight * speed * speed;
+ if (ekin > e) ekin = e;
}
@Override
- public int elevationCorrection( RoutingContext rc )
- {
+ public int elevationCorrection(RoutingContext rc) {
return 0;
}
@Override
- public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
- {
- KinematicPath p = (KinematicPath)path;
+ public boolean definitlyWorseThan(OsmPath path, RoutingContext rc) {
+ KinematicPath p = (KinematicPath) path;
- int c = p.cost;
- return cost > c + 100;
+ int c = p.cost;
+ return cost > c + 100;
}
@Override
- public double getTotalTime()
- {
+ public double getTotalTime() {
return totalTime;
}
@Override
- public double getTotalEnergy()
- {
+ public double getTotalEnergy() {
return totalEnergy;
}
}
diff --git a/brouter-core/src/main/java/btools/router/KinematicPrePath.java b/brouter-core/src/main/java/btools/router/KinematicPrePath.java
index 7d3efc2..c5bdc45 100644
--- a/brouter-core/src/main/java/btools/router/KinematicPrePath.java
+++ b/brouter-core/src/main/java/btools/router/KinematicPrePath.java
@@ -8,16 +8,14 @@ package btools.router;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
-final class KinematicPrePath extends OsmPrePath
-{
+final class KinematicPrePath extends OsmPrePath {
public double angle;
public int priorityclassifier;
public int classifiermask;
- protected void initPrePath(OsmPath origin, RoutingContext rc )
- {
+ protected void initPrePath(OsmPath origin, RoutingContext rc) {
byte[] description = link.descriptionBitmap;
- if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
+ if (description == null) throw new IllegalArgumentException("null description for: " + link);
// extract the 3 positions of the first section
int lon0 = origin.originLon;
@@ -27,32 +25,29 @@ final class KinematicPrePath extends OsmPrePath
int lon1 = p1.getILon();
int lat1 = p1.getILat();
- boolean isReverse = link.isReverse( sourceNode );
+ boolean isReverse = link.isReverse(sourceNode);
// evaluate the way tags
- rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
+ rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
OsmTransferNode transferNode = link.geometry == null ? null
- : rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
+ : rc.geometryDecoder.decodeGeometry(link.geometry, p1, targetNode, isReverse);
int lon2;
int lat2;
- if ( transferNode == null )
- {
+ if (transferNode == null) {
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
- }
- else
- {
+ } else {
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
}
- int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
+ int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
- angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
- priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
- classifiermask = (int)rc.expctxWay.getClassifierMask();
+ angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
+ priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
+ classifiermask = (int) rc.expctxWay.getClassifierMask();
}
}
diff --git a/brouter-core/src/main/java/btools/router/MessageData.java b/brouter-core/src/main/java/btools/router/MessageData.java
index 3e3b6bb..0fde0f8 100644
--- a/brouter-core/src/main/java/btools/router/MessageData.java
+++ b/brouter-core/src/main/java/btools/router/MessageData.java
@@ -6,15 +6,13 @@
package btools.router;
-
-final class MessageData implements Cloneable
-{
+final class MessageData implements Cloneable {
int linkdist = 0;
int linkelevationcost = 0;
int linkturncost = 0;
int linknodecost = 0;
int linkinitcost = 0;
-
+
float costfactor;
int priorityclassifier;
int classifiermask;
@@ -25,7 +23,7 @@ final class MessageData implements Cloneable
int lon;
int lat;
short ele;
-
+
float time;
float energy;
@@ -37,84 +35,70 @@ final class MessageData implements Cloneable
int vnode1 = 999;
int extraTime = 0;
- String toMessage()
- {
- if ( wayKeyValues == null )
- {
+ String toMessage() {
+ if (wayKeyValues == null) {
return null;
}
-
- int iCost = (int)(costfactor*1000 + 0.5f);
- return (lon-180000000) + "\t"
- + (lat-90000000) + "\t"
- + ele/4 + "\t"
- + linkdist + "\t"
- + iCost + "\t"
- + linkelevationcost
- + "\t" + linkturncost
- + "\t" + linknodecost
- + "\t" + linkinitcost
- + "\t" + wayKeyValues
- + "\t" + ( nodeKeyValues == null ? "" : nodeKeyValues )
- + "\t" + ((int)time)
- + "\t" + ((int)energy);
+
+ int iCost = (int) (costfactor * 1000 + 0.5f);
+ return (lon - 180000000) + "\t"
+ + (lat - 90000000) + "\t"
+ + ele / 4 + "\t"
+ + linkdist + "\t"
+ + iCost + "\t"
+ + linkelevationcost
+ + "\t" + linkturncost
+ + "\t" + linknodecost
+ + "\t" + linkinitcost
+ + "\t" + wayKeyValues
+ + "\t" + (nodeKeyValues == null ? "" : nodeKeyValues)
+ + "\t" + ((int) time)
+ + "\t" + ((int) energy);
}
- void add( MessageData d )
- {
+ void add(MessageData d) {
linkdist += d.linkdist;
linkelevationcost += d.linkelevationcost;
linkturncost += d.linkturncost;
linknodecost += d.linknodecost;
- linkinitcost+= d.linkinitcost;
+ linkinitcost += d.linkinitcost;
}
- MessageData copy()
- {
- try
- {
- return (MessageData)clone();
- }
- catch( CloneNotSupportedException e )
- {
- throw new RuntimeException( e );
+ MessageData copy() {
+ try {
+ return (MessageData) clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
}
}
@Override
- public String toString()
- {
+ public String toString() {
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
}
- public int getPrio()
- {
+ public int getPrio() {
return priorityclassifier;
}
- public boolean isBadOneway()
- {
- return ( classifiermask & 1 ) != 0;
+ public boolean isBadOneway() {
+ return (classifiermask & 1) != 0;
}
- public boolean isGoodOneway()
- {
- return ( classifiermask & 2 ) != 0;
+ public boolean isGoodOneway() {
+ return (classifiermask & 2) != 0;
}
- public boolean isRoundabout()
- {
- return ( classifiermask & 4 ) != 0;
+ public boolean isRoundabout() {
+ return (classifiermask & 4) != 0;
}
- public boolean isLinktType()
- {
- return ( classifiermask & 8 ) != 0;
+ public boolean isLinktType() {
+ return (classifiermask & 8) != 0;
}
- public boolean isGoodForCars()
- {
- return ( classifiermask & 16 ) != 0;
+ public boolean isGoodForCars() {
+ return (classifiermask & 16) != 0;
}
}
diff --git a/brouter-core/src/main/java/btools/router/OsmNodeNamed.java b/brouter-core/src/main/java/btools/router/OsmNodeNamed.java
index f0f89ba..ff0b789 100644
--- a/brouter-core/src/main/java/btools/router/OsmNodeNamed.java
+++ b/brouter-core/src/main/java/btools/router/OsmNodeNamed.java
@@ -1,103 +1,98 @@
-/**
- * Container for an osm node
- *
- * @author ab
- */
-package btools.router;
-
-import btools.mapaccess.OsmNode;
-import btools.util.CheapRuler;
-
-public class OsmNodeNamed extends OsmNode
-{
- public String name;
- public double radius; // radius of nogopoint (in meters)
- public double nogoWeight; // weight for nogopoint
- public boolean isNogo = false;
-
- public OsmNodeNamed()
- {
- }
-
- public OsmNodeNamed( OsmNode n)
- {
- super( n.ilon, n.ilat );
- }
-
- @Override
- public String toString()
- {
- if ( Double.isNaN(nogoWeight ) ) {
- return ilon + "," + ilat + "," + name;
- } else {
- return ilon + "," + ilat + "," + name + "," + nogoWeight;
- }
- }
-
- public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
- double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1 + lat2) >> 1 );
-
- boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
- boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
- // First point is within the circle
- if (isFirstPointWithinCircle) {
- // Last point is within the circle
- if (isLastPointWithinCircle) {
- return totalSegmentLength;
- }
- // Last point is not within the circle
- // Just swap points and go on with first first point not within the
- // circle now.
- // Swap longitudes
- int tmp = lon2;
- lon2 = lon1;
- lon1 = tmp;
- // Swap latitudes
- tmp = lat2;
- lat2 = lat1;
- lat1 = tmp;
- // Fix boolean values
- isLastPointWithinCircle = isFirstPointWithinCircle;
- isFirstPointWithinCircle = false;
- }
- // Distance between the initial point and projection of center of
- // the circle on the current segment.
- double initialToProject = (
- (lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
- + (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
- ) / totalSegmentLength;
- // Distance between the initial point and the center of the circle.
- double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
- // Half length of the segment within the circle
- double halfDistanceWithin = Math.sqrt(
- radius*radius - (
- initialToCenter*initialToCenter -
- initialToProject*initialToProject
- )
- );
- // Last point is within the circle
- if (isLastPointWithinCircle) {
- return halfDistanceWithin + (totalSegmentLength - initialToProject);
- }
- return 2 * halfDistanceWithin;
- }
-
- public static OsmNodeNamed decodeNogo( String s )
- {
- OsmNodeNamed n = new OsmNodeNamed();
- int idx1 = s.indexOf( ',' );
- n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
- int idx2 = s.indexOf( ',', idx1+1 );
- n.ilat = Integer.parseInt( s.substring( idx1+1, idx2 ) );
- int idx3 = s.indexOf( ',', idx2+1 );
- if ( idx3 == -1) {
- n.name = s.substring( idx2 + 1 );
- n.nogoWeight = Double.NaN;
- } else {
- n.name = s.substring( idx2+1, idx3 );
- n.nogoWeight = Double.parseDouble( s.substring( idx3 + 1 ) );
- }
- n.isNogo = true;
- return n;
- }
-}
+/**
+ * Container for an osm node
+ *
+ * @author ab
+ */
+package btools.router;
+
+import btools.mapaccess.OsmNode;
+import btools.util.CheapRuler;
+
+public class OsmNodeNamed extends OsmNode {
+ public String name;
+ public double radius; // radius of nogopoint (in meters)
+ public double nogoWeight; // weight for nogopoint
+ public boolean isNogo = false;
+
+ public OsmNodeNamed() {
+ }
+
+ public OsmNodeNamed(OsmNode n) {
+ super(n.ilon, n.ilat);
+ }
+
+ @Override
+ public String toString() {
+ if (Double.isNaN(nogoWeight)) {
+ return ilon + "," + ilat + "," + name;
+ } else {
+ return ilon + "," + ilat + "," + name + "," + nogoWeight;
+ }
+ }
+
+ public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
+ double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
+
+ boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
+ boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
+ // First point is within the circle
+ if (isFirstPointWithinCircle) {
+ // Last point is within the circle
+ if (isLastPointWithinCircle) {
+ return totalSegmentLength;
+ }
+ // Last point is not within the circle
+ // Just swap points and go on with first first point not within the
+ // circle now.
+ // Swap longitudes
+ int tmp = lon2;
+ lon2 = lon1;
+ lon1 = tmp;
+ // Swap latitudes
+ tmp = lat2;
+ lat2 = lat1;
+ lat1 = tmp;
+ // Fix boolean values
+ isLastPointWithinCircle = isFirstPointWithinCircle;
+ isFirstPointWithinCircle = false;
+ }
+ // Distance between the initial point and projection of center of
+ // the circle on the current segment.
+ double initialToProject = (
+ (lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
+ + (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
+ ) / totalSegmentLength;
+ // Distance between the initial point and the center of the circle.
+ double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
+ // Half length of the segment within the circle
+ double halfDistanceWithin = Math.sqrt(
+ radius * radius - (
+ initialToCenter * initialToCenter -
+ initialToProject * initialToProject
+ )
+ );
+ // Last point is within the circle
+ if (isLastPointWithinCircle) {
+ return halfDistanceWithin + (totalSegmentLength - initialToProject);
+ }
+ return 2 * halfDistanceWithin;
+ }
+
+ public static OsmNodeNamed decodeNogo(String s) {
+ OsmNodeNamed n = new OsmNodeNamed();
+ int idx1 = s.indexOf(',');
+ n.ilon = Integer.parseInt(s.substring(0, idx1));
+ int idx2 = s.indexOf(',', idx1 + 1);
+ n.ilat = Integer.parseInt(s.substring(idx1 + 1, idx2));
+ int idx3 = s.indexOf(',', idx2 + 1);
+ if (idx3 == -1) {
+ n.name = s.substring(idx2 + 1);
+ n.nogoWeight = Double.NaN;
+ } else {
+ n.name = s.substring(idx2 + 1, idx3);
+ n.nogoWeight = Double.parseDouble(s.substring(idx3 + 1));
+ }
+ n.isNogo = true;
+ return n;
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java
index cce62db..3ebd067 100644
--- a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java
+++ b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java
@@ -1,542 +1,499 @@
-/**********************************************************************************************
- Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
-
- The following methods are based on work of Dan Sunday published at:
- http://geomalgorithms.com/a03-_inclusion.html
-
- cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
-
-**********************************************************************************************/
-package btools.router;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import btools.util.CheapRuler;
-
-public class OsmNogoPolygon extends OsmNodeNamed
-{
- public final static class Point
- {
- public final int y;
- public final int x;
-
- Point(final int lon, final int lat)
- {
- x = lon;
- y = lat;
- }
- }
-
- public final List points = new ArrayList();
-
- public final boolean isClosed;
-
- public OsmNogoPolygon(boolean closed)
- {
- this.isClosed = closed;
- this.isNogo = true;
- this.name = "";
- }
-
- public final void addVertex(int lon, int lat)
- {
- points.add(new Point(lon, lat));
- }
-
- /**
- * calcBoundingCircle is inspired by the algorithm described on
- * http://geomalgorithms.com/a08-_containers.html
- * (fast computation of bounding circly in c). It is not as fast (the original
- * algorithm runs in linear time), as it may do more iterations but it takes
- * into account the coslat-factor being used for the linear approximation that
- * is also used in other places of brouter does change when moving the centerpoint
- * with each iteration.
- * This is done to ensure the calculated radius being used
- * in RoutingContext.calcDistance will actually contain the whole polygon.
- *
- * For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
- * As this is only run once on initialization of OsmNogoPolygon this methods
- * overall usage of cpu is neglegible in comparism to the cpu-usage of the
- * actual routing algoritm.
- */
- public void calcBoundingCircle()
- {
- int cxmin, cxmax, cymin, cymax;
- cxmin = cymin = Integer.MAX_VALUE;
- cxmax = cymax = Integer.MIN_VALUE;
-
- // first calculate a starting center point as center of boundingbox
- for (int i = 0; i < points.size(); i++)
- {
- final Point p = points.get(i);
- if (p.x < cxmin)
- {
- cxmin = p.x;
- }
- if (p.x > cxmax)
- {
- cxmax = p.x;
- }
- if (p.y < cymin)
- {
- cymin = p.y;
- }
- if (p.y > cymax)
- {
- cymax = p.y;
- }
- }
-
- int cx = (cxmax+cxmin) / 2; // center of circle
- int cy = (cymax+cymin) / 2;
-
- double[] lonlat2m = CheapRuler.getLonLatToMeterScales( cy ); // conversion-factors at the center of circle
- double dlon2m = lonlat2m[0];
- double dlat2m = lonlat2m[1];
-
- double rad = 0; // radius
-
- double dmax = 0; // length of vector from center to point
- int i_max = -1;
-
- do
- {
- // now identify the point outside of the circle that has the greatest distance
- for (int i = 0; i < points.size(); i++)
- {
- final Point p = points.get(i);
-
- // to get precisely the same results as in RoutingContext.calcDistance()
- // it's crucial to use the factors of the center!
- final double x1 = (cx - p.x) * dlon2m;
- final double y1 = (cy - p.y) * dlat2m;
- final double dist = Math.sqrt( x1*x1 + y1*y1 );
-
- if (dist <= rad)
- {
- continue;
- }
- if (dist > dmax)
- {
- // new maximum distance found
- dmax = dist;
- i_max = i;
- }
- }
- if (i_max < 0)
- {
- break; // leave loop when no point outside the circle is found any more.
- }
- final double dd = 0.5 * (1 - rad / dmax);
-
- final Point p = points.get(i_max); // calculate new radius to just include this point
- cx += (int)(dd * (p.x - cx) + 0.5); // shift center toward point
- cy += (int)(dd * (p.y - cy) + 0.5);
-
- // get new factors at shifted centerpoint
- lonlat2m = CheapRuler.getLonLatToMeterScales( cy );
- dlon2m = lonlat2m[0];
- dlat2m = lonlat2m[1];
-
- final double x1 = (cx - p.x) * dlon2m;
- final double y1 = (cy - p.y) * dlat2m;
- dmax = rad = Math.sqrt( x1*x1 + y1*y1 );
- i_max = -1;
- }
- while (true);
-
- ilon = cx;
- ilat = cy;
- radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
- return;
- }
-
- /**
- * tests whether a segment defined by lon and lat of two points does either
- * intersect the polygon or any of the endpoints (or both) are enclosed by
- * the polygon. For this test the winding-number algorithm is
- * being used. That means a point being within an overlapping region of the
- * polygon is also taken as being 'inside' the polygon.
- *
- * @param lon0 longitude of start point
- * @param lat0 latitude of start point
- * @param lon1 longitude of end point
- * @param lat1 latitude of start point
- * @return true if segment or any of it's points are 'inside' of polygon
- */
- public boolean intersects(int lon0, int lat0, int lon1, int lat1)
- {
- final Point p0 = new Point (lon0,lat0);
- final Point p1 = new Point (lon1,lat1);
- int i_last = points.size()-1;
- Point p2 = points.get(isClosed ? i_last : 0 );
- for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
- {
- Point p3 = points.get(i);
- // does it intersect with at least one of the polygon's segments?
- if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
- {
- return true;
- }
- p2 = p3;
- }
- return false;
- }
-
- public boolean isOnPolyline( long px, long py )
- {
- int i_last = points.size()-1;
- Point p1 = points.get(0);
- for (int i = 1 ; i <= i_last; i++)
- {
- final Point p2 = points.get(i);
- if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
- {
- return true;
- }
- p1 = p2;
- }
- return false;
- }
-
- public static boolean isOnLine( long px, long py, long p0x, long p0y, long p1x, long p1y )
- {
- final double v10x = px-p0x;
- final double v10y = py-p0y;
- final double v12x = p1x-p0x;
- final double v12y = p1y-p0y;
-
- if ( v10x == 0 ) // P0->P1 vertical?
- {
- if ( v10y == 0 ) // P0 == P1?
- {
- return true;
- }
- if ( v12x != 0 ) // P1->P2 not vertical?
- {
- return false;
- }
- return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0?
- }
- if ( v10y == 0 ) // P0->P1 horizontal?
- {
- if ( v12y != 0 ) // P1->P2 not horizontal?
- {
- return false;
- }
- // if ( P10x == 0 ) // P0 == P1? already tested
- return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0?
- }
- final double kx = v12x / v10x;
- if ( kx < 1 )
- {
- return false;
- }
- return kx == v12y / v10y;
- }
-
-/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
- This code may be freely used and modified for any purpose providing that
- this copyright notice is included with it. SoftSurfer makes no warranty for
- this code, and cannot be held liable for any real or imagined damage
- resulting from its use. Users of this code must verify correctness for
- their application. */
- /**
- * winding number test for a point in a polygon
- *
- * @param px longitude of the point to check
- * @param py latitude of the point to check
- * @return a boolean whether the point is within the polygon or not.
- */
- public boolean isWithin(final long px, final long py)
- {
- int wn = 0; // the winding number counter
-
- // loop through all edges of the polygon
- final int i_last = points.size()-1;
- final Point p0 = points.get(isClosed ? i_last : 0);
- long p0x = p0.x; // need to use long to avoid overflow in products
- long p0y = p0.y;
-
- for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
- {
- final Point p1 = points.get(i);
-
- final long p1x = p1.x;
- final long p1y = p1.y;
-
- if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
- {
- return true;
- }
-
- if (p0y <= py) // start y <= p.y
- {
- if (p1y > py) // an upward crossing
- { // p left of edge
- if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0)
- {
- ++wn; // have a valid up intersect
- }
- }
- }
- else // start y > p.y (no test needed)
- {
- if (p1y <= py) // a downward crossing
- { // p right of edge
- if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
- {
- --wn; // have a valid down intersect
- }
- }
- }
- p0x = p1x;
- p0y = p1y;
- }
- return wn != 0;
- }
-
- /**
- * Compute the length of the segment within the polygon.
- *
- * @param lon1 Integer longitude of the first point of the segment.
- * @param lat1 Integer latitude of the first point of the segment.
- * @param lon2 Integer longitude of the last point of the segment.
- * @param lat2 Integer latitude of the last point of the segment.
- *
- * @return The length, in meters, of the portion of the segment which is
- * included in the polygon.
- */
- public double distanceWithinPolygon(int lon1, int lat1, int lon2, int lat2) {
- double distance = 0.;
-
- // Extremities of the segments
- final Point p1 = new Point (lon1, lat1);
- final Point p2 = new Point (lon2, lat2);
-
- Point previousIntersectionOnSegment = null;
- if (isWithin(lon1, lat1))
- {
- // Start point of the segment is within the polygon, this is the first
- // "intersection".
- previousIntersectionOnSegment = p1;
- }
-
- // Loop over edges of the polygon to find intersections
- int i_last = points.size() - 1;
- for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++)
- {
- Point edgePoint1 = points.get(j);
- Point edgePoint2 = points.get(i);
- int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
-
- if (isClosed && intersectsEdge == 1)
- {
- // Intersects with a (closed) polygon edge on a single point
- // Distance is zero when crossing a polyline.
- // Let's find this intersection point
- int xdiffSegment = lon1 - lon2;
- int xdiffEdge = edgePoint1.x - edgePoint2.x;
- int ydiffSegment = lat1 - lat2;
- int ydiffEdge = edgePoint1.y - edgePoint2.y;
- int div = xdiffSegment * ydiffEdge - xdiffEdge * ydiffSegment;
- long dSegment = (long) lon1 * (long) lat2 - (long) lon2 * (long) lat1;
- long dEdge = (long) edgePoint1.x * (long) edgePoint2.y - (long) edgePoint2.x * (long) edgePoint1.y;
- // Coordinates of the intersection
- Point intersection = new Point(
- (int) ((dSegment * xdiffEdge - dEdge * xdiffSegment) / div),
- (int) ((dSegment * ydiffEdge - dEdge * ydiffSegment) / div)
- );
- if (
- previousIntersectionOnSegment != null
- && isWithin(
- (intersection.x + previousIntersectionOnSegment.x) >> 1,
- (intersection.y + previousIntersectionOnSegment.y) >> 1
- )
- ) {
- // There was a previous match within the polygon and this part of the
- // segment is within the polygon.
- distance += CheapRuler.distance(
- previousIntersectionOnSegment.x, previousIntersectionOnSegment.y,
- intersection.x, intersection.y
- );
- }
- previousIntersectionOnSegment = intersection;
- }
- else if (intersectsEdge == 2) {
- // Segment and edge overlaps
- // FIXME: Could probably be done in a smarter way
- distance += Math.min(
- CheapRuler.distance(p1.x, p1.y, p2.x, p2.y),
- Math.min(
- CheapRuler.distance(edgePoint1.x, edgePoint1.y, edgePoint2.x, edgePoint2.y),
- Math.min(
- CheapRuler.distance(p1.x, p1.y, edgePoint2.x, edgePoint2.y),
- CheapRuler.distance(edgePoint1.x, edgePoint1.y, p2.x, p2.y)
- )
- )
- );
- // FIXME: We could store intersection.
- previousIntersectionOnSegment = null;
- }
- }
-
- if (
- previousIntersectionOnSegment != null
- && isWithin(lon2, lat2)
- ) {
- // Last point is within the polygon, add the remaining missing distance.
- distance += CheapRuler.distance(
- previousIntersectionOnSegment.x, previousIntersectionOnSegment.y,
- lon2, lat2
- );
- }
- return distance;
- }
-
-/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
- This code may be freely used and modified for any purpose providing that
- this copyright notice is included with it. SoftSurfer makes no warranty for
- this code, and cannot be held liable for any real or imagined damage
- resulting from its use. Users of this code must verify correctness for
- their application. */
- /**
- * inSegment(): determine if a point is inside a segment
- *
- * @param p a point
- * @param seg_p0 starting point of segment
- * @param seg_p1 ending point of segment
- * @return 1 = P is inside S
- * 0 = P is not inside S
- */
- private static boolean inSegment( final Point p, final Point seg_p0, final Point seg_p1)
- {
- final int sp0x = seg_p0.x;
- final int sp1x = seg_p1.x;
-
- if (sp0x != sp1x) // S is not vertical
- {
- final int px = p.x;
- if (sp0x <= px && px <= sp1x)
- {
- return true;
- }
- if (sp0x >= px && px >= sp1x)
- {
- return true;
- }
- }
- else // S is vertical, so test y coordinate
- {
- final int sp0y = seg_p0.y;
- final int sp1y = seg_p1.y;
- final int py = p.y;
-
- if (sp0y <= py && py <= sp1y)
- {
- return true;
- }
- if (sp0y >= py && py >= sp1y)
- {
- return true;
- }
- }
- return false;
- }
-
-/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
- This code may be freely used and modified for any purpose providing that
- this copyright notice is included with it. SoftSurfer makes no warranty for
- this code, and cannot be held liable for any real or imagined damage
- resulting from its use. Users of this code must verify correctness for
- their application. */
- /**
- * intersect2D_2Segments(): find the 2D intersection of 2 finite segments
- * @param s1p0 start point of segment 1
- * @param s1p1 end point of segment 1
- * @param s2p0 start point of segment 2
- * @param s2p1 end point of segment 2
- * @return 0=disjoint (no intersect)
- * 1=intersect in unique point I0
- * 2=overlap in segment from I0 to I1
- */
- private static int intersect2D_2Segments( final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1 )
- {
- final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
- final long uy = s1p1.y - s1p0.y;
- final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
- final long vy = s2p1.y - s2p0.y;
- final long wx = s1p0.x - s2p0.x; // vector w = S1P0-S2P0 (from start of segment 2 to start of segment 1
- final long wy = s1p0.y - s2p0.y;
-
- final double d = ux * vy - uy * vx;
-
- // test if they are parallel (includes either being a point)
- if (d == 0) // S1 and S2 are parallel
- {
- if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
- {
- return 0; // they are NOT collinear
- }
-
- // they are collinear or degenerate
- // check if they are degenerate points
- final boolean du = ((ux == 0) && (uy == 0));
- final boolean dv = ((vx == 0) && (vy == 0));
- if (du && dv) // both segments are points
- {
- return (wx == 0 && wy == 0) ? 0 : 1; // return 0 if they are distinct points
- }
- if (du) // S1 is a single point
- {
- return inSegment(s1p0, s2p0, s2p1) ? 1 : 0; // is it part of S2?
- }
- if (dv) // S2 a single point
- {
- return inSegment(s2p0, s1p0, s1p1) ? 1 : 0; // is it part of S1?
- }
- // they are collinear segments - get overlap (or not)
- double t0, t1; // endpoints of S1 in eqn for S2
- final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
- final int w2y = s1p1.y - s2p0.y;
- if (vx != 0)
- {
- t0 = wx / vx;
- t1 = w2x / vx;
- }
- else
- {
- t0 = wy / vy;
- t1 = w2y / vy;
- }
- if (t0 > t1) // must have t0 smaller than t1
- {
- final double t=t0; // swap if not
- t0=t1;
- t1=t;
- }
- if (t0 > 1 || t1 < 0)
- {
- return 0; // NO overlap
- }
- t0 = t0<0? 0 : t0; // clip to min 0
- t1 = t1>1? 1 : t1; // clip to max 1
-
- return (t0 == t1) ? 1 : 2; // return 1 if intersect is a point
- }
-
- // the segments are skew and may intersect in a point
- // get the intersect parameter for S1
-
- final double sI = (vx * wy - vy * wx) / d;
- if (sI < 0 || sI > 1) // no intersect with S1
- {
- return 0;
- }
-
- // get the intersect parameter for S2
- final double tI = (ux * wy - uy * wx) / d;
- return (tI < 0 || tI > 1) ? 0 : 1; // return 0 if no intersect with S2
- }
-}
+/**********************************************************************************************
+ Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
+
+ The following methods are based on work of Dan Sunday published at:
+ http://geomalgorithms.com/a03-_inclusion.html
+
+ cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
+ **********************************************************************************************/
+package btools.router;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import btools.util.CheapRuler;
+
+public class OsmNogoPolygon extends OsmNodeNamed {
+ public final static class Point {
+ public final int y;
+ public final int x;
+
+ Point(final int lon, final int lat) {
+ x = lon;
+ y = lat;
+ }
+ }
+
+ public final List points = new ArrayList();
+
+ public final boolean isClosed;
+
+ public OsmNogoPolygon(boolean closed) {
+ this.isClosed = closed;
+ this.isNogo = true;
+ this.name = "";
+ }
+
+ public final void addVertex(int lon, int lat) {
+ points.add(new Point(lon, lat));
+ }
+
+ /**
+ * calcBoundingCircle is inspired by the algorithm described on
+ * http://geomalgorithms.com/a08-_containers.html
+ * (fast computation of bounding circly in c). It is not as fast (the original
+ * algorithm runs in linear time), as it may do more iterations but it takes
+ * into account the coslat-factor being used for the linear approximation that
+ * is also used in other places of brouter does change when moving the centerpoint
+ * with each iteration.
+ * This is done to ensure the calculated radius being used
+ * in RoutingContext.calcDistance will actually contain the whole polygon.
+ *
+ * For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
+ * As this is only run once on initialization of OsmNogoPolygon this methods
+ * overall usage of cpu is neglegible in comparism to the cpu-usage of the
+ * actual routing algoritm.
+ */
+ public void calcBoundingCircle() {
+ int cxmin, cxmax, cymin, cymax;
+ cxmin = cymin = Integer.MAX_VALUE;
+ cxmax = cymax = Integer.MIN_VALUE;
+
+ // first calculate a starting center point as center of boundingbox
+ for (int i = 0; i < points.size(); i++) {
+ final Point p = points.get(i);
+ if (p.x < cxmin) {
+ cxmin = p.x;
+ }
+ if (p.x > cxmax) {
+ cxmax = p.x;
+ }
+ if (p.y < cymin) {
+ cymin = p.y;
+ }
+ if (p.y > cymax) {
+ cymax = p.y;
+ }
+ }
+
+ int cx = (cxmax + cxmin) / 2; // center of circle
+ int cy = (cymax + cymin) / 2;
+
+ double[] lonlat2m = CheapRuler.getLonLatToMeterScales(cy); // conversion-factors at the center of circle
+ double dlon2m = lonlat2m[0];
+ double dlat2m = lonlat2m[1];
+
+ double rad = 0; // radius
+
+ double dmax = 0; // length of vector from center to point
+ int i_max = -1;
+
+ do {
+ // now identify the point outside of the circle that has the greatest distance
+ for (int i = 0; i < points.size(); i++) {
+ final Point p = points.get(i);
+
+ // to get precisely the same results as in RoutingContext.calcDistance()
+ // it's crucial to use the factors of the center!
+ final double x1 = (cx - p.x) * dlon2m;
+ final double y1 = (cy - p.y) * dlat2m;
+ final double dist = Math.sqrt(x1 * x1 + y1 * y1);
+
+ if (dist <= rad) {
+ continue;
+ }
+ if (dist > dmax) {
+ // new maximum distance found
+ dmax = dist;
+ i_max = i;
+ }
+ }
+ if (i_max < 0) {
+ break; // leave loop when no point outside the circle is found any more.
+ }
+ final double dd = 0.5 * (1 - rad / dmax);
+
+ final Point p = points.get(i_max); // calculate new radius to just include this point
+ cx += (int) (dd * (p.x - cx) + 0.5); // shift center toward point
+ cy += (int) (dd * (p.y - cy) + 0.5);
+
+ // get new factors at shifted centerpoint
+ lonlat2m = CheapRuler.getLonLatToMeterScales(cy);
+ dlon2m = lonlat2m[0];
+ dlat2m = lonlat2m[1];
+
+ final double x1 = (cx - p.x) * dlon2m;
+ final double y1 = (cy - p.y) * dlat2m;
+ dmax = rad = Math.sqrt(x1 * x1 + y1 * y1);
+ i_max = -1;
+ }
+ while (true);
+
+ ilon = cx;
+ ilat = cy;
+ radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
+ return;
+ }
+
+ /**
+ * tests whether a segment defined by lon and lat of two points does either
+ * intersect the polygon or any of the endpoints (or both) are enclosed by
+ * the polygon. For this test the winding-number algorithm is
+ * being used. That means a point being within an overlapping region of the
+ * polygon is also taken as being 'inside' the polygon.
+ *
+ * @param lon0 longitude of start point
+ * @param lat0 latitude of start point
+ * @param lon1 longitude of end point
+ * @param lat1 latitude of start point
+ * @return true if segment or any of it's points are 'inside' of polygon
+ */
+ public boolean intersects(int lon0, int lat0, int lon1, int lat1) {
+ final Point p0 = new Point(lon0, lat0);
+ final Point p1 = new Point(lon1, lat1);
+ int i_last = points.size() - 1;
+ Point p2 = points.get(isClosed ? i_last : 0);
+ for (int i = isClosed ? 0 : 1; i <= i_last; i++) {
+ Point p3 = points.get(i);
+ // does it intersect with at least one of the polygon's segments?
+ if (intersect2D_2Segments(p0, p1, p2, p3) > 0) {
+ return true;
+ }
+ p2 = p3;
+ }
+ return false;
+ }
+
+ public boolean isOnPolyline(long px, long py) {
+ int i_last = points.size() - 1;
+ Point p1 = points.get(0);
+ for (int i = 1; i <= i_last; i++) {
+ final Point p2 = points.get(i);
+ if (OsmNogoPolygon.isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) {
+ return true;
+ }
+ p1 = p2;
+ }
+ return false;
+ }
+
+ public static boolean isOnLine(long px, long py, long p0x, long p0y, long p1x, long p1y) {
+ final double v10x = px - p0x;
+ final double v10y = py - p0y;
+ final double v12x = p1x - p0x;
+ final double v12y = p1y - p0y;
+
+ if (v10x == 0) // P0->P1 vertical?
+ {
+ if (v10y == 0) // P0 == P1?
+ {
+ return true;
+ }
+ if (v12x != 0) // P1->P2 not vertical?
+ {
+ return false;
+ }
+ return (v12y / v10y) >= 1; // P1->P2 at least as long as P1->P0?
+ }
+ if (v10y == 0) // P0->P1 horizontal?
+ {
+ if (v12y != 0) // P1->P2 not horizontal?
+ {
+ return false;
+ }
+ // if ( P10x == 0 ) // P0 == P1? already tested
+ return (v12x / v10x) >= 1; // P1->P2 at least as long as P1->P0?
+ }
+ final double kx = v12x / v10x;
+ if (kx < 1) {
+ return false;
+ }
+ return kx == v12y / v10y;
+ }
+
+/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
+ This code may be freely used and modified for any purpose providing that
+ this copyright notice is included with it. SoftSurfer makes no warranty for
+ this code, and cannot be held liable for any real or imagined damage
+ resulting from its use. Users of this code must verify correctness for
+ their application. */
+
+ /**
+ * winding number test for a point in a polygon
+ *
+ * @param px longitude of the point to check
+ * @param py latitude of the point to check
+ * @return a boolean whether the point is within the polygon or not.
+ */
+ public boolean isWithin(final long px, final long py) {
+ int wn = 0; // the winding number counter
+
+ // loop through all edges of the polygon
+ final int i_last = points.size() - 1;
+ final Point p0 = points.get(isClosed ? i_last : 0);
+ long p0x = p0.x; // need to use long to avoid overflow in products
+ long p0y = p0.y;
+
+ for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
+ {
+ final Point p1 = points.get(i);
+
+ final long p1x = p1.x;
+ final long p1y = p1.y;
+
+ if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y)) {
+ return true;
+ }
+
+ if (p0y <= py) // start y <= p.y
+ {
+ if (p1y > py) // an upward crossing
+ { // p left of edge
+ if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0) {
+ ++wn; // have a valid up intersect
+ }
+ }
+ } else // start y > p.y (no test needed)
+ {
+ if (p1y <= py) // a downward crossing
+ { // p right of edge
+ if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0) {
+ --wn; // have a valid down intersect
+ }
+ }
+ }
+ p0x = p1x;
+ p0y = p1y;
+ }
+ return wn != 0;
+ }
+
+ /**
+ * Compute the length of the segment within the polygon.
+ *
+ * @param lon1 Integer longitude of the first point of the segment.
+ * @param lat1 Integer latitude of the first point of the segment.
+ * @param lon2 Integer longitude of the last point of the segment.
+ * @param lat2 Integer latitude of the last point of the segment.
+ * @return The length, in meters, of the portion of the segment which is
+ * included in the polygon.
+ */
+ public double distanceWithinPolygon(int lon1, int lat1, int lon2, int lat2) {
+ double distance = 0.;
+
+ // Extremities of the segments
+ final Point p1 = new Point(lon1, lat1);
+ final Point p2 = new Point(lon2, lat2);
+
+ Point previousIntersectionOnSegment = null;
+ if (isWithin(lon1, lat1)) {
+ // Start point of the segment is within the polygon, this is the first
+ // "intersection".
+ previousIntersectionOnSegment = p1;
+ }
+
+ // Loop over edges of the polygon to find intersections
+ int i_last = points.size() - 1;
+ for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++) {
+ Point edgePoint1 = points.get(j);
+ Point edgePoint2 = points.get(i);
+ int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
+
+ if (isClosed && intersectsEdge == 1) {
+ // Intersects with a (closed) polygon edge on a single point
+ // Distance is zero when crossing a polyline.
+ // Let's find this intersection point
+ int xdiffSegment = lon1 - lon2;
+ int xdiffEdge = edgePoint1.x - edgePoint2.x;
+ int ydiffSegment = lat1 - lat2;
+ int ydiffEdge = edgePoint1.y - edgePoint2.y;
+ int div = xdiffSegment * ydiffEdge - xdiffEdge * ydiffSegment;
+ long dSegment = (long) lon1 * (long) lat2 - (long) lon2 * (long) lat1;
+ long dEdge = (long) edgePoint1.x * (long) edgePoint2.y - (long) edgePoint2.x * (long) edgePoint1.y;
+ // Coordinates of the intersection
+ Point intersection = new Point(
+ (int) ((dSegment * xdiffEdge - dEdge * xdiffSegment) / div),
+ (int) ((dSegment * ydiffEdge - dEdge * ydiffSegment) / div)
+ );
+ if (
+ previousIntersectionOnSegment != null
+ && isWithin(
+ (intersection.x + previousIntersectionOnSegment.x) >> 1,
+ (intersection.y + previousIntersectionOnSegment.y) >> 1
+ )
+ ) {
+ // There was a previous match within the polygon and this part of the
+ // segment is within the polygon.
+ distance += CheapRuler.distance(
+ previousIntersectionOnSegment.x, previousIntersectionOnSegment.y,
+ intersection.x, intersection.y
+ );
+ }
+ previousIntersectionOnSegment = intersection;
+ } else if (intersectsEdge == 2) {
+ // Segment and edge overlaps
+ // FIXME: Could probably be done in a smarter way
+ distance += Math.min(
+ CheapRuler.distance(p1.x, p1.y, p2.x, p2.y),
+ Math.min(
+ CheapRuler.distance(edgePoint1.x, edgePoint1.y, edgePoint2.x, edgePoint2.y),
+ Math.min(
+ CheapRuler.distance(p1.x, p1.y, edgePoint2.x, edgePoint2.y),
+ CheapRuler.distance(edgePoint1.x, edgePoint1.y, p2.x, p2.y)
+ )
+ )
+ );
+ // FIXME: We could store intersection.
+ previousIntersectionOnSegment = null;
+ }
+ }
+
+ if (
+ previousIntersectionOnSegment != null
+ && isWithin(lon2, lat2)
+ ) {
+ // Last point is within the polygon, add the remaining missing distance.
+ distance += CheapRuler.distance(
+ previousIntersectionOnSegment.x, previousIntersectionOnSegment.y,
+ lon2, lat2
+ );
+ }
+ return distance;
+ }
+
+/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
+ This code may be freely used and modified for any purpose providing that
+ this copyright notice is included with it. SoftSurfer makes no warranty for
+ this code, and cannot be held liable for any real or imagined damage
+ resulting from its use. Users of this code must verify correctness for
+ their application. */
+
+ /**
+ * inSegment(): determine if a point is inside a segment
+ *
+ * @param p a point
+ * @param seg_p0 starting point of segment
+ * @param seg_p1 ending point of segment
+ * @return 1 = P is inside S
+ * 0 = P is not inside S
+ */
+ private static boolean inSegment(final Point p, final Point seg_p0, final Point seg_p1) {
+ final int sp0x = seg_p0.x;
+ final int sp1x = seg_p1.x;
+
+ if (sp0x != sp1x) // S is not vertical
+ {
+ final int px = p.x;
+ if (sp0x <= px && px <= sp1x) {
+ return true;
+ }
+ if (sp0x >= px && px >= sp1x) {
+ return true;
+ }
+ } else // S is vertical, so test y coordinate
+ {
+ final int sp0y = seg_p0.y;
+ final int sp1y = seg_p1.y;
+ final int py = p.y;
+
+ if (sp0y <= py && py <= sp1y) {
+ return true;
+ }
+ if (sp0y >= py && py >= sp1y) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
+ This code may be freely used and modified for any purpose providing that
+ this copyright notice is included with it. SoftSurfer makes no warranty for
+ this code, and cannot be held liable for any real or imagined damage
+ resulting from its use. Users of this code must verify correctness for
+ their application. */
+
+ /**
+ * intersect2D_2Segments(): find the 2D intersection of 2 finite segments
+ *
+ * @param s1p0 start point of segment 1
+ * @param s1p1 end point of segment 1
+ * @param s2p0 start point of segment 2
+ * @param s2p1 end point of segment 2
+ * @return 0=disjoint (no intersect)
+ * 1=intersect in unique point I0
+ * 2=overlap in segment from I0 to I1
+ */
+ private static int intersect2D_2Segments(final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1) {
+ final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
+ final long uy = s1p1.y - s1p0.y;
+ final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
+ final long vy = s2p1.y - s2p0.y;
+ final long wx = s1p0.x - s2p0.x; // vector w = S1P0-S2P0 (from start of segment 2 to start of segment 1
+ final long wy = s1p0.y - s2p0.y;
+
+ final double d = ux * vy - uy * vx;
+
+ // test if they are parallel (includes either being a point)
+ if (d == 0) // S1 and S2 are parallel
+ {
+ if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0) {
+ return 0; // they are NOT collinear
+ }
+
+ // they are collinear or degenerate
+ // check if they are degenerate points
+ final boolean du = ((ux == 0) && (uy == 0));
+ final boolean dv = ((vx == 0) && (vy == 0));
+ if (du && dv) // both segments are points
+ {
+ return (wx == 0 && wy == 0) ? 0 : 1; // return 0 if they are distinct points
+ }
+ if (du) // S1 is a single point
+ {
+ return inSegment(s1p0, s2p0, s2p1) ? 1 : 0; // is it part of S2?
+ }
+ if (dv) // S2 a single point
+ {
+ return inSegment(s2p0, s1p0, s1p1) ? 1 : 0; // is it part of S1?
+ }
+ // they are collinear segments - get overlap (or not)
+ double t0, t1; // endpoints of S1 in eqn for S2
+ final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
+ final int w2y = s1p1.y - s2p0.y;
+ if (vx != 0) {
+ t0 = wx / vx;
+ t1 = w2x / vx;
+ } else {
+ t0 = wy / vy;
+ t1 = w2y / vy;
+ }
+ if (t0 > t1) // must have t0 smaller than t1
+ {
+ final double t = t0; // swap if not
+ t0 = t1;
+ t1 = t;
+ }
+ if (t0 > 1 || t1 < 0) {
+ return 0; // NO overlap
+ }
+ t0 = t0 < 0 ? 0 : t0; // clip to min 0
+ t1 = t1 > 1 ? 1 : t1; // clip to max 1
+
+ return (t0 == t1) ? 1 : 2; // return 1 if intersect is a point
+ }
+
+ // the segments are skew and may intersect in a point
+ // get the intersect parameter for S1
+
+ final double sI = (vx * wy - vy * wx) / d;
+ if (sI < 0 || sI > 1) // no intersect with S1
+ {
+ return 0;
+ }
+
+ // get the intersect parameter for S2
+ final double tI = (ux * wy - uy * wx) / d;
+ return (tI < 0 || tI > 1) ? 0 : 1; // return 0 if no intersect with S2
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java
index edbf738..f9c0101 100644
--- a/brouter-core/src/main/java/btools/router/OsmPath.java
+++ b/brouter-core/src/main/java/btools/router/OsmPath.java
@@ -1,524 +1,444 @@
-/**
- * Container for link between two Osm nodes
- *
- * @author ab
- */
-package btools.router;
-
-import java.io.IOException;
-
-import btools.mapaccess.OsmLink;
-import btools.mapaccess.OsmLinkHolder;
-import btools.mapaccess.OsmNode;
-import btools.mapaccess.OsmTransferNode;
-import btools.mapaccess.TurnRestriction;
-import btools.util.CheapRuler;
-
-abstract class OsmPath implements OsmLinkHolder
-{
- /**
- * The cost of that path (a modified distance)
- */
- public int cost = 0;
-
- // the elevation assumed for that path can have a value
- // if the corresponding node has not
- public short selev;
-
- public int airdistance = 0; // distance to endpos
-
- protected OsmNode sourceNode;
- protected OsmNode targetNode;
-
- protected OsmLink link;
- public OsmPathElement originElement;
- public OsmPathElement myElement;
-
- protected float traffic;
-
- private OsmLinkHolder nextForLink = null;
-
- public int treedepth = 0;
-
- // the position of the waypoint just before
- // this path position (for angle calculation)
- public int originLon;
- public int originLat;
-
- // the classifier of the segment just before this paths position
- protected float lastClassifier;
- protected float lastInitialCost;
-
- protected int priorityclassifier;
-
- private static final int PATH_START_BIT = 1;
- private static final int CAN_LEAVE_DESTINATION_BIT = 2;
- private static final int IS_ON_DESTINATION_BIT = 4;
- private static final int HAD_DESTINATION_START_BIT = 8;
- protected int bitfield = PATH_START_BIT;
-
- private boolean getBit( int mask )
- {
- return (bitfield & mask ) != 0;
- }
-
- private void setBit( int mask, boolean bit )
- {
- if ( getBit( mask ) != bit )
- {
- bitfield ^= mask;
- }
- }
-
- public boolean didEnterDestinationArea()
- {
- return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
- }
-
- public MessageData message;
-
- public void unregisterUpTree( RoutingContext rc )
- {
- try
- {
- OsmPathElement pe = originElement;
- while( pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic)pe).unregister(rc) )
- {
- pe = pe.origin;
- }
- }
- catch( IOException ioe )
- {
- throw new RuntimeException( ioe );
- }
- }
-
- public void registerUpTree()
- {
- if ( originElement instanceof OsmPathElementWithTraffic )
- {
- OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)originElement;
- ot.register();
- ot.addTraffic( traffic );
- }
- }
-
- public void init( OsmLink link )
- {
- this.link = link;
- targetNode = link.getTarget( null );
- selev = targetNode.getSElev();
-
- originLon = -1;
- originLat = -1;
- }
-
- public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
- {
- if ( origin.myElement == null )
- {
- origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
- }
- this.originElement = origin.myElement;
- this.link = link;
- this.sourceNode = origin.targetNode;
- this.targetNode = link.getTarget( sourceNode );
- this.cost = origin.cost;
- this.lastClassifier = origin.lastClassifier;
- this.lastInitialCost = origin.lastInitialCost;
- this.bitfield = origin.bitfield;
- init( origin );
- addAddionalPenalty(refTrack, detailMode, origin, link, rc );
- }
-
- protected abstract void init( OsmPath orig );
-
- protected abstract void resetState();
-
-
- protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
- {
- byte[] description = link.descriptionBitmap;
- if ( description == null )
- {
- return; // could be a beeline path
- }
-
- boolean recordTransferNodes = detailMode || rc.countTraffic;
-
- rc.nogoCost = 0.;
-
- // extract the 3 positions of the first section
- int lon0 = origin.originLon;
- int lat0 = origin.originLat;
-
- int lon1 = sourceNode.getILon();
- int lat1 = sourceNode.getILat();
- short ele1 = origin.selev;
-
- int linkdisttotal = 0;
-
- message = detailMode ? new MessageData() : null;
-
- boolean isReverse = link.isReverse( sourceNode );
-
- // evaluate the way tags
- rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
-
-
- // calculate the costfactor inputs
- float costfactor = rc.expctxWay.getCostfactor();
- boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
- int lastpriorityclassifier = priorityclassifier;
- priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
-
- // *** add initial cost if the classifier changed
- float newClassifier = rc.expctxWay.getInitialClassifier();
- float newInitialCost = rc.expctxWay.getInitialcost();
- float classifierDiff = newClassifier - lastClassifier;
- if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
- {
- float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
- if ( initialcost >= 1000000. )
- {
- cost = -1;
- return;
- }
-
- int iicost = (int)initialcost;
- if ( message != null )
- {
- message.linkinitcost += iicost;
- }
- cost += iicost;
- }
- lastClassifier = newClassifier;
- lastInitialCost = newInitialCost;
-
- // *** destination logic: no destination access in between
- int classifiermask = (int)rc.expctxWay.getClassifierMask();
- boolean newDestination = (classifiermask & 64) != 0;
- boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
- if ( getBit( PATH_START_BIT ) )
- {
- setBit( PATH_START_BIT, false );
- setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
- setBit( HAD_DESTINATION_START_BIT, newDestination );
- }
- else
- {
- if ( oldDestination && !newDestination )
- {
- if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
- {
- setBit( CAN_LEAVE_DESTINATION_BIT, false );
- }
- else
- {
- cost = -1;
- return;
- }
- }
- }
- setBit( IS_ON_DESTINATION_BIT, newDestination );
-
-
- OsmTransferNode transferNode = link.geometry == null ? null
- : rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
-
- for(int nsection=0; ;nsection++)
- {
-
- originLon = lon1;
- originLat = lat1;
-
- int lon2;
- int lat2;
- short ele2;
-
- if ( transferNode == null )
- {
- lon2 = targetNode.ilon;
- lat2 = targetNode.ilat;
- ele2 = targetNode.selev;
- }
- else
- {
- lon2 = transferNode.ilon;
- lat2 = transferNode.ilat;
- ele2 = transferNode.selev;
- }
-
- boolean isStartpoint = lon0 == -1 && lat0 == -1;
-
- // check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
- if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
- {
- if ( rc.inverseDirection
- ? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode )
- : TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode ) )
- {
- cost = -1;
- return;
- }
- }
-
- // if recording, new MessageData for each section (needed for turn-instructions)
- if ( message != null && message.wayKeyValues != null )
- {
- originElement.message = message;
- message = new MessageData();
- }
-
- int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
-
- boolean stopAtEndpoint = false;
- if ( rc.shortestmatch )
- {
- if ( rc.isEndpoint )
- {
- stopAtEndpoint = true;
- ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
- }
- else
- {
- // we just start here, reset everything
- cost = 0;
- resetState();
- lon0 = -1; // reset turncost-pipe
- lat0 = -1;
- isStartpoint = true;
-
- if ( recordTransferNodes )
- {
- if ( rc.wayfraction > 0. )
- {
- ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
- originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
- }
- else
- {
- originElement = null; // prevent duplicate point
- }
- }
-
- if ( rc.checkPendingEndpoint() )
- {
- dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
- if ( rc.shortestmatch )
- {
- stopAtEndpoint = true;
- ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
- }
- }
- }
- }
-
- if ( message != null )
- {
- message.linkdist += dist;
- }
- linkdisttotal += dist;
-
- // apply a start-direction if appropriate (by faking the origin position)
- if ( isStartpoint )
- {
- if ( rc.startDirectionValid )
- {
- double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
- double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
- lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
- lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
- }
- else
- {
- lon0 = lon1 - (lon2-lon1);
- lat0 = lat1 - (lat2-lat1);
- }
- }
- double angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
- double cosangle = rc.anglemeter.getCosAngle();
-
- // *** elevation stuff
- double delta_h = 0.;
- if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
- if ( ele1 != Short.MIN_VALUE )
- {
- delta_h = (ele2 - ele1)/4.;
- if ( rc.inverseDirection )
- {
- delta_h = -delta_h;
- }
- }
-
-
-
-
- double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
-
- double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
- if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
- {
- cost = -1;
- return;
- }
-
- if ( isTrafficBackbone )
- {
- sectionCost = 0.;
- }
-
- cost += (int)sectionCost;
-
- // calculate traffic
- if ( rc.countTraffic )
- {
- int minDist = (int)rc.trafficSourceMinDist;
- int cost2 = cost < minDist ? minDist : cost;
- traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
- }
-
- // compute kinematic
- computeKinematic( rc, dist, delta_h, detailMode );
-
- if ( message != null )
- {
- message.turnangle = (float)angle;
- message.time = (float)getTotalTime();
- message.energy = (float)getTotalEnergy();
- message.priorityclassifier = priorityclassifier;
- message.classifiermask = classifiermask;
- message.lon = lon2;
- message.lat = lat2;
- message.ele = ele2;
- message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
- }
-
- if ( stopAtEndpoint )
- {
- if ( recordTransferNodes )
- {
- originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
- originElement.cost = cost;
- if ( message != null )
- {
- originElement.message = message;
- }
- }
- if ( rc.nogoCost < 0)
- {
- cost = -1;
- }
- else
- {
- cost += rc.nogoCost;
- }
- return;
- }
-
- if ( transferNode == null )
- {
- // *** penalty for being part of the reference track
- if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
- {
- int reftrackcost = linkdisttotal;
- cost += reftrackcost;
- }
- selev = ele2;
- break;
- }
- transferNode = transferNode.next;
-
- if ( recordTransferNodes )
- {
- originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
- originElement.cost = cost;
- originElement.addTraffic( traffic );
- traffic = 0;
- }
- lon0 = lon1;
- lat0 = lat1;
- lon1 = lon2;
- lat1 = lat2;
- ele1 = ele2;
- }
-
- // check for nogo-matches (after the *actual* start of segment)
- if ( rc.nogoCost < 0)
- {
- cost = -1;
- return;
- }
- else
- {
- cost += rc.nogoCost;
- }
-
- // add target-node costs
- double targetCost = processTargetNode( rc );
- if ( targetCost < 0. || targetCost + cost >= 2000000000. )
- {
- cost = -1;
- return;
- }
- cost += (int)targetCost;
- }
-
-
- public short interpolateEle( short e1, short e2, double fraction )
- {
- if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
- {
- return Short.MIN_VALUE;
- }
- return (short)( e1*(1.-fraction) + e2*fraction );
- }
-
- protected abstract double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier );
-
- protected abstract double processTargetNode( RoutingContext rc );
-
- protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
- {
- }
-
- public abstract int elevationCorrection( RoutingContext rc );
-
- public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
-
- public OsmNode getSourceNode()
- {
- return sourceNode;
- }
-
- public OsmNode getTargetNode()
- {
- return targetNode;
- }
-
- public OsmLink getLink()
- {
- return link;
- }
-
-
- public void setNextForLink( OsmLinkHolder holder )
- {
- nextForLink = holder;
- }
-
- public OsmLinkHolder getNextForLink()
- {
- return nextForLink;
- }
-
- public double getTotalTime()
- {
- return 0.;
- }
-
- public double getTotalEnergy()
- {
- return 0.;
- }
-}
+/**
+ * Container for link between two Osm nodes
+ *
+ * @author ab
+ */
+package btools.router;
+
+import java.io.IOException;
+
+import btools.mapaccess.OsmLink;
+import btools.mapaccess.OsmLinkHolder;
+import btools.mapaccess.OsmNode;
+import btools.mapaccess.OsmTransferNode;
+import btools.mapaccess.TurnRestriction;
+import btools.util.CheapRuler;
+
+abstract class OsmPath implements OsmLinkHolder {
+ /**
+ * The cost of that path (a modified distance)
+ */
+ public int cost = 0;
+
+ // the elevation assumed for that path can have a value
+ // if the corresponding node has not
+ public short selev;
+
+ public int airdistance = 0; // distance to endpos
+
+ protected OsmNode sourceNode;
+ protected OsmNode targetNode;
+
+ protected OsmLink link;
+ public OsmPathElement originElement;
+ public OsmPathElement myElement;
+
+ protected float traffic;
+
+ private OsmLinkHolder nextForLink = null;
+
+ public int treedepth = 0;
+
+ // the position of the waypoint just before
+ // this path position (for angle calculation)
+ public int originLon;
+ public int originLat;
+
+ // the classifier of the segment just before this paths position
+ protected float lastClassifier;
+ protected float lastInitialCost;
+
+ protected int priorityclassifier;
+
+ private static final int PATH_START_BIT = 1;
+ private static final int CAN_LEAVE_DESTINATION_BIT = 2;
+ private static final int IS_ON_DESTINATION_BIT = 4;
+ private static final int HAD_DESTINATION_START_BIT = 8;
+ protected int bitfield = PATH_START_BIT;
+
+ private boolean getBit(int mask) {
+ return (bitfield & mask) != 0;
+ }
+
+ private void setBit(int mask, boolean bit) {
+ if (getBit(mask) != bit) {
+ bitfield ^= mask;
+ }
+ }
+
+ public boolean didEnterDestinationArea() {
+ return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
+ }
+
+ public MessageData message;
+
+ public void unregisterUpTree(RoutingContext rc) {
+ try {
+ OsmPathElement pe = originElement;
+ while (pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic) pe).unregister(rc)) {
+ pe = pe.origin;
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ public void registerUpTree() {
+ if (originElement instanceof OsmPathElementWithTraffic) {
+ OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) originElement;
+ ot.register();
+ ot.addTraffic(traffic);
+ }
+ }
+
+ public void init(OsmLink link) {
+ this.link = link;
+ targetNode = link.getTarget(null);
+ selev = targetNode.getSElev();
+
+ originLon = -1;
+ originLat = -1;
+ }
+
+ public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
+ if (origin.myElement == null) {
+ origin.myElement = OsmPathElement.create(origin, rc.countTraffic);
+ }
+ this.originElement = origin.myElement;
+ this.link = link;
+ this.sourceNode = origin.targetNode;
+ this.targetNode = link.getTarget(sourceNode);
+ this.cost = origin.cost;
+ this.lastClassifier = origin.lastClassifier;
+ this.lastInitialCost = origin.lastInitialCost;
+ this.bitfield = origin.bitfield;
+ init(origin);
+ addAddionalPenalty(refTrack, detailMode, origin, link, rc);
+ }
+
+ protected abstract void init(OsmPath orig);
+
+ protected abstract void resetState();
+
+
+ protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
+ byte[] description = link.descriptionBitmap;
+ if (description == null) {
+ return; // could be a beeline path
+ }
+
+ boolean recordTransferNodes = detailMode || rc.countTraffic;
+
+ rc.nogoCost = 0.;
+
+ // extract the 3 positions of the first section
+ int lon0 = origin.originLon;
+ int lat0 = origin.originLat;
+
+ int lon1 = sourceNode.getILon();
+ int lat1 = sourceNode.getILat();
+ short ele1 = origin.selev;
+
+ int linkdisttotal = 0;
+
+ message = detailMode ? new MessageData() : null;
+
+ boolean isReverse = link.isReverse(sourceNode);
+
+ // evaluate the way tags
+ rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
+
+
+ // calculate the costfactor inputs
+ float costfactor = rc.expctxWay.getCostfactor();
+ boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
+ int lastpriorityclassifier = priorityclassifier;
+ priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
+
+ // *** add initial cost if the classifier changed
+ float newClassifier = rc.expctxWay.getInitialClassifier();
+ float newInitialCost = rc.expctxWay.getInitialcost();
+ float classifierDiff = newClassifier - lastClassifier;
+ if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
+ float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
+ if (initialcost >= 1000000.) {
+ cost = -1;
+ return;
+ }
+
+ int iicost = (int) initialcost;
+ if (message != null) {
+ message.linkinitcost += iicost;
+ }
+ cost += iicost;
+ }
+ lastClassifier = newClassifier;
+ lastInitialCost = newInitialCost;
+
+ // *** destination logic: no destination access in between
+ int classifiermask = (int) rc.expctxWay.getClassifierMask();
+ boolean newDestination = (classifiermask & 64) != 0;
+ boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
+ if (getBit(PATH_START_BIT)) {
+ setBit(PATH_START_BIT, false);
+ setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
+ setBit(HAD_DESTINATION_START_BIT, newDestination);
+ } else {
+ if (oldDestination && !newDestination) {
+ if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
+ setBit(CAN_LEAVE_DESTINATION_BIT, false);
+ } else {
+ cost = -1;
+ return;
+ }
+ }
+ }
+ setBit(IS_ON_DESTINATION_BIT, newDestination);
+
+
+ OsmTransferNode transferNode = link.geometry == null ? null
+ : rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
+
+ for (int nsection = 0; ; nsection++) {
+
+ originLon = lon1;
+ originLat = lat1;
+
+ int lon2;
+ int lat2;
+ short ele2;
+
+ if (transferNode == null) {
+ lon2 = targetNode.ilon;
+ lat2 = targetNode.ilat;
+ ele2 = targetNode.selev;
+ } else {
+ lon2 = transferNode.ilon;
+ lat2 = transferNode.ilat;
+ ele2 = transferNode.selev;
+ }
+
+ boolean isStartpoint = lon0 == -1 && lat0 == -1;
+
+ // check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
+ if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
+ if (rc.inverseDirection
+ ? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
+ : TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
+ cost = -1;
+ return;
+ }
+ }
+
+ // if recording, new MessageData for each section (needed for turn-instructions)
+ if (message != null && message.wayKeyValues != null) {
+ originElement.message = message;
+ message = new MessageData();
+ }
+
+ int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
+
+ boolean stopAtEndpoint = false;
+ if (rc.shortestmatch) {
+ if (rc.isEndpoint) {
+ stopAtEndpoint = true;
+ ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
+ } else {
+ // we just start here, reset everything
+ cost = 0;
+ resetState();
+ lon0 = -1; // reset turncost-pipe
+ lat0 = -1;
+ isStartpoint = true;
+
+ if (recordTransferNodes) {
+ if (rc.wayfraction > 0.) {
+ ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
+ originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic);
+ } else {
+ originElement = null; // prevent duplicate point
+ }
+ }
+
+ if (rc.checkPendingEndpoint()) {
+ dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
+ if (rc.shortestmatch) {
+ stopAtEndpoint = true;
+ ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
+ }
+ }
+ }
+ }
+
+ if (message != null) {
+ message.linkdist += dist;
+ }
+ linkdisttotal += dist;
+
+ // apply a start-direction if appropriate (by faking the origin position)
+ if (isStartpoint) {
+ if (rc.startDirectionValid) {
+ double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
+ double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
+ lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
+ lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
+ } else {
+ lon0 = lon1 - (lon2 - lon1);
+ lat0 = lat1 - (lat2 - lat1);
+ }
+ }
+ double angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
+ double cosangle = rc.anglemeter.getCosAngle();
+
+ // *** elevation stuff
+ double delta_h = 0.;
+ if (ele2 == Short.MIN_VALUE) ele2 = ele1;
+ if (ele1 != Short.MIN_VALUE) {
+ delta_h = (ele2 - ele1) / 4.;
+ if (rc.inverseDirection) {
+ delta_h = -delta_h;
+ }
+ }
+
+
+ double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
+
+ double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
+ if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
+ cost = -1;
+ return;
+ }
+
+ if (isTrafficBackbone) {
+ sectionCost = 0.;
+ }
+
+ cost += (int) sectionCost;
+
+ // calculate traffic
+ if (rc.countTraffic) {
+ int minDist = (int) rc.trafficSourceMinDist;
+ int cost2 = cost < minDist ? minDist : cost;
+ traffic += dist * rc.expctxWay.getTrafficSourceDensity() * Math.pow(cost2 / 10000.f, rc.trafficSourceExponent);
+ }
+
+ // compute kinematic
+ computeKinematic(rc, dist, delta_h, detailMode);
+
+ if (message != null) {
+ message.turnangle = (float) angle;
+ message.time = (float) getTotalTime();
+ message.energy = (float) getTotalEnergy();
+ message.priorityclassifier = priorityclassifier;
+ message.classifiermask = classifiermask;
+ message.lon = lon2;
+ message.lat = lat2;
+ message.ele = ele2;
+ message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
+ }
+
+ if (stopAtEndpoint) {
+ if (recordTransferNodes) {
+ originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic);
+ originElement.cost = cost;
+ if (message != null) {
+ originElement.message = message;
+ }
+ }
+ if (rc.nogoCost < 0) {
+ cost = -1;
+ } else {
+ cost += rc.nogoCost;
+ }
+ return;
+ }
+
+ if (transferNode == null) {
+ // *** penalty for being part of the reference track
+ if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
+ int reftrackcost = linkdisttotal;
+ cost += reftrackcost;
+ }
+ selev = ele2;
+ break;
+ }
+ transferNode = transferNode.next;
+
+ if (recordTransferNodes) {
+ originElement = OsmPathElement.create(lon2, lat2, ele2, originElement, rc.countTraffic);
+ originElement.cost = cost;
+ originElement.addTraffic(traffic);
+ traffic = 0;
+ }
+ lon0 = lon1;
+ lat0 = lat1;
+ lon1 = lon2;
+ lat1 = lat2;
+ ele1 = ele2;
+ }
+
+ // check for nogo-matches (after the *actual* start of segment)
+ if (rc.nogoCost < 0) {
+ cost = -1;
+ return;
+ } else {
+ cost += rc.nogoCost;
+ }
+
+ // add target-node costs
+ double targetCost = processTargetNode(rc);
+ if (targetCost < 0. || targetCost + cost >= 2000000000.) {
+ cost = -1;
+ return;
+ }
+ cost += (int) targetCost;
+ }
+
+
+ public short interpolateEle(short e1, short e2, double fraction) {
+ if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
+ return Short.MIN_VALUE;
+ }
+ return (short) (e1 * (1. - fraction) + e2 * fraction);
+ }
+
+ protected abstract double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier);
+
+ protected abstract double processTargetNode(RoutingContext rc);
+
+ protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
+ }
+
+ public abstract int elevationCorrection(RoutingContext rc);
+
+ public abstract boolean definitlyWorseThan(OsmPath p, RoutingContext rc);
+
+ public OsmNode getSourceNode() {
+ return sourceNode;
+ }
+
+ public OsmNode getTargetNode() {
+ return targetNode;
+ }
+
+ public OsmLink getLink() {
+ return link;
+ }
+
+
+ public void setNextForLink(OsmLinkHolder holder) {
+ nextForLink = holder;
+ }
+
+ public OsmLinkHolder getNextForLink() {
+ return nextForLink;
+ }
+
+ public double getTotalTime() {
+ return 0.;
+ }
+
+ public double getTotalEnergy() {
+ return 0.;
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/OsmPathElement.java b/brouter-core/src/main/java/btools/router/OsmPathElement.java
index 0077543..c89f5e3 100644
--- a/brouter-core/src/main/java/btools/router/OsmPathElement.java
+++ b/brouter-core/src/main/java/btools/router/OsmPathElement.java
@@ -1,136 +1,116 @@
-package btools.router;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-
-import btools.mapaccess.OsmNode;
-import btools.mapaccess.OsmPos;
-import btools.util.CheapRuler;
-
-/**
- * Container for link between two Osm nodes
- *
- * @author ab
- */
-
-public class OsmPathElement implements OsmPos
-{
- private int ilat; // latitude
- private int ilon; // longitude
- private short selev; // longitude
-
- public MessageData message = null; // description
-
- public int cost;
-
- // interface OsmPos
- public final int getILat()
- {
- return ilat;
- }
-
- public final int getILon()
- {
- return ilon;
- }
-
- public final short getSElev()
- {
- return selev;
- }
-
- public final double getElev()
- {
- return selev / 4.;
- }
-
- public final float getTime()
- {
- return message == null ? 0.f : message.time;
- }
-
- public final void setTime( float t )
- {
- if ( message != null )
- {
- message.time = t;
- }
- }
-
- public final float getEnergy()
- {
- return message == null ? 0.f : message.energy;
- }
-
- public final void setEnergy( float e )
- {
- if ( message != null )
- {
- message.energy = e;
- }
- }
-
- public final long getIdFromPos()
- {
- return ((long)ilon)<<32 | ilat;
- }
-
- public final int calcDistance( OsmPos p )
- {
- return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
- }
-
- public OsmPathElement origin;
-
- // construct a path element from a path
- public static final OsmPathElement create( OsmPath path, boolean countTraffic )
- {
- OsmNode n = path.getTargetNode();
- OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
- pe.cost = path.cost;
- pe.message = path.message;
- return pe;
- }
-
- public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
- {
- OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
- pe.ilon = ilon;
- pe.ilat = ilat;
- pe.selev = selev;
- pe.origin = origin;
- return pe;
- }
-
- protected OsmPathElement()
- {
- }
-
- public void addTraffic( float traffic )
- {
- }
-
- public String toString()
- {
- return ilon + "_" + ilat;
- }
-
- public void writeToStream( DataOutput dos ) throws IOException
- {
- dos.writeInt( ilat );
- dos.writeInt( ilon );
- dos.writeShort( selev );
- dos.writeInt( cost );
- }
-
- public static OsmPathElement readFromStream( DataInput dis ) throws IOException
- {
- OsmPathElement pe = new OsmPathElement();
- pe.ilat = dis.readInt();
- pe.ilon = dis.readInt();
- pe.selev = dis.readShort();
- pe.cost = dis.readInt();
- return pe;
- }
-}
+package btools.router;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import btools.mapaccess.OsmNode;
+import btools.mapaccess.OsmPos;
+import btools.util.CheapRuler;
+
+/**
+ * Container for link between two Osm nodes
+ *
+ * @author ab
+ */
+
+public class OsmPathElement implements OsmPos {
+ private int ilat; // latitude
+ private int ilon; // longitude
+ private short selev; // longitude
+
+ public MessageData message = null; // description
+
+ public int cost;
+
+ // interface OsmPos
+ public final int getILat() {
+ return ilat;
+ }
+
+ public final int getILon() {
+ return ilon;
+ }
+
+ public final short getSElev() {
+ return selev;
+ }
+
+ public final double getElev() {
+ return selev / 4.;
+ }
+
+ public final float getTime() {
+ return message == null ? 0.f : message.time;
+ }
+
+ public final void setTime(float t) {
+ if (message != null) {
+ message.time = t;
+ }
+ }
+
+ public final float getEnergy() {
+ return message == null ? 0.f : message.energy;
+ }
+
+ public final void setEnergy(float e) {
+ if (message != null) {
+ message.energy = e;
+ }
+ }
+
+ public final long getIdFromPos() {
+ return ((long) ilon) << 32 | ilat;
+ }
+
+ public final int calcDistance(OsmPos p) {
+ return (int) (CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0);
+ }
+
+ public OsmPathElement origin;
+
+ // construct a path element from a path
+ public static final OsmPathElement create(OsmPath path, boolean countTraffic) {
+ OsmNode n = path.getTargetNode();
+ OsmPathElement pe = create(n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic);
+ pe.cost = path.cost;
+ pe.message = path.message;
+ return pe;
+ }
+
+ public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic) {
+ OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
+ pe.ilon = ilon;
+ pe.ilat = ilat;
+ pe.selev = selev;
+ pe.origin = origin;
+ return pe;
+ }
+
+ protected OsmPathElement() {
+ }
+
+ public void addTraffic(float traffic) {
+ }
+
+ public String toString() {
+ return ilon + "_" + ilat;
+ }
+
+ public void writeToStream(DataOutput dos) throws IOException {
+ dos.writeInt(ilat);
+ dos.writeInt(ilon);
+ dos.writeShort(selev);
+ dos.writeInt(cost);
+ }
+
+ public static OsmPathElement readFromStream(DataInput dis) throws IOException {
+ OsmPathElement pe = new OsmPathElement();
+ pe.ilat = dis.readInt();
+ pe.ilon = dis.readInt();
+ pe.selev = dis.readShort();
+ pe.cost = dis.readInt();
+ return pe;
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java b/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java
index aa8cb5a..18496f4 100644
--- a/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java
+++ b/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java
@@ -9,19 +9,15 @@ import java.io.IOException;
* @author ab
*/
-public final class OsmPathElementWithTraffic extends OsmPathElement
-{
+public final class OsmPathElementWithTraffic extends OsmPathElement {
private int registerCount;
private float farTraffic;
private float nearTraffic;
-
- public void register()
- {
- if ( registerCount++ == 0 )
- {
- if ( origin instanceof OsmPathElementWithTraffic )
- {
- OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
+
+ public void register() {
+ if (registerCount++ == 0) {
+ if (origin instanceof OsmPathElementWithTraffic) {
+ OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin;
ot.register();
ot.farTraffic += farTraffic;
ot.nearTraffic += nearTraffic;
@@ -30,42 +26,36 @@ public final class OsmPathElementWithTraffic extends OsmPathElement
}
}
}
-
+
@Override
- public void addTraffic( float traffic )
- {
+ public void addTraffic(float traffic) {
this.farTraffic += traffic;
this.nearTraffic += traffic;
}
// unregister from origin if our registercount is 0, else do nothing
-public static double maxtraffic = 0.;
+ public static double maxtraffic = 0.;
- public boolean unregister( RoutingContext rc ) throws IOException
- {
- if ( --registerCount == 0 )
- {
- if ( origin instanceof OsmPathElementWithTraffic )
- {
- OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
-
- int costdelta = cost-ot.cost;
- ot.farTraffic += farTraffic*Math.exp(-costdelta/rc.farTrafficDecayLength);
- ot.nearTraffic += nearTraffic*Math.exp(-costdelta/rc.nearTrafficDecayLength);
+ public boolean unregister(RoutingContext rc) throws IOException {
+ if (--registerCount == 0) {
+ if (origin instanceof OsmPathElementWithTraffic) {
+ OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin;
-if ( costdelta > 0 && farTraffic > maxtraffic ) maxtraffic = farTraffic;
-
- int t2 = cost == ot.cost ? -1 : (int)(rc.farTrafficWeight*farTraffic + rc.nearTrafficWeight*nearTraffic);
-
- if ( t2 > 4000 || t2 == -1 )
- {
+ int costdelta = cost - ot.cost;
+ ot.farTraffic += farTraffic * Math.exp(-costdelta / rc.farTrafficDecayLength);
+ ot.nearTraffic += nearTraffic * Math.exp(-costdelta / rc.nearTrafficDecayLength);
+
+ if (costdelta > 0 && farTraffic > maxtraffic) maxtraffic = farTraffic;
+
+ int t2 = cost == ot.cost ? -1 : (int) (rc.farTrafficWeight * farTraffic + rc.nearTrafficWeight * nearTraffic);
+
+ if (t2 > 4000 || t2 == -1) {
// System.out.println( "unregistered: " + this + " origin=" + ot + " farTraffic =" + farTraffic + " nearTraffic =" + nearTraffic + " cost=" + cost );
- if ( rc.trafficOutputStream != null )
- {
- rc.trafficOutputStream.writeLong( getIdFromPos());
- rc.trafficOutputStream.writeLong( ot.getIdFromPos());
- rc.trafficOutputStream.writeInt( t2 );
+ if (rc.trafficOutputStream != null) {
+ rc.trafficOutputStream.writeLong(getIdFromPos());
+ rc.trafficOutputStream.writeLong(ot.getIdFromPos());
+ rc.trafficOutputStream.writeInt(t2);
}
}
farTraffic = 0;
diff --git a/brouter-core/src/main/java/btools/router/OsmPathModel.java b/brouter-core/src/main/java/btools/router/OsmPathModel.java
index 63b21b3..d0b5a5f 100644
--- a/brouter-core/src/main/java/btools/router/OsmPathModel.java
+++ b/brouter-core/src/main/java/btools/router/OsmPathModel.java
@@ -11,11 +11,10 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
-abstract class OsmPathModel
-{
+abstract class OsmPathModel {
public abstract OsmPrePath createPrePath();
public abstract OsmPath createPath();
- public abstract void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map keyValues );
+ public abstract void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map keyValues);
}
diff --git a/brouter-core/src/main/java/btools/router/OsmPrePath.java b/brouter-core/src/main/java/btools/router/OsmPrePath.java
index 266cf58..4131d11 100644
--- a/brouter-core/src/main/java/btools/router/OsmPrePath.java
+++ b/brouter-core/src/main/java/btools/router/OsmPrePath.java
@@ -9,21 +9,19 @@ import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
-public abstract class OsmPrePath
-{
+public abstract class OsmPrePath {
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
-
+
public OsmPrePath next;
- public void init( OsmPath origin, OsmLink link, RoutingContext rc )
- {
+ public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
this.link = link;
this.sourceNode = origin.getTargetNode();
- this.targetNode = link.getTarget( sourceNode );
- initPrePath(origin, rc );
+ this.targetNode = link.getTarget(sourceNode);
+ initPrePath(origin, rc);
}
- protected abstract void initPrePath(OsmPath origin, RoutingContext rc );
+ protected abstract void initPrePath(OsmPath origin, RoutingContext rc);
}
diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java
index b094551..0ac5e2e 100644
--- a/brouter-core/src/main/java/btools/router/OsmTrack.java
+++ b/brouter-core/src/main/java/btools/router/OsmTrack.java
@@ -1,1150 +1,1005 @@
-/**
- * Container for a track
- *
- * @author ab
- */
-package btools.router;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-import btools.mapaccess.MatchedWaypoint;
-import btools.mapaccess.OsmPos;
-import btools.util.CompactLongMap;
-import btools.util.FrozenLongMap;
-import btools.util.StringUtils;
-
-public final class OsmTrack
-{
- final public static String version = "1.6.3";
- final public static String versionDate = "21122021";
-
- // csv-header-line
- private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
-
- public MatchedWaypoint endPoint;
- public long[] nogoChecksums;
- public long profileTimestamp;
- public boolean isDirty;
-
- public boolean showspeed;
- public boolean showSpeedProfile;
-
- public List pois = new ArrayList();
-
- private static class OsmPathElementHolder
- {
- public OsmPathElement node;
- public OsmPathElementHolder nextHolder;
- }
-
- public ArrayList nodes = new ArrayList();
-
- private CompactLongMap nodesMap;
-
- private CompactLongMap detourMap;
-
- private VoiceHintList voiceHints;
-
- public String message = null;
- public ArrayList messageList = null;
-
- public String name = "unset";
-
- protected List matchedWaypoints;
- public boolean exportWaypoints = false;
-
- public void addNode( OsmPathElement node )
- {
- nodes.add( 0, node );
- }
-
- public void registerDetourForId( long id, OsmPathElement detour )
- {
- if ( detourMap == null )
- {
- detourMap = new CompactLongMap();
- }
- OsmPathElementHolder nh = new OsmPathElementHolder();
- nh.node = detour;
- OsmPathElementHolder h = detourMap.get( id );
- if ( h != null )
- {
- while ( h.nextHolder != null )
- {
- h = h.nextHolder;
- }
- h.nextHolder = nh;
- }
- else
- {
- detourMap.fastPut( id, nh );
- }
- }
-
- public void copyDetours( OsmTrack source )
- {
- detourMap = source.detourMap == null ? null : new FrozenLongMap( source.detourMap );
- }
-
- public void buildMap()
- {
- nodesMap = new CompactLongMap();
- for ( OsmPathElement node : nodes )
- {
- long id = node.getIdFromPos();
- OsmPathElementHolder nh = new OsmPathElementHolder();
- nh.node = node;
- OsmPathElementHolder h = nodesMap.get( id );
- if ( h != null )
- {
- while (h.nextHolder != null)
- {
- h = h.nextHolder;
- }
- h.nextHolder = nh;
- }
- else
- {
- nodesMap.fastPut( id, nh );
- }
- }
- nodesMap = new FrozenLongMap( nodesMap );
- }
-
- private ArrayList aggregateMessages()
- {
- ArrayList res = new ArrayList();
- MessageData current = null;
- for ( OsmPathElement n : nodes )
- {
- if ( n.message != null && n.message.wayKeyValues != null )
- {
- MessageData md = n.message.copy();
- if ( current != null )
- {
- if ( current.nodeKeyValues != null || !current.wayKeyValues.equals( md.wayKeyValues ) )
- {
- res.add( current.toMessage() );
- }
- else
- {
- md.add( current );
- }
- }
- current = md;
- }
- }
- if ( current != null )
- {
- res.add( current.toMessage() );
- }
- return res;
- }
-
- private ArrayList aggregateSpeedProfile()
- {
- ArrayList res = new ArrayList();
- int vmax = -1;
- int vmaxe = -1;
- int vmin = -1;
- int extraTime = 0;
- for( int i = nodes.size()-1; i > 0; i-- )
- {
- OsmPathElement n = nodes.get(i);
- MessageData m = n.message;
- int vnode = getVNode( i );
- if ( m != null && ( vmax != m.vmax || vmin != m.vmin || vmaxe != m.vmaxExplicit || vnode < m.vmax || extraTime != m.extraTime ) )
- {
- vmax = m.vmax;
- vmin = m.vmin;
- vmaxe = m.vmaxExplicit;
- extraTime = m.extraTime;
- res.add( i + "," + vmaxe + "," + vmax + "," + vmin + "," + vnode + "," + extraTime );
- }
- }
- return res;
- }
-
-
- /**
- * writes the track in binary-format to a file
- *
- * @param filename
- * the filename to write to
- */
- public void writeBinary( String filename ) throws Exception
- {
- DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( filename ) ) );
-
- endPoint.writeToStream( dos );
- dos.writeInt( nodes.size() );
- for ( OsmPathElement node : nodes )
- {
- node.writeToStream( dos );
- }
- dos.writeLong( nogoChecksums[0] );
- dos.writeLong( nogoChecksums[1] );
- dos.writeLong( nogoChecksums[2] );
- dos.writeBoolean( isDirty );
- dos.writeLong( profileTimestamp );
- dos.close();
- }
-
- public static OsmTrack readBinary( String filename, OsmNodeNamed newEp, long[] nogoChecksums, long profileChecksum, StringBuilder debugInfo )
- {
- OsmTrack t = null;
- if ( filename != null )
- {
- File f = new File( filename );
- if ( f.exists() )
- {
- try
- {
- DataInputStream dis = new DataInputStream( new BufferedInputStream( new FileInputStream( f ) ) );
- MatchedWaypoint ep = MatchedWaypoint.readFromStream( dis );
- int dlon = ep.waypoint.ilon - newEp.ilon;
- int dlat = ep.waypoint.ilat - newEp.ilat;
- boolean targetMatch = dlon < 20 && dlon > -20 && dlat < 20 && dlat > -20;
- if ( debugInfo != null )
- {
- debugInfo.append( "target-delta = " + dlon + "/" + dlat + " targetMatch=" + targetMatch );
- }
- if ( targetMatch )
- {
- t = new OsmTrack();
- t.endPoint = ep;
- int n = dis.readInt();
- OsmPathElement last_pe = null;
- for ( int i = 0; i < n; i++ )
- {
- OsmPathElement pe = OsmPathElement.readFromStream( dis );
- pe.origin = last_pe;
- last_pe = pe;
- t.nodes.add( pe );
- }
- t.cost = last_pe.cost;
- t.buildMap();
-
- // check cheecksums, too
- long[] al = new long[3];
- long pchecksum = 0;
- try
- {
- al[0] = dis.readLong();
- al[1] = dis.readLong();
- al[2] = dis.readLong();
- }
- catch (EOFException eof) { /* kind of expected */ }
- try
- {
- t.isDirty = dis.readBoolean();
- }
- catch (EOFException eof) { /* kind of expected */ }
- try
- {
- pchecksum = dis.readLong();
- }
- catch (EOFException eof) { /* kind of expected */ }
- boolean nogoCheckOk = Math.abs( al[0] - nogoChecksums[0] ) <= 20
- && Math.abs( al[1] - nogoChecksums[1] ) <= 20
- && Math.abs( al[2] - nogoChecksums[2] ) <= 20;
- boolean profileCheckOk = pchecksum == profileChecksum;
-
- if ( debugInfo != null )
- {
- debugInfo.append( " nogoCheckOk=" + nogoCheckOk + " profileCheckOk=" + profileCheckOk );
- debugInfo.append( " al=" + formatLongs(al) + " nogoChecksums=" + formatLongs(nogoChecksums) );
- }
- if ( !(nogoCheckOk && profileCheckOk) ) return null;
- }
- dis.close();
- }
- catch (Exception e)
- {
- throw new RuntimeException( "Exception reading rawTrack: " + e );
- }
- }
- }
- return t;
- }
-
- private static String formatLongs( long[] al )
- {
- StringBuilder sb = new StringBuilder();
- sb.append( '{' );
- for( long l : al )
- {
- sb.append( l );
- sb.append( ' ' );
- }
- sb.append( '}' );
- return sb.toString();
- }
-
-
- public void addNodes( OsmTrack t )
- {
- for ( OsmPathElement n : t.nodes )
- addNode( n );
- buildMap();
- }
-
- public boolean containsNode( OsmPos node )
- {
- return nodesMap.contains( node.getIdFromPos() );
- }
-
- public OsmPathElement getLink( long n1, long n2 )
- {
- OsmPathElementHolder h = nodesMap.get( n2 );
- while (h != null)
- {
- OsmPathElement e1 = h.node.origin;
- if ( e1 != null && e1.getIdFromPos() == n1 )
- {
- return h.node;
- }
- h = h.nextHolder;
- }
- return null;
- }
-
- public void appendTrack( OsmTrack t )
- {
- int ourSize = nodes.size();
- float t0 = ourSize > 0 ? nodes.get(ourSize - 1 ).getTime() : 0;
- float e0 = ourSize > 0 ? nodes.get(ourSize - 1 ).getEnergy() : 0;
- for ( int i = 0; i < t.nodes.size(); i++ )
- {
- if ( i > 0 || ourSize == 0 )
- {
- OsmPathElement e = t.nodes.get( i );
- e.setTime( e.getTime() + t0 );
- e.setEnergy( e.getEnergy() + e0 );
- nodes.add( e );
- }
- }
-
- if ( t.voiceHints != null )
- {
- if (ourSize > 0){
- for (VoiceHint hint : t.voiceHints.list) {
- hint.indexInTrack = hint.indexInTrack + ourSize -1;
- }
- }
- if ( voiceHints == null )
- {
- voiceHints = t.voiceHints;
- }
- else
- {
- voiceHints.list.addAll( t.voiceHints.list );
- }
- }
-
- distance += t.distance;
- ascend += t.ascend;
- plainAscend += t.plainAscend;
- cost += t.cost;
- energy += t.energy;
-
- showspeed |= t.showspeed;
- showSpeedProfile |= t.showSpeedProfile;
- }
-
- public int distance;
- public int ascend;
- public int plainAscend;
- public int cost;
- public int energy;
-
- /**
- * writes the track in gpx-format to a file
- *
- * @param filename
- * the filename to write to
- */
- public void writeGpx( String filename ) throws Exception
- {
- BufferedWriter bw = new BufferedWriter( new FileWriter( filename ) );
- formatAsGpx( bw );
- bw.close();
- }
-
- public String formatAsGpx()
- {
- try
- {
- StringWriter sw = new StringWriter( 8192 );
- BufferedWriter bw = new BufferedWriter( sw );
- formatAsGpx( bw );
- bw.close();
- return sw.toString();
- }
- catch( Exception e )
- {
- throw new RuntimeException( e );
- }
- }
-
- public String formatAsGpx( BufferedWriter sb ) throws IOException
- {
- int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
-
- sb.append( "\n" );
- for ( int i = messageList.size() - 1; i >= 0; i-- )
- {
- String message = messageList.get( i );
- if ( i < messageList.size() - 1 )
- message = "(alt-index " + i + ": " + message + " )";
- if ( message != null )
- sb.append( "\n" );
- }
-
- if ( turnInstructionMode == 4 ) // comment style
- {
- sb.append( "\n" );
- sb.append( "\n" );
- sb.append( "\n" );
- }
- sb.append( "\n" );
- }
- else
- {
- sb.append( " creator=\"BRouter-" + version + "\" version=\"1.1\">\n" );
- }
-
- if ( turnInstructionMode == 3) // osmand style
- {
- float lastRteTime = 0;
-
- sb.append(" \n");
-
- sb.append(" \n" )
- .append ( " start\n \n");
-
- float rteTime = getVoiceHintTime( 0 );
-
- if ( rteTime != lastRteTime ) // add timing only if available
- {
- double t = rteTime - lastRteTime;
- sb.append( " \n" );
- lastRteTime = rteTime;
- }
- sb.append(" 0\n \n \n");
-
- for( int i = 0 ; i < voiceHints.list.size(); i++ )
- {
- VoiceHint hint = voiceHints.list.get(i);
- sb.append(" \n" )
- .append ( " " ).append( hint.getMessageString() ).append( "\n \n");
-
- rteTime = getVoiceHintTime( i+1 );
-
- if ( rteTime != lastRteTime ) // add timing only if available
- {
- double t = rteTime - lastRteTime;
- sb.append( " \n" );
- lastRteTime = rteTime;
- }
- sb.append(" " ).append( hint.getCommandString() ).append("\n ").append( "" + (int)hint.angle )
- .append("\n ").append( "" + hint.indexInTrack ).append("\n \n \n");
- }
- sb.append(" \n" )
- .append ( " destination\n \n");
- sb.append( " \n" );
- sb.append(" ").append( "" + (nodes.size()-1) ).append("\n \n \n");
-
- sb.append("\n");
- }
-
- if ( turnInstructionMode == 2 ) // locus style
- {
- float lastRteTime = getVoiceHintTime( 0 );
-
- for( int i=0; i" )
- .append( hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "" )
- .append( "" ).append( hint.getMessageString() ).append( "" )
- .append( "" ).append( "" + hint.distanceToNext ).append( "" );
- float rteTime = getVoiceHintTime( i+1 );
- if ( rteTime != lastRteTime ) // add timing only if available
- {
- double t = rteTime - lastRteTime;
- double speed = hint.distanceToNext / t;
- sb.append( "" ).append( "" + t ).append( "" )
- .append( "" ).append( "" + speed ).append( "" );
- lastRteTime = rteTime;
- }
- sb.append( "" ).append( "" + hint.getLocusAction() ).append( "" )
- .append( "\n" );
- }
- }
- if ( turnInstructionMode == 5 ) // gpsies style
- {
- for( VoiceHint hint: voiceHints.list )
- {
- sb.append( " " )
- .append( "" ).append( hint.getMessageString() ).append( "" )
- .append( "" ).append( hint.getSymbolString().toLowerCase() ).append( "" )
- .append( "" ).append( hint.getSymbolString() ).append( "" )
- .append( "\n" );
- }
- }
-
- if ( turnInstructionMode == 6 ) // orux style
- {
- for( VoiceHint hint: voiceHints.list )
- {
- sb.append( " " )
- .append( hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "" )
- .append( "\n" +
- "\n" +
- "" ).append("" + hint.getOruxAction() )
- .append( "\n" +
- "\n" +
- "\n" +
- "" );
- }
- }
-
- for( int i=0; i<=pois.size() - 1; i++ )
- {
- OsmNodeNamed poi = pois.get(i);
- sb.append( " \n" )
- .append( " " ).append( StringUtils.escapeXml10(poi.name) ).append( "\n" )
- .append( " \n" );
- }
-
- if ( exportWaypoints )
- {
- for( int i=0; i<=matchedWaypoints.size() - 1; i++ )
- {
- MatchedWaypoint wt = matchedWaypoints.get(i);
- sb.append( " \n" )
- .append( " " ).append( StringUtils.escapeXml10(wt.name) ).append( "\n" );
- if(i == 0)
- {
- sb.append( " from\n" );
- }
- else if (i == matchedWaypoints.size() - 1)
- {
- sb.append( " to\n" );
- }
- else {
- sb.append( " via\n" );
- }
- sb.append( " \n" );
- }
- }
- sb.append( " \n" );
- sb.append( " " ).append( name ).append( "\n" );
- if ( turnInstructionMode == 1 ) // trkpt/sym style
- {
- sb.append( " " ).append( voiceHints.getTransportMode() ).append( "\n" );
- }
-
- if ( turnInstructionMode == 2 )
- {
- sb.append( " \n" );
- sb.append( " " ).append( "" + voiceHints.getLocusRouteType() ).append( "\n" );
- sb.append( " 1\n" );
- sb.append( " \n" );
- }
-
- sb.append( " \n" );
-
- for ( int idx = 0; idx < nodes.size(); idx++ )
- {
- OsmPathElement n = nodes.get(idx);
- String sele = n.getSElev() == Short.MIN_VALUE ? "" : "" + n.getElev() + "";
- if ( turnInstructionMode == 1 ) // trkpt/sym style
- {
- for ( VoiceHint hint : voiceHints.list )
- {
- if ( hint.indexInTrack == idx )
- {
- sele += "" + hint.getCommandString() + "";
- }
- }
- }
- sb.append( " " ).append( sele ).append( "\n" );
- }
-
- sb.append( " \n" );
- sb.append( " \n" );
- sb.append( "\n" );
-
- return sb.toString();
- }
-
- public void writeKml( String filename ) throws Exception
- {
- BufferedWriter bw = new BufferedWriter( new FileWriter( filename ) );
-
- bw.write( formatAsKml() );
- bw.close();
- }
-
- public String formatAsKml()
- {
- StringBuilder sb = new StringBuilder( 8192 );
-
- sb.append( "\n" );
-
- sb.append( "\n" );
- sb.append( " \n" );
- sb.append( " KML Samples\n" );
- sb.append( " 1\n" );
- sb.append( " 3.497064\n" );
- sb.append( " 872\n" );
- sb.append( " To enable simple instructions add: 'instructions=1' as parameter to the URL\n" );
- sb.append( " \n" );
- sb.append( " Paths\n" );
- sb.append( " 0\n" );
- sb.append( " Examples of paths.\n" );
- sb.append( " \n" );
- sb.append( " Tessellated\n" );
- sb.append( " 0\n" );
- sb.append( " tag has a value of 1, the line will contour to the underlying terrain]]>\n" );
- sb.append( " \n" );
- sb.append( " 1\n" );
- sb.append( " " );
-
- for ( OsmPathElement n : nodes )
- {
- sb.append( formatILon( n.getILon() ) ).append( "," ).append( formatILat( n.getILat() ) ).append( "\n" );
- }
-
- sb.append( " \n" );
- sb.append( " \n" );
- sb.append( " \n" );
- sb.append( " \n" );
- if ( exportWaypoints || !pois.isEmpty() )
- {
- if (!pois.isEmpty()) {
- sb.append( " \n" );
- sb.append(" poi\n");
- for (int i = 0; i < pois.size(); i++) {
- OsmNodeNamed poi = pois.get(i);
- createPlaceMark(sb, poi.name, poi.ilat, poi.ilon);
- }
- sb.append(" \n");
- }
-
- if (exportWaypoints)
- {
- int size = matchedWaypoints.size();
- createFolder(sb, "start", matchedWaypoints.subList(0, 1));
- if (matchedWaypoints.size() > 2) {
- createFolder(sb, "via", matchedWaypoints.subList(1, size - 1));
- }
- createFolder(sb, "end", matchedWaypoints.subList(size - 1, size));
- }
- }
- sb.append( " \n" );
- sb.append( "\n" );
-
- return sb.toString();
- }
-
- private void createFolder(StringBuilder sb, String type, List waypoints) {
- sb.append( " \n" );
- sb.append( " " + type + "\n" );
- for (int i = 0; i < waypoints.size(); i++) {
- MatchedWaypoint wp = waypoints.get(i);
- createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
- }
- sb.append( " \n" );
- }
-
- private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
- sb.append(" \n");
- sb.append(" " + StringUtils.escapeXml10(name) + "\n");
- sb.append(" \n");
- sb.append(" " + formatILon(ilon) + "," + formatILat(ilat) + "\n");
- sb.append(" \n");
- sb.append(" \n");
- }
-
- public List iternity;
-
- public void writeJson( String filename ) throws Exception
- {
- BufferedWriter bw = new BufferedWriter( new FileWriter( filename ) );
-
- bw.write( formatAsGeoJson() );
- bw.close();
- }
-
-
- public String formatAsGeoJson()
- {
- int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
-
- StringBuilder sb = new StringBuilder( 8192 );
-
- sb.append( "{\n" );
- sb.append( " \"type\": \"FeatureCollection\",\n" );
- sb.append( " \"features\": [\n" );
- sb.append( " {\n" );
- sb.append( " \"type\": \"Feature\",\n" );
- sb.append( " \"properties\": {\n" );
- sb.append( " \"creator\": \"BRouter-" + version + "\",\n" );
- sb.append( " \"name\": \"" ).append( name ).append( "\",\n" );
- sb.append( " \"track-length\": \"" ).append( distance ).append( "\",\n" );
- sb.append( " \"filtered ascend\": \"" ).append( ascend ).append( "\",\n" );
- sb.append( " \"plain-ascend\": \"" ).append( plainAscend ).append( "\",\n" );
- sb.append( " \"total-time\": \"" ).append( getTotalSeconds() ).append( "\",\n" );
- sb.append( " \"total-energy\": \"" ).append( energy ).append( "\",\n" );
- sb.append( " \"cost\": \"" ).append( cost ).append( "\",\n" );
- if ( voiceHints != null && !voiceHints.list.isEmpty() )
- {
- sb.append( " \"voicehints\": [\n" );
- for( VoiceHint hint: voiceHints.list )
- {
- sb.append( " [" );
- sb.append( hint.indexInTrack );
- sb.append( ',' ).append( hint.getCommand() );
- sb.append( ',' ).append( hint.getExitNumber() );
- sb.append( ',' ).append( hint.distanceToNext );
- sb.append( ',' ).append( (int) hint.angle );
-
- // not always include geometry because longer and only needed for comment style
- if ( turnInstructionMode == 4 ) // comment style
- {
- sb.append( ",\"" ).append( hint.formatGeometry() ).append( "\"" );
- }
-
- sb.append( "],\n" );
- }
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
- sb.append( " ],\n" );
- }
- if ( showSpeedProfile ) // set in profile
- {
- ArrayList sp = aggregateSpeedProfile();
- if ( sp.size() > 0 )
- {
- sb.append( " \"speedprofile\": [\n" );
- for( int i=sp.size()-1; i>=0; i-- )
- {
- sb.append( " [" ).append( sp.get(i) ).append( i> 0 ? "],\n" : "]\n" );
- }
- sb.append( " ],\n" );
- }
- }
- // ... traditional message list
- {
- sb.append( " \"messages\": [\n" );
- sb.append( " [\"" ).append( MESSAGES_HEADER.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" );
- for ( String m : aggregateMessages() )
- {
- sb.append( " [\"" ).append( m.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" );
- }
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
- sb.append( " ],\n" );
- }
-
- if ( getTotalSeconds() > 0 ) {
- sb.append( " \"times\": [" );
- DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance( Locale.ENGLISH );
- decimalFormat.applyPattern( "0.###" );
- for ( OsmPathElement n : nodes ) {
- sb.append( decimalFormat.format( n.getTime() ) ).append( "," );
- }
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
- sb.append( "]\n" );
- } else {
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
- }
-
- sb.append( " },\n" );
-
- if ( iternity != null )
- {
- sb.append( " \"iternity\": [\n" );
- for ( String s : iternity )
- {
- sb.append( " \"" ).append( s ).append( "\",\n" );
- }
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
- sb.append( " ],\n" );
- }
- sb.append( " \"geometry\": {\n" );
- sb.append( " \"type\": \"LineString\",\n" );
- sb.append( " \"coordinates\": [\n" );
-
- OsmPathElement nn = null;
- for ( OsmPathElement n : nodes )
- {
- String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
- if ( showspeed ) // hack: show speed instead of elevation
- {
- double speed = 0;
- if ( nn != null )
- {
- int dist = n.calcDistance( nn );
- float dt = n.getTime()-nn.getTime();
- if ( dt != 0.f )
- {
- speed = ((3.6f*dist)/dt + 0.5);
- }
- }
- sele = ", " + (((int)(speed*10))/10.f);
- }
- sb.append( " [" ).append( formatILon( n.getILon() ) ).append( ", " ).append( formatILat( n.getILat() ) )
- .append( sele ).append( "],\n" );
- nn = n;
- }
- sb.deleteCharAt( sb.lastIndexOf( "," ) );
-
- sb.append( " ]\n" );
- sb.append( " }\n" );
- if ( exportWaypoints || !pois.isEmpty())
- {
- sb.append( " },\n" );
- for( int i=0; i<=pois.size() - 1; i++ )
- {
- OsmNodeNamed poi = pois.get(i);
- addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon);
- if (i < matchedWaypoints.size() - 1) {
- sb.append(",");
- }
- sb.append( " \n" );
- }
- if (exportWaypoints) {
- for (int i = 0; i <= matchedWaypoints.size() - 1; i++) {
- String type;
- if (i == 0) {
- type = "from";
- } else if (i == matchedWaypoints.size() - 1) {
- type = "to";
- } else {
- type = "via";
- }
-
- MatchedWaypoint wp = matchedWaypoints.get(i);
- addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
- if (i < matchedWaypoints.size() - 1) {
- sb.append(",");
- }
- sb.append(" \n");
- }
- }
- }
- else {
- sb.append( " }\n" );
- }
- sb.append( " ]\n" );
- sb.append( "}\n" );
-
- return sb.toString();
- }
-
- private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) {
- sb.append( " {\n" );
- sb.append( " \"type\": \"Feature\",\n" );
- sb.append( " \"properties\": {\n" );
- sb.append( " \"name\": \"" + StringUtils.escapeJson(name) + "\",\n" );
- sb.append( " \"type\": \"" + type + "\"\n" );
- sb.append( " },\n" );
- sb.append( " \"geometry\": {\n" );
- sb.append( " \"type\": \"Point\",\n" );
- sb.append( " \"coordinates\": [\n" );
- sb.append( " " + formatILon(ilon) + ",\n" );
- sb.append( " " + formatILat(ilat) + "\n" );
- sb.append( " ]\n" );
- sb.append( " }\n" );
- sb.append( " }" );
- }
-
- private int getVNode( int i )
- {
- MessageData m1 = i+1 < nodes.size() ? nodes.get(i+1).message : null;
- MessageData m0 = i < nodes.size() ? nodes.get(i ).message : null;
- int vnode0 = m1 == null ? 999 : m1.vnode0;
- int vnode1 = m0 == null ? 999 : m0.vnode1;
- return vnode0 < vnode1 ? vnode0 : vnode1;
- }
-
- private int getTotalSeconds()
- {
- float s = nodes.size() < 2 ? 0 : nodes.get( nodes.size()-1 ).getTime() - nodes.get( 0 ).getTime();
- return (int)(s + 0.5);
- }
-
- public String getFormattedTime()
- {
- return format1( getTotalSeconds()/60. ) + "m";
- }
-
- public String getFormattedTime2()
- {
- int seconds = (int)(getTotalSeconds() + 0.5);
- int hours = seconds/3600;
- int minutes = (seconds - hours * 3600) / 60;
- seconds = seconds - hours * 3600 - minutes * 60;
- String time = "";
- if (hours != 0)
- time = "" + hours + "h ";
- if (minutes != 0)
- time = time + minutes + "m ";
- if (seconds != 0)
- time = time + seconds + "s";
- return time;
- }
-
- public String getFormattedEnergy()
- {
- return format1( energy/3600000. ) + "kwh";
- }
-
- private static String formatILon( int ilon )
- {
- return formatPos( ilon - 180000000 );
- }
-
- private static String formatILat( int ilat )
- {
- return formatPos( ilat - 90000000 );
- }
-
- private static String formatPos( int p )
- {
- boolean negative = p < 0;
- if ( negative )
- p = -p;
- char[] ac = new char[12];
- int i = 11;
- while (p != 0 || i > 3)
- {
- ac[i--] = (char) ( '0' + ( p % 10 ) );
- p /= 10;
- if ( i == 5 )
- ac[i--] = '.';
- }
- if ( negative )
- ac[i--] = '-';
- return new String( ac, i + 1, 11 - i );
- }
-
- private String format1( double n )
- {
- String s = "" + (long)(n*10 + 0.5);
- int len = s.length();
- return s.substring( 0, len-1 ) + "." + s.charAt( len-1 );
- }
-
- public void dumpMessages( String filename, RoutingContext rc ) throws Exception
- {
- BufferedWriter bw = filename == null ? null : new BufferedWriter( new FileWriter( filename ) );
- writeMessages( bw, rc );
- }
-
- public void writeMessages( BufferedWriter bw, RoutingContext rc ) throws Exception
- {
- dumpLine( bw, MESSAGES_HEADER );
- for ( String m : aggregateMessages() )
- {
- dumpLine( bw, m );
- }
- if ( bw != null )
- bw.close();
- }
-
- private void dumpLine( BufferedWriter bw, String s ) throws Exception
- {
- if ( bw == null )
- {
- System.out.println( s );
- }
- else
- {
- bw.write( s );
- bw.write( "\n" );
- }
- }
-
- public void readGpx( String filename ) throws Exception
- {
- File f = new File( filename );
- if ( !f.exists() )
- return;
- BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream( f ) ) );
-
- for ( ;; )
- {
- String line = br.readLine();
- if ( line == null )
- break;
-
- int idx0 = line.indexOf( "= 0 )
- {
- idx0 += 12;
- int idx1 = line.indexOf( '"', idx0 );
- int ilon = (int) ( ( Double.parseDouble( line.substring( idx0, idx1 ) ) + 180. ) * 1000000. + 0.5 );
- int idx2 = line.indexOf( " lat=\"" );
- if ( idx2 < 0 )
- continue;
- idx2 += 6;
- int idx3 = line.indexOf( '"', idx2 );
- int ilat = (int) ( ( Double.parseDouble( line.substring( idx2, idx3 ) ) + 90. ) * 1000000. + 0.5 );
- nodes.add( OsmPathElement.create( ilon, ilat, (short) 0, null, false ) );
- }
- }
- br.close();
- }
-
- public boolean equalsTrack( OsmTrack t )
- {
- if ( nodes.size() != t.nodes.size() )
- return false;
- for ( int i = 0; i < nodes.size(); i++ )
- {
- OsmPathElement e1 = nodes.get( i );
- OsmPathElement e2 = t.nodes.get( i );
- if ( e1.getILon() != e2.getILon() || e1.getILat() != e2.getILat() )
- return false;
- }
- return true;
- }
-
- public void prepareSpeedProfile( RoutingContext rc )
- {
- // sendSpeedProfile = rc.keyValues != null && rc.keyValues.containsKey( "vmax" );
- }
-
- public void processVoiceHints( RoutingContext rc )
- {
- voiceHints = new VoiceHintList();
- voiceHints.setTransportMode( rc.carMode, rc.bikeMode );
- voiceHints.turnInstructionMode = rc.turnInstructionMode;
-
- if ( detourMap == null )
- {
- return;
- }
- int nodeNr = nodes.size() - 1;
- OsmPathElement node = nodes.get( nodeNr );
- List inputs = new ArrayList();
- while (node != null)
- {
- if ( node.origin != null )
- {
- VoiceHint input = new VoiceHint();
- inputs.add( input );
- input.ilat = node.origin.getILat();
- input.ilon = node.origin.getILon();
- input.selev = node.origin.getSElev();
- input.indexInTrack = --nodeNr;
- input.goodWay = node.message;
- input.oldWay = node.origin.message == null ? node.message : node.origin.message;
-
- OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() );
- if ( detours != null )
- {
- OsmPathElementHolder h = detours;
- while (h != null)
- {
- OsmPathElement e = h.node;
- input.addBadWay( startSection( e, node.origin ) );
- h = h.nextHolder;
- }
- }
- }
- node = node.origin;
- }
-
- VoiceHintProcessor vproc = new VoiceHintProcessor( rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts );
- List results = vproc.process( inputs );
- for( VoiceHint hint : results )
- {
- voiceHints.list.add( hint );
- }
- }
-
- private float getVoiceHintTime( int i )
- {
- if ( voiceHints.list.isEmpty() )
- {
- return 0f;
- }
- if ( i < voiceHints.list.size() )
- {
- return voiceHints.list.get(i).getTime();
- }
- if ( nodes.isEmpty() )
- {
- return 0f;
- }
- return nodes.get(nodes.size() - 1).getTime();
- }
-
-
- private MessageData startSection( OsmPathElement element, OsmPathElement root )
- {
- OsmPathElement e = element;
- int cnt = 0;
- while( e != null && e.origin != null )
- {
- if ( e.origin.getILat() == root.getILat() && e.origin.getILon() == root.getILon() )
- {
- return e.message;
- }
- e = e.origin;
- if ( cnt++ == 1000000 )
- {
- throw new IllegalArgumentException( "ups: " + root + "->" + element );
- }
- }
- return null;
- }
-}
+/**
+ * Container for a track
+ *
+ * @author ab
+ */
+package btools.router;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import btools.mapaccess.MatchedWaypoint;
+import btools.mapaccess.OsmPos;
+import btools.util.CompactLongMap;
+import btools.util.FrozenLongMap;
+import btools.util.StringUtils;
+
+public final class OsmTrack {
+ final public static String version = "1.6.3";
+ final public static String versionDate = "21122021";
+
+ // csv-header-line
+ private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
+
+ public MatchedWaypoint endPoint;
+ public long[] nogoChecksums;
+ public long profileTimestamp;
+ public boolean isDirty;
+
+ public boolean showspeed;
+ public boolean showSpeedProfile;
+
+ public List pois = new ArrayList();
+
+ private static class OsmPathElementHolder {
+ public OsmPathElement node;
+ public OsmPathElementHolder nextHolder;
+ }
+
+ public ArrayList nodes = new ArrayList();
+
+ private CompactLongMap nodesMap;
+
+ private CompactLongMap detourMap;
+
+ private VoiceHintList voiceHints;
+
+ public String message = null;
+ public ArrayList messageList = null;
+
+ public String name = "unset";
+
+ protected List matchedWaypoints;
+ public boolean exportWaypoints = false;
+
+ public void addNode(OsmPathElement node) {
+ nodes.add(0, node);
+ }
+
+ public void registerDetourForId(long id, OsmPathElement detour) {
+ if (detourMap == null) {
+ detourMap = new CompactLongMap();
+ }
+ OsmPathElementHolder nh = new OsmPathElementHolder();
+ nh.node = detour;
+ OsmPathElementHolder h = detourMap.get(id);
+ if (h != null) {
+ while (h.nextHolder != null) {
+ h = h.nextHolder;
+ }
+ h.nextHolder = nh;
+ } else {
+ detourMap.fastPut(id, nh);
+ }
+ }
+
+ public void copyDetours(OsmTrack source) {
+ detourMap = source.detourMap == null ? null : new FrozenLongMap(source.detourMap);
+ }
+
+ public void buildMap() {
+ nodesMap = new CompactLongMap();
+ for (OsmPathElement node : nodes) {
+ long id = node.getIdFromPos();
+ OsmPathElementHolder nh = new OsmPathElementHolder();
+ nh.node = node;
+ OsmPathElementHolder h = nodesMap.get(id);
+ if (h != null) {
+ while (h.nextHolder != null) {
+ h = h.nextHolder;
+ }
+ h.nextHolder = nh;
+ } else {
+ nodesMap.fastPut(id, nh);
+ }
+ }
+ nodesMap = new FrozenLongMap(nodesMap);
+ }
+
+ private ArrayList aggregateMessages() {
+ ArrayList res = new ArrayList();
+ MessageData current = null;
+ for (OsmPathElement n : nodes) {
+ if (n.message != null && n.message.wayKeyValues != null) {
+ MessageData md = n.message.copy();
+ if (current != null) {
+ if (current.nodeKeyValues != null || !current.wayKeyValues.equals(md.wayKeyValues)) {
+ res.add(current.toMessage());
+ } else {
+ md.add(current);
+ }
+ }
+ current = md;
+ }
+ }
+ if (current != null) {
+ res.add(current.toMessage());
+ }
+ return res;
+ }
+
+ private ArrayList aggregateSpeedProfile() {
+ ArrayList res = new ArrayList();
+ int vmax = -1;
+ int vmaxe = -1;
+ int vmin = -1;
+ int extraTime = 0;
+ for (int i = nodes.size() - 1; i > 0; i--) {
+ OsmPathElement n = nodes.get(i);
+ MessageData m = n.message;
+ int vnode = getVNode(i);
+ if (m != null && (vmax != m.vmax || vmin != m.vmin || vmaxe != m.vmaxExplicit || vnode < m.vmax || extraTime != m.extraTime)) {
+ vmax = m.vmax;
+ vmin = m.vmin;
+ vmaxe = m.vmaxExplicit;
+ extraTime = m.extraTime;
+ res.add(i + "," + vmaxe + "," + vmax + "," + vmin + "," + vnode + "," + extraTime);
+ }
+ }
+ return res;
+ }
+
+
+ /**
+ * writes the track in binary-format to a file
+ *
+ * @param filename the filename to write to
+ */
+ public void writeBinary(String filename) throws Exception {
+ DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename)));
+
+ endPoint.writeToStream(dos);
+ dos.writeInt(nodes.size());
+ for (OsmPathElement node : nodes) {
+ node.writeToStream(dos);
+ }
+ dos.writeLong(nogoChecksums[0]);
+ dos.writeLong(nogoChecksums[1]);
+ dos.writeLong(nogoChecksums[2]);
+ dos.writeBoolean(isDirty);
+ dos.writeLong(profileTimestamp);
+ dos.close();
+ }
+
+ public static OsmTrack readBinary(String filename, OsmNodeNamed newEp, long[] nogoChecksums, long profileChecksum, StringBuilder debugInfo) {
+ OsmTrack t = null;
+ if (filename != null) {
+ File f = new File(filename);
+ if (f.exists()) {
+ try {
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
+ MatchedWaypoint ep = MatchedWaypoint.readFromStream(dis);
+ int dlon = ep.waypoint.ilon - newEp.ilon;
+ int dlat = ep.waypoint.ilat - newEp.ilat;
+ boolean targetMatch = dlon < 20 && dlon > -20 && dlat < 20 && dlat > -20;
+ if (debugInfo != null) {
+ debugInfo.append("target-delta = " + dlon + "/" + dlat + " targetMatch=" + targetMatch);
+ }
+ if (targetMatch) {
+ t = new OsmTrack();
+ t.endPoint = ep;
+ int n = dis.readInt();
+ OsmPathElement last_pe = null;
+ for (int i = 0; i < n; i++) {
+ OsmPathElement pe = OsmPathElement.readFromStream(dis);
+ pe.origin = last_pe;
+ last_pe = pe;
+ t.nodes.add(pe);
+ }
+ t.cost = last_pe.cost;
+ t.buildMap();
+
+ // check cheecksums, too
+ long[] al = new long[3];
+ long pchecksum = 0;
+ try {
+ al[0] = dis.readLong();
+ al[1] = dis.readLong();
+ al[2] = dis.readLong();
+ } catch (EOFException eof) { /* kind of expected */ }
+ try {
+ t.isDirty = dis.readBoolean();
+ } catch (EOFException eof) { /* kind of expected */ }
+ try {
+ pchecksum = dis.readLong();
+ } catch (EOFException eof) { /* kind of expected */ }
+ boolean nogoCheckOk = Math.abs(al[0] - nogoChecksums[0]) <= 20
+ && Math.abs(al[1] - nogoChecksums[1]) <= 20
+ && Math.abs(al[2] - nogoChecksums[2]) <= 20;
+ boolean profileCheckOk = pchecksum == profileChecksum;
+
+ if (debugInfo != null) {
+ debugInfo.append(" nogoCheckOk=" + nogoCheckOk + " profileCheckOk=" + profileCheckOk);
+ debugInfo.append(" al=" + formatLongs(al) + " nogoChecksums=" + formatLongs(nogoChecksums));
+ }
+ if (!(nogoCheckOk && profileCheckOk)) return null;
+ }
+ dis.close();
+ } catch (Exception e) {
+ throw new RuntimeException("Exception reading rawTrack: " + e);
+ }
+ }
+ }
+ return t;
+ }
+
+ private static String formatLongs(long[] al) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (long l : al) {
+ sb.append(l);
+ sb.append(' ');
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+
+ public void addNodes(OsmTrack t) {
+ for (OsmPathElement n : t.nodes)
+ addNode(n);
+ buildMap();
+ }
+
+ public boolean containsNode(OsmPos node) {
+ return nodesMap.contains(node.getIdFromPos());
+ }
+
+ public OsmPathElement getLink(long n1, long n2) {
+ OsmPathElementHolder h = nodesMap.get(n2);
+ while (h != null) {
+ OsmPathElement e1 = h.node.origin;
+ if (e1 != null && e1.getIdFromPos() == n1) {
+ return h.node;
+ }
+ h = h.nextHolder;
+ }
+ return null;
+ }
+
+ public void appendTrack(OsmTrack t) {
+ int ourSize = nodes.size();
+ float t0 = ourSize > 0 ? nodes.get(ourSize - 1).getTime() : 0;
+ float e0 = ourSize > 0 ? nodes.get(ourSize - 1).getEnergy() : 0;
+ for (int i = 0; i < t.nodes.size(); i++) {
+ if (i > 0 || ourSize == 0) {
+ OsmPathElement e = t.nodes.get(i);
+ e.setTime(e.getTime() + t0);
+ e.setEnergy(e.getEnergy() + e0);
+ nodes.add(e);
+ }
+ }
+
+ if (t.voiceHints != null) {
+ if (ourSize > 0) {
+ for (VoiceHint hint : t.voiceHints.list) {
+ hint.indexInTrack = hint.indexInTrack + ourSize - 1;
+ }
+ }
+ if (voiceHints == null) {
+ voiceHints = t.voiceHints;
+ } else {
+ voiceHints.list.addAll(t.voiceHints.list);
+ }
+ }
+
+ distance += t.distance;
+ ascend += t.ascend;
+ plainAscend += t.plainAscend;
+ cost += t.cost;
+ energy += t.energy;
+
+ showspeed |= t.showspeed;
+ showSpeedProfile |= t.showSpeedProfile;
+ }
+
+ public int distance;
+ public int ascend;
+ public int plainAscend;
+ public int cost;
+ public int energy;
+
+ /**
+ * writes the track in gpx-format to a file
+ *
+ * @param filename the filename to write to
+ */
+ public void writeGpx(String filename) throws Exception {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
+ formatAsGpx(bw);
+ bw.close();
+ }
+
+ public String formatAsGpx() {
+ try {
+ StringWriter sw = new StringWriter(8192);
+ BufferedWriter bw = new BufferedWriter(sw);
+ formatAsGpx(bw);
+ bw.close();
+ return sw.toString();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String formatAsGpx(BufferedWriter sb) throws IOException {
+ int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
+
+ sb.append("\n");
+ for (int i = messageList.size() - 1; i >= 0; i--) {
+ String message = messageList.get(i);
+ if (i < messageList.size() - 1)
+ message = "(alt-index " + i + ": " + message + " )";
+ if (message != null)
+ sb.append("\n");
+ }
+
+ if (turnInstructionMode == 4) // comment style
+ {
+ sb.append("\n");
+ sb.append("\n");
+ sb.append("\n");
+ }
+ sb.append("\n");
+ } else {
+ sb.append(" creator=\"BRouter-" + version + "\" version=\"1.1\">\n");
+ }
+
+ if (turnInstructionMode == 3) // osmand style
+ {
+ float lastRteTime = 0;
+
+ sb.append(" \n");
+
+ sb.append(" \n")
+ .append(" start\n \n");
+
+ float rteTime = getVoiceHintTime(0);
+
+ if (rteTime != lastRteTime) // add timing only if available
+ {
+ double t = rteTime - lastRteTime;
+ sb.append(" \n");
+ lastRteTime = rteTime;
+ }
+ sb.append(" 0\n \n \n");
+
+ for (int i = 0; i < voiceHints.list.size(); i++) {
+ VoiceHint hint = voiceHints.list.get(i);
+ sb.append(" \n")
+ .append(" ").append(hint.getMessageString()).append("\n \n");
+
+ rteTime = getVoiceHintTime(i + 1);
+
+ if (rteTime != lastRteTime) // add timing only if available
+ {
+ double t = rteTime - lastRteTime;
+ sb.append(" \n");
+ lastRteTime = rteTime;
+ }
+ sb.append(" ").append(hint.getCommandString()).append("\n ").append("" + (int) hint.angle)
+ .append("\n ").append("" + hint.indexInTrack).append("\n \n \n");
+ }
+ sb.append(" \n")
+ .append(" destination\n \n");
+ sb.append(" \n");
+ sb.append(" ").append("" + (nodes.size() - 1)).append("\n \n \n");
+
+ sb.append("\n");
+ }
+
+ if (turnInstructionMode == 2) // locus style
+ {
+ float lastRteTime = getVoiceHintTime(0);
+
+ for (int i = 0; i < voiceHints.list.size(); i++) {
+ VoiceHint hint = voiceHints.list.get(i);
+ sb.append(" ")
+ .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "")
+ .append("").append(hint.getMessageString()).append("")
+ .append("").append("" + hint.distanceToNext).append("");
+ float rteTime = getVoiceHintTime(i + 1);
+ if (rteTime != lastRteTime) // add timing only if available
+ {
+ double t = rteTime - lastRteTime;
+ double speed = hint.distanceToNext / t;
+ sb.append("").append("" + t).append("")
+ .append("").append("" + speed).append("");
+ lastRteTime = rteTime;
+ }
+ sb.append("").append("" + hint.getLocusAction()).append("")
+ .append("\n");
+ }
+ }
+ if (turnInstructionMode == 5) // gpsies style
+ {
+ for (VoiceHint hint : voiceHints.list) {
+ sb.append(" ")
+ .append("").append(hint.getMessageString()).append("")
+ .append("").append(hint.getSymbolString().toLowerCase()).append("")
+ .append("").append(hint.getSymbolString()).append("")
+ .append("\n");
+ }
+ }
+
+ if (turnInstructionMode == 6) // orux style
+ {
+ for (VoiceHint hint : voiceHints.list) {
+ sb.append(" ")
+ .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "")
+ .append("\n" +
+ "\n" +
+ "").append("" + hint.getOruxAction())
+ .append("\n" +
+ "\n" +
+ "\n" +
+ "");
+ }
+ }
+
+ for (int i = 0; i <= pois.size() - 1; i++) {
+ OsmNodeNamed poi = pois.get(i);
+ sb.append(" \n")
+ .append(" ").append(StringUtils.escapeXml10(poi.name)).append("\n")
+ .append(" \n");
+ }
+
+ if (exportWaypoints) {
+ for (int i = 0; i <= matchedWaypoints.size() - 1; i++) {
+ MatchedWaypoint wt = matchedWaypoints.get(i);
+ sb.append(" \n")
+ .append(" ").append(StringUtils.escapeXml10(wt.name)).append("\n");
+ if (i == 0) {
+ sb.append(" from\n");
+ } else if (i == matchedWaypoints.size() - 1) {
+ sb.append(" to\n");
+ } else {
+ sb.append(" via\n");
+ }
+ sb.append(" \n");
+ }
+ }
+ sb.append(" \n");
+ sb.append(" ").append(name).append("\n");
+ if (turnInstructionMode == 1) // trkpt/sym style
+ {
+ sb.append(" ").append(voiceHints.getTransportMode()).append("\n");
+ }
+
+ if (turnInstructionMode == 2) {
+ sb.append(" \n");
+ sb.append(" ").append("" + voiceHints.getLocusRouteType()).append("\n");
+ sb.append(" 1\n");
+ sb.append(" \n");
+ }
+
+ sb.append(" \n");
+
+ for (int idx = 0; idx < nodes.size(); idx++) {
+ OsmPathElement n = nodes.get(idx);
+ String sele = n.getSElev() == Short.MIN_VALUE ? "" : "" + n.getElev() + "";
+ if (turnInstructionMode == 1) // trkpt/sym style
+ {
+ for (VoiceHint hint : voiceHints.list) {
+ if (hint.indexInTrack == idx) {
+ sele += "" + hint.getCommandString() + "";
+ }
+ }
+ }
+ sb.append(" ").append(sele).append("\n");
+ }
+
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append("\n");
+
+ return sb.toString();
+ }
+
+ public void writeKml(String filename) throws Exception {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
+
+ bw.write(formatAsKml());
+ bw.close();
+ }
+
+ public String formatAsKml() {
+ StringBuilder sb = new StringBuilder(8192);
+
+ sb.append("\n");
+
+ sb.append("\n");
+ sb.append(" \n");
+ sb.append(" KML Samples\n");
+ sb.append(" 1\n");
+ sb.append(" 3.497064\n");
+ sb.append(" 872\n");
+ sb.append(" To enable simple instructions add: 'instructions=1' as parameter to the URL\n");
+ sb.append(" \n");
+ sb.append(" Paths\n");
+ sb.append(" 0\n");
+ sb.append(" Examples of paths.\n");
+ sb.append(" \n");
+ sb.append(" Tessellated\n");
+ sb.append(" 0\n");
+ sb.append(" tag has a value of 1, the line will contour to the underlying terrain]]>\n");
+ sb.append(" \n");
+ sb.append(" 1\n");
+ sb.append(" ");
+
+ for (OsmPathElement n : nodes) {
+ sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
+ }
+
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ if (exportWaypoints || !pois.isEmpty()) {
+ if (!pois.isEmpty()) {
+ sb.append(" \n");
+ sb.append(" poi\n");
+ for (int i = 0; i < pois.size(); i++) {
+ OsmNodeNamed poi = pois.get(i);
+ createPlaceMark(sb, poi.name, poi.ilat, poi.ilon);
+ }
+ sb.append(" \n");
+ }
+
+ if (exportWaypoints) {
+ int size = matchedWaypoints.size();
+ createFolder(sb, "start", matchedWaypoints.subList(0, 1));
+ if (matchedWaypoints.size() > 2) {
+ createFolder(sb, "via", matchedWaypoints.subList(1, size - 1));
+ }
+ createFolder(sb, "end", matchedWaypoints.subList(size - 1, size));
+ }
+ }
+ sb.append(" \n");
+ sb.append("\n");
+
+ return sb.toString();
+ }
+
+ private void createFolder(StringBuilder sb, String type, List waypoints) {
+ sb.append(" \n");
+ sb.append(" " + type + "\n");
+ for (int i = 0; i < waypoints.size(); i++) {
+ MatchedWaypoint wp = waypoints.get(i);
+ createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
+ }
+ sb.append(" \n");
+ }
+
+ private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
+ sb.append(" \n");
+ sb.append(" " + StringUtils.escapeXml10(name) + "\n");
+ sb.append(" \n");
+ sb.append(" " + formatILon(ilon) + "," + formatILat(ilat) + "\n");
+ sb.append(" \n");
+ sb.append(" \n");
+ }
+
+ public List iternity;
+
+ public void writeJson(String filename) throws Exception {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
+
+ bw.write(formatAsGeoJson());
+ bw.close();
+ }
+
+
+ public String formatAsGeoJson() {
+ int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
+
+ StringBuilder sb = new StringBuilder(8192);
+
+ sb.append("{\n");
+ sb.append(" \"type\": \"FeatureCollection\",\n");
+ sb.append(" \"features\": [\n");
+ sb.append(" {\n");
+ sb.append(" \"type\": \"Feature\",\n");
+ sb.append(" \"properties\": {\n");
+ sb.append(" \"creator\": \"BRouter-" + version + "\",\n");
+ sb.append(" \"name\": \"").append(name).append("\",\n");
+ sb.append(" \"track-length\": \"").append(distance).append("\",\n");
+ sb.append(" \"filtered ascend\": \"").append(ascend).append("\",\n");
+ sb.append(" \"plain-ascend\": \"").append(plainAscend).append("\",\n");
+ sb.append(" \"total-time\": \"").append(getTotalSeconds()).append("\",\n");
+ sb.append(" \"total-energy\": \"").append(energy).append("\",\n");
+ sb.append(" \"cost\": \"").append(cost).append("\",\n");
+ if (voiceHints != null && !voiceHints.list.isEmpty()) {
+ sb.append(" \"voicehints\": [\n");
+ for (VoiceHint hint : voiceHints.list) {
+ sb.append(" [");
+ sb.append(hint.indexInTrack);
+ sb.append(',').append(hint.getCommand());
+ sb.append(',').append(hint.getExitNumber());
+ sb.append(',').append(hint.distanceToNext);
+ sb.append(',').append((int) hint.angle);
+
+ // not always include geometry because longer and only needed for comment style
+ if (turnInstructionMode == 4) // comment style
+ {
+ sb.append(",\"").append(hint.formatGeometry()).append("\"");
+ }
+
+ sb.append("],\n");
+ }
+ sb.deleteCharAt(sb.lastIndexOf(","));
+ sb.append(" ],\n");
+ }
+ if (showSpeedProfile) // set in profile
+ {
+ ArrayList sp = aggregateSpeedProfile();
+ if (sp.size() > 0) {
+ sb.append(" \"speedprofile\": [\n");
+ for (int i = sp.size() - 1; i >= 0; i--) {
+ sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n");
+ }
+ sb.append(" ],\n");
+ }
+ }
+ // ... traditional message list
+ {
+ sb.append(" \"messages\": [\n");
+ sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n");
+ for (String m : aggregateMessages()) {
+ sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n");
+ }
+ sb.deleteCharAt(sb.lastIndexOf(","));
+ sb.append(" ],\n");
+ }
+
+ if (getTotalSeconds() > 0) {
+ sb.append(" \"times\": [");
+ DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
+ decimalFormat.applyPattern("0.###");
+ for (OsmPathElement n : nodes) {
+ sb.append(decimalFormat.format(n.getTime())).append(",");
+ }
+ sb.deleteCharAt(sb.lastIndexOf(","));
+ sb.append("]\n");
+ } else {
+ sb.deleteCharAt(sb.lastIndexOf(","));
+ }
+
+ sb.append(" },\n");
+
+ if (iternity != null) {
+ sb.append(" \"iternity\": [\n");
+ for (String s : iternity) {
+ sb.append(" \"").append(s).append("\",\n");
+ }
+ sb.deleteCharAt(sb.lastIndexOf(","));
+ sb.append(" ],\n");
+ }
+ sb.append(" \"geometry\": {\n");
+ sb.append(" \"type\": \"LineString\",\n");
+ sb.append(" \"coordinates\": [\n");
+
+ OsmPathElement nn = null;
+ for (OsmPathElement n : nodes) {
+ String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
+ if (showspeed) // hack: show speed instead of elevation
+ {
+ double speed = 0;
+ if (nn != null) {
+ int dist = n.calcDistance(nn);
+ float dt = n.getTime() - nn.getTime();
+ if (dt != 0.f) {
+ speed = ((3.6f * dist) / dt + 0.5);
+ }
+ }
+ sele = ", " + (((int) (speed * 10)) / 10.f);
+ }
+ sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat()))
+ .append(sele).append("],\n");
+ nn = n;
+ }
+ sb.deleteCharAt(sb.lastIndexOf(","));
+
+ sb.append(" ]\n");
+ sb.append(" }\n");
+ if (exportWaypoints || !pois.isEmpty()) {
+ sb.append(" },\n");
+ for (int i = 0; i <= pois.size() - 1; i++) {
+ OsmNodeNamed poi = pois.get(i);
+ addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon);
+ if (i < matchedWaypoints.size() - 1) {
+ sb.append(",");
+ }
+ sb.append(" \n");
+ }
+ if (exportWaypoints) {
+ for (int i = 0; i <= matchedWaypoints.size() - 1; i++) {
+ String type;
+ if (i == 0) {
+ type = "from";
+ } else if (i == matchedWaypoints.size() - 1) {
+ type = "to";
+ } else {
+ type = "via";
+ }
+
+ MatchedWaypoint wp = matchedWaypoints.get(i);
+ addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
+ if (i < matchedWaypoints.size() - 1) {
+ sb.append(",");
+ }
+ sb.append(" \n");
+ }
+ }
+ } else {
+ sb.append(" }\n");
+ }
+ sb.append(" ]\n");
+ sb.append("}\n");
+
+ return sb.toString();
+ }
+
+ private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) {
+ sb.append(" {\n");
+ sb.append(" \"type\": \"Feature\",\n");
+ sb.append(" \"properties\": {\n");
+ sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
+ sb.append(" \"type\": \"" + type + "\"\n");
+ sb.append(" },\n");
+ sb.append(" \"geometry\": {\n");
+ sb.append(" \"type\": \"Point\",\n");
+ sb.append(" \"coordinates\": [\n");
+ sb.append(" " + formatILon(ilon) + ",\n");
+ sb.append(" " + formatILat(ilat) + "\n");
+ sb.append(" ]\n");
+ sb.append(" }\n");
+ sb.append(" }");
+ }
+
+ private int getVNode(int i) {
+ MessageData m1 = i + 1 < nodes.size() ? nodes.get(i + 1).message : null;
+ MessageData m0 = i < nodes.size() ? nodes.get(i).message : null;
+ int vnode0 = m1 == null ? 999 : m1.vnode0;
+ int vnode1 = m0 == null ? 999 : m0.vnode1;
+ return vnode0 < vnode1 ? vnode0 : vnode1;
+ }
+
+ private int getTotalSeconds() {
+ float s = nodes.size() < 2 ? 0 : nodes.get(nodes.size() - 1).getTime() - nodes.get(0).getTime();
+ return (int) (s + 0.5);
+ }
+
+ public String getFormattedTime() {
+ return format1(getTotalSeconds() / 60.) + "m";
+ }
+
+ public String getFormattedTime2() {
+ int seconds = (int) (getTotalSeconds() + 0.5);
+ int hours = seconds / 3600;
+ int minutes = (seconds - hours * 3600) / 60;
+ seconds = seconds - hours * 3600 - minutes * 60;
+ String time = "";
+ if (hours != 0)
+ time = "" + hours + "h ";
+ if (minutes != 0)
+ time = time + minutes + "m ";
+ if (seconds != 0)
+ time = time + seconds + "s";
+ return time;
+ }
+
+ public String getFormattedEnergy() {
+ return format1(energy / 3600000.) + "kwh";
+ }
+
+ private static String formatILon(int ilon) {
+ return formatPos(ilon - 180000000);
+ }
+
+ private static String formatILat(int ilat) {
+ return formatPos(ilat - 90000000);
+ }
+
+ private static String formatPos(int p) {
+ boolean negative = p < 0;
+ if (negative)
+ p = -p;
+ char[] ac = new char[12];
+ int i = 11;
+ while (p != 0 || i > 3) {
+ ac[i--] = (char) ('0' + (p % 10));
+ p /= 10;
+ if (i == 5)
+ ac[i--] = '.';
+ }
+ if (negative)
+ ac[i--] = '-';
+ return new String(ac, i + 1, 11 - i);
+ }
+
+ private String format1(double n) {
+ String s = "" + (long) (n * 10 + 0.5);
+ int len = s.length();
+ return s.substring(0, len - 1) + "." + s.charAt(len - 1);
+ }
+
+ public void dumpMessages(String filename, RoutingContext rc) throws Exception {
+ BufferedWriter bw = filename == null ? null : new BufferedWriter(new FileWriter(filename));
+ writeMessages(bw, rc);
+ }
+
+ public void writeMessages(BufferedWriter bw, RoutingContext rc) throws Exception {
+ dumpLine(bw, MESSAGES_HEADER);
+ for (String m : aggregateMessages()) {
+ dumpLine(bw, m);
+ }
+ if (bw != null)
+ bw.close();
+ }
+
+ private void dumpLine(BufferedWriter bw, String s) throws Exception {
+ if (bw == null) {
+ System.out.println(s);
+ } else {
+ bw.write(s);
+ bw.write("\n");
+ }
+ }
+
+ public void readGpx(String filename) throws Exception {
+ File f = new File(filename);
+ if (!f.exists())
+ return;
+ BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
+
+ for (; ; ) {
+ String line = br.readLine();
+ if (line == null)
+ break;
+
+ int idx0 = line.indexOf("= 0) {
+ idx0 += 12;
+ int idx1 = line.indexOf('"', idx0);
+ int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5);
+ int idx2 = line.indexOf(" lat=\"");
+ if (idx2 < 0)
+ continue;
+ idx2 += 6;
+ int idx3 = line.indexOf('"', idx2);
+ int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5);
+ nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null, false));
+ }
+ }
+ br.close();
+ }
+
+ public boolean equalsTrack(OsmTrack t) {
+ if (nodes.size() != t.nodes.size())
+ return false;
+ for (int i = 0; i < nodes.size(); i++) {
+ OsmPathElement e1 = nodes.get(i);
+ OsmPathElement e2 = t.nodes.get(i);
+ if (e1.getILon() != e2.getILon() || e1.getILat() != e2.getILat())
+ return false;
+ }
+ return true;
+ }
+
+ public void prepareSpeedProfile(RoutingContext rc) {
+ // sendSpeedProfile = rc.keyValues != null && rc.keyValues.containsKey( "vmax" );
+ }
+
+ public void processVoiceHints(RoutingContext rc) {
+ voiceHints = new VoiceHintList();
+ voiceHints.setTransportMode(rc.carMode, rc.bikeMode);
+ voiceHints.turnInstructionMode = rc.turnInstructionMode;
+
+ if (detourMap == null) {
+ return;
+ }
+ int nodeNr = nodes.size() - 1;
+ OsmPathElement node = nodes.get(nodeNr);
+ List inputs = new ArrayList();
+ while (node != null) {
+ if (node.origin != null) {
+ VoiceHint input = new VoiceHint();
+ inputs.add(input);
+ input.ilat = node.origin.getILat();
+ input.ilon = node.origin.getILon();
+ input.selev = node.origin.getSElev();
+ input.indexInTrack = --nodeNr;
+ input.goodWay = node.message;
+ input.oldWay = node.origin.message == null ? node.message : node.origin.message;
+
+ OsmPathElementHolder detours = detourMap.get(node.origin.getIdFromPos());
+ if (detours != null) {
+ OsmPathElementHolder h = detours;
+ while (h != null) {
+ OsmPathElement e = h.node;
+ input.addBadWay(startSection(e, node.origin));
+ h = h.nextHolder;
+ }
+ }
+ }
+ node = node.origin;
+ }
+
+ VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts);
+ List results = vproc.process(inputs);
+ for (VoiceHint hint : results) {
+ voiceHints.list.add(hint);
+ }
+ }
+
+ private float getVoiceHintTime(int i) {
+ if (voiceHints.list.isEmpty()) {
+ return 0f;
+ }
+ if (i < voiceHints.list.size()) {
+ return voiceHints.list.get(i).getTime();
+ }
+ if (nodes.isEmpty()) {
+ return 0f;
+ }
+ return nodes.get(nodes.size() - 1).getTime();
+ }
+
+
+ private MessageData startSection(OsmPathElement element, OsmPathElement root) {
+ OsmPathElement e = element;
+ int cnt = 0;
+ while (e != null && e.origin != null) {
+ if (e.origin.getILat() == root.getILat() && e.origin.getILon() == root.getILon()) {
+ return e.message;
+ }
+ e = e.origin;
+ if (cnt++ == 1000000) {
+ throw new IllegalArgumentException("ups: " + root + "->" + element);
+ }
+ }
+ return null;
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/ProfileCache.java b/brouter-core/src/main/java/btools/router/ProfileCache.java
index 8ff9e2e..34529e4 100644
--- a/brouter-core/src/main/java/btools/router/ProfileCache.java
+++ b/brouter-core/src/main/java/btools/router/ProfileCache.java
@@ -11,146 +11,125 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
-public final class ProfileCache
-{
-
+public final class ProfileCache {
+
private static File lastLookupFile;
private static long lastLookupTimestamp;
private BExpressionContextWay expctxWay;
private BExpressionContextNode expctxNode;
private File lastProfileFile;
- private long lastProfileTimestamp;
+ private long lastProfileTimestamp;
private boolean profilesBusy;
private long lastUseTime;
-
- private static ProfileCache[] apc = new ProfileCache[1];
- private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
- public static synchronized void setSize( int size )
- {
+ private static ProfileCache[] apc = new ProfileCache[1];
+ private static boolean debug = Boolean.getBoolean("debugProfileCache");
+
+ public static synchronized void setSize(int size) {
apc = new ProfileCache[size];
}
- public static synchronized boolean parseProfile( RoutingContext rc )
- {
- String profileBaseDir = System.getProperty( "profileBaseDir" );
- File profileDir;
- File profileFile;
- if ( profileBaseDir == null )
- {
- profileDir = new File( rc.localFunction ).getParentFile();
- profileFile = new File( rc.localFunction ) ;
- }
- else
- {
- profileDir = new File( profileBaseDir );
- profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
- }
+ public static synchronized boolean parseProfile(RoutingContext rc) {
+ String profileBaseDir = System.getProperty("profileBaseDir");
+ File profileDir;
+ File profileFile;
+ if (profileBaseDir == null) {
+ profileDir = new File(rc.localFunction).getParentFile();
+ profileFile = new File(rc.localFunction);
+ } else {
+ profileDir = new File(profileBaseDir);
+ profileFile = new File(profileDir, rc.localFunction + ".brf");
+ }
- rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum()<<24;
- File lookupFile = new File( profileDir, "lookups.dat" );
-
- // invalidate cache at lookup-table update
- if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
- {
- if ( lastLookupFile != null )
- {
- System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
- }
- apc = new ProfileCache[apc.length];
- lastLookupFile = lookupFile;
- lastLookupTimestamp = lookupFile.lastModified();
- }
-
- ProfileCache lru = null;
- int unusedSlot =-1;
+ rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum() << 24;
+ File lookupFile = new File(profileDir, "lookups.dat");
- // check for re-use
- for( int i=0; i we overide this one
- unusedSlot = -1;
- break;
- }
- if ( lru == null || lru.lastUseTime > pc.lastUseTime )
- {
- lru = pc;
+ // invalidate cache at lookup-table update
+ if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
+ if (lastLookupFile != null) {
+ System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
+ }
+ apc = new ProfileCache[apc.length];
+ lastLookupFile = lookupFile;
+ lastLookupTimestamp = lookupFile.lastModified();
+ }
+
+ ProfileCache lru = null;
+ int unusedSlot = -1;
+
+ // check for re-use
+ for (int i = 0; i < apc.length; i++) {
+ ProfileCache pc = apc[i];
+
+ if (pc != null) {
+ if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
+ if (rc.profileTimestamp == pc.lastProfileTimestamp) {
+ rc.expctxWay = pc.expctxWay;
+ rc.expctxNode = pc.expctxNode;
+ rc.readGlobalConfig();
+ pc.profilesBusy = true;
+ return true;
}
+ lru = pc; // name-match but timestamp-mismatch -> we overide this one
+ unusedSlot = -1;
+ break;
}
- else if ( unusedSlot < 0 )
- {
- unusedSlot = i;
+ if (lru == null || lru.lastUseTime > pc.lastUseTime) {
+ lru = pc;
}
+ } else if (unusedSlot < 0) {
+ unusedSlot = i;
}
-
- BExpressionMetaData meta = new BExpressionMetaData();
-
- rc.expctxWay = new BExpressionContextWay( rc.memoryclass * 512, meta );
- rc.expctxNode = new BExpressionContextNode( 0, meta );
- rc.expctxNode.setForeignContext( rc.expctxWay );
-
- meta.readMetaData( new File( profileDir, "lookups.dat" ) );
+ }
- rc.expctxWay.parseFile( profileFile, "global" );
- rc.expctxNode.parseFile( profileFile, "global" );
+ BExpressionMetaData meta = new BExpressionMetaData();
- rc.readGlobalConfig();
-
- if ( rc.processUnusedTags )
- {
- rc.expctxWay.setAllTagsUsed();
+ rc.expctxWay = new BExpressionContextWay(rc.memoryclass * 512, meta);
+ rc.expctxNode = new BExpressionContextNode(0, meta);
+ rc.expctxNode.setForeignContext(rc.expctxWay);
+
+ meta.readMetaData(new File(profileDir, "lookups.dat"));
+
+ rc.expctxWay.parseFile(profileFile, "global");
+ rc.expctxNode.parseFile(profileFile, "global");
+
+ rc.readGlobalConfig();
+
+ if (rc.processUnusedTags) {
+ rc.expctxWay.setAllTagsUsed();
+ }
+
+ if (lru == null || unusedSlot >= 0) {
+ lru = new ProfileCache();
+ if (unusedSlot >= 0) {
+ apc[unusedSlot] = lru;
+ if (debug)
+ System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
}
+ }
- if ( lru == null || unusedSlot >= 0 )
- {
- lru = new ProfileCache();
- if ( unusedSlot >= 0 )
- {
- apc[unusedSlot] = lru;
- if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
- }
- }
+ if (lru.lastProfileFile != null) {
+ if (debug)
+ System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
+ }
- if ( lru.lastProfileFile != null )
- {
- if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
- }
-
- lru.lastProfileTimestamp = rc.profileTimestamp;
- lru.lastProfileFile = profileFile;
- lru.expctxWay = rc.expctxWay;
- lru.expctxNode = rc.expctxNode;
- lru.profilesBusy = true;
- lru.lastUseTime = System.currentTimeMillis();
- return false;
+ lru.lastProfileTimestamp = rc.profileTimestamp;
+ lru.lastProfileFile = profileFile;
+ lru.expctxWay = rc.expctxWay;
+ lru.expctxNode = rc.expctxNode;
+ lru.profilesBusy = true;
+ lru.lastUseTime = System.currentTimeMillis();
+ return false;
}
- public static synchronized void releaseProfile( RoutingContext rc )
- {
- for( int i=0; i max ? max : alternativeIdx);
- }
- public int alternativeIdx = 0;
- public String localFunction;
- public long profileTimestamp;
-
- public Map keyValues;
-
- public String rawTrackPath;
-
- public String getProfileName()
- {
- String name = localFunction == null ? "unknown" : localFunction;
- if ( name.endsWith( ".brf" ) ) name = name.substring( 0, localFunction.length() - 4 );
- int idx = name.lastIndexOf( File.separatorChar );
- if ( idx >= 0 ) name = name.substring( idx+1 );
- return name;
- }
-
- public BExpressionContextWay expctxWay;
- public BExpressionContextNode expctxNode;
-
- public GeometryDecoder geometryDecoder = new GeometryDecoder();
-
- public int memoryclass = 64;
-
- public int downhillcostdiv;
- public int downhillcutoff;
- public int uphillcostdiv;
- public int uphillcutoff;
- public boolean carMode;
- public boolean bikeMode;
- public boolean footMode;
- public boolean considerTurnRestrictions;
- public boolean processUnusedTags;
- public boolean forceSecondaryData;
- public double pass1coefficient;
- public double pass2coefficient;
- public int elevationpenaltybuffer;
- public int elevationmaxbuffer;
- public int elevationbufferreduce;
-
- public double cost1speed;
- public double additionalcostfactor;
- public double changetime;
- public double buffertime;
- public double waittimeadjustment;
- public double inittimeadjustment;
- public double starttimeoffset;
- public boolean transitonly;
-
- public double waypointCatchingRange;
-
- private void setModel( String className )
- {
- if ( className == null )
- {
- pm = new StdModel();
- }
- else
- {
- try
- {
- Class clazz = Class.forName( className );
- pm = (OsmPathModel) clazz.newInstance();
- }
- catch( Exception e )
- {
- throw new RuntimeException( "Cannot create path-model: " + e );
- }
- }
- initModel();
- }
-
- public void initModel()
- {
- pm.init( expctxWay, expctxNode, keyValues );
- }
-
- public long getKeyValueChecksum()
- {
- long s = 0L;
- if ( keyValues != null )
- {
- for( Map.Entry e : keyValues.entrySet() )
- {
- s += e.getKey().hashCode() + e.getValue().hashCode();
- }
- }
- return s;
- }
-
- public void readGlobalConfig()
- {
- BExpressionContext expctxGlobal = expctxWay; // just one of them...
-
- if (keyValues != null) {
- // add parameter to context
- for (Map.Entry e : keyValues.entrySet()) {
- float f = Float.parseFloat(e.getValue());
- expctxWay.setVariableValue(e.getKey(), f, false );
- expctxNode.setVariableValue(e.getKey(), f, false );
- }
- }
-
- setModel( expctxGlobal._modelClass );
-
- downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
- downhillcutoff = (int)(expctxGlobal.getVariableValue( "downhillcutoff", 0.f )*10000);
- uphillcostdiv = (int)expctxGlobal.getVariableValue( "uphillcost", 0.f );
- uphillcutoff = (int)(expctxGlobal.getVariableValue( "uphillcutoff", 0.f )*10000);
- if ( downhillcostdiv != 0 ) downhillcostdiv = 1000000/downhillcostdiv;
- if ( uphillcostdiv != 0 ) uphillcostdiv = 1000000/uphillcostdiv;
- carMode = 0.f != expctxGlobal.getVariableValue( "validForCars", 0.f );
- bikeMode = 0.f != expctxGlobal.getVariableValue( "validForBikes", 0.f );
- footMode = 0.f != expctxGlobal.getVariableValue( "validForFoot", 0.f );
-
- waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
-
- // turn-restrictions not used per default for foot profiles
- considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", footMode ? 0.f : 1.f );
-
- // process tags not used in the profile (to have them in the data-tab)
- processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
-
- forceSecondaryData = 0.f != expctxGlobal.getVariableValue( "forceSecondaryData", 0.f );
- pass1coefficient = expctxGlobal.getVariableValue( "pass1coefficient", 1.5f );
- pass2coefficient = expctxGlobal.getVariableValue( "pass2coefficient", 0.f );
- elevationpenaltybuffer = (int)(expctxGlobal.getVariableValue( "elevationpenaltybuffer", 5.f )*1000000);
- elevationmaxbuffer = (int)(expctxGlobal.getVariableValue( "elevationmaxbuffer", 10.f )*1000000);
- elevationbufferreduce = (int)(expctxGlobal.getVariableValue( "elevationbufferreduce", 0.f )*10000);
-
- cost1speed = expctxGlobal.getVariableValue( "cost1speed", 22.f );
- additionalcostfactor = expctxGlobal.getVariableValue( "additionalcostfactor", 1.5f );
- changetime = expctxGlobal.getVariableValue( "changetime", 180.f );
- buffertime = expctxGlobal.getVariableValue( "buffertime", 120.f );
- waittimeadjustment = expctxGlobal.getVariableValue( "waittimeadjustment", 0.9f );
- inittimeadjustment = expctxGlobal.getVariableValue( "inittimeadjustment", 0.2f );
- starttimeoffset = expctxGlobal.getVariableValue( "starttimeoffset", 0.f );
- transitonly = expctxGlobal.getVariableValue( "transitonly", 0.f ) != 0.f;
-
- farTrafficWeight = expctxGlobal.getVariableValue( "farTrafficWeight", 2.f );
- nearTrafficWeight = expctxGlobal.getVariableValue( "nearTrafficWeight", 2.f );
- farTrafficDecayLength = expctxGlobal.getVariableValue( "farTrafficDecayLength", 30000.f );
- nearTrafficDecayLength = expctxGlobal.getVariableValue( "nearTrafficDecayLength", 3000.f );
- trafficDirectionFactor = expctxGlobal.getVariableValue( "trafficDirectionFactor", 0.9f );
- trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
- trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
-
- showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f );
- showSpeedProfile = 0.f != expctxGlobal.getVariableValue( "showSpeedProfile", 0.f );
- inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f );
-
- int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
- if ( tiMode != 1 ) // automatic selection from coordinate source
- {
- turnInstructionMode = tiMode;
- }
- turnInstructionCatchingRange = expctxGlobal.getVariableValue( "turnInstructionCatchingRange", 40.f );
- turnInstructionRoundabouts = expctxGlobal.getVariableValue( "turnInstructionRoundabouts", 1.f ) != 0.f;
-
- // Speed computation model (for bikes)
- // Total mass (biker + bike + luggages or hiker), in kg
- totalMass = expctxGlobal.getVariableValue( "totalMass", 90.f );
- // Max speed (before braking), in km/h in profile and m/s in code
- if (footMode) {
- maxSpeed = expctxGlobal.getVariableValue( "maxSpeed", 6.f ) / 3.6;
- } else {
- maxSpeed = expctxGlobal.getVariableValue( "maxSpeed", 45.f ) / 3.6;
- }
- // Equivalent surface for wind, S * C_x, F = -1/2 * S * C_x * v^2 = - S_C_x * v^2
- S_C_x = expctxGlobal.getVariableValue( "S_C_x", 0.5f * 0.45f );
- // Default resistance of the road, F = - m * g * C_r (for good quality road)
- defaultC_r = expctxGlobal.getVariableValue( "C_r", 0.01f );
- // Constant power of the biker (in W)
- bikerPower = expctxGlobal.getVariableValue( "bikerPower", 100.f );
- }
-
- public List poipoints;
-
- public List nogopoints = null;
- private List nogopoints_all = null; // full list not filtered for wayoints-in-nogos
- private List keepnogopoints = null;
- private OsmNodeNamed pendingEndpoint = null;
-
- public Integer startDirection;
- public boolean startDirectionValid;
- public boolean forceUseStartDirection;
-
- public CheapAngleMeter anglemeter = new CheapAngleMeter();
-
- public double nogoCost = 0.;
- public boolean isEndpoint = false;
-
- public boolean shortestmatch = false;
- public double wayfraction;
- public int ilatshortest;
- public int ilonshortest;
-
- public boolean countTraffic;
- public boolean inverseDirection;
- public DataOutput trafficOutputStream;
-
- public double farTrafficWeight;
- public double nearTrafficWeight;
- public double farTrafficDecayLength;
- public double nearTrafficDecayLength;
- public double trafficDirectionFactor;
- public double trafficSourceExponent;
- public double trafficSourceMinDist;
-
- public boolean showspeed;
- public boolean showSpeedProfile;
- public boolean inverseRouting;
-
- public OsmPrePath firstPrePath;
-
- public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style
- public double turnInstructionCatchingRange;
- public boolean turnInstructionRoundabouts;
-
- // Speed computation model (for bikes)
- public double totalMass;
- public double maxSpeed;
- public double S_C_x;
- public double defaultC_r;
- public double bikerPower;
-
- public static void prepareNogoPoints( List nogos )
- {
- for( OsmNodeNamed nogo : nogos )
- {
- if (nogo instanceof OsmNogoPolygon)
- {
- continue;
- }
- String s = nogo.name;
- int idx = s.indexOf( ' ' );
- if ( idx > 0 ) s = s.substring( 0 , idx );
- int ir = 20; // default radius
- if ( s.length() > 4 )
- {
- try { ir = Integer.parseInt( s.substring( 4 ) ); }
- catch( Exception e ) { /* ignore */ }
- }
- // Radius of the nogo point in meters
- nogo.radius = ir;
- }
- }
-
- /**
- * restore the full nogolist previously saved by cleanNogoList
- */
- public void restoreNogoList()
- {
- nogopoints = nogopoints_all;
- }
-
- /**
- * clean the nogolist (previoulsy saved by saveFullNogolist())
- * by removing nogos with waypoints within
- *
- * @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
- */
- public void cleanNogoList( List waypoints )
- {
- nogopoints_all = nogopoints;
- if ( nogopoints == null ) return;
- List nogos = new ArrayList();
- for( OsmNodeNamed nogo : nogopoints )
- {
- boolean goodGuy = true;
- for( OsmNode wp : waypoints )
- {
- if ( wp.calcDistance( nogo ) < nogo.radius
- && (!(nogo instanceof OsmNogoPolygon)
- || (((OsmNogoPolygon)nogo).isClosed
- ? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
- : ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
- {
- goodGuy = false;
- }
- }
- if ( goodGuy ) nogos.add( nogo );
- }
- nogopoints = nogos.isEmpty() ? null : nogos;
- }
-
- public boolean allInOneNogo( List waypoints )
- {
- if ( nogopoints == null ) return false;
- boolean allInTotal = false;
- for( OsmNodeNamed nogo : nogopoints )
- {
- boolean allIn = Double.isNaN( nogo.nogoWeight );
- for( OsmNode wp : waypoints )
- {
- int dist = wp.calcDistance( nogo );
- if ( dist < nogo.radius
- && (!(nogo instanceof OsmNogoPolygon)
- || (((OsmNogoPolygon)nogo).isClosed
- ? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
- : ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
- {
- continue;
- }
- allIn = false;
- }
- allInTotal |= allIn;
- }
- return allInTotal;
- }
-
- public long[] getNogoChecksums()
- {
- long[] cs = new long[3];
- int n = nogopoints == null ? 0 : nogopoints.size();
- for( int i=0; i();
- nogopoints.add( wp );
- if ( keepnogopoints != null ) nogopoints.addAll( keepnogopoints );
- isEndpoint = endpoint;
- this.pendingEndpoint = pendingEndpoint;
- }
-
- public boolean checkPendingEndpoint()
- {
- if ( pendingEndpoint != null )
- {
- isEndpoint = true;
- nogopoints.set( 0, pendingEndpoint );
- pendingEndpoint = null;
- return true;
- }
- return false;
- }
-
- public void unsetWaypoint()
- {
- nogopoints = keepnogopoints;
- pendingEndpoint = null;
- isEndpoint = false;
- }
-
- public int calcDistance( int lon1, int lat1, int lon2, int lat2 )
- {
- double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 );
- double dlon2m = lonlat2m[0];
- double dlat2m = lonlat2m[1];
- double dx = (lon2 - lon1 ) * dlon2m;
- double dy = (lat2 - lat1 ) * dlat2m;
- double d = Math.sqrt( dy*dy + dx*dx );
-
- shortestmatch = false;
-
- if ( nogopoints != null && !nogopoints.isEmpty() && d > 0. )
- {
- for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ )
- {
- OsmNodeNamed nogo = nogopoints.get(ngidx);
- double x1 = (lon1 - nogo.ilon) * dlon2m;
- double y1 = (lat1 - nogo.ilat) * dlat2m;
- double x2 = (lon2 - nogo.ilon) * dlon2m;
- double y2 = (lat2 - nogo.ilat) * dlat2m;
- double r12 = x1*x1 + y1*y1;
- double r22 = x2*x2 + y2*y2;
- double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d;
-
- if ( radius < nogo.radius ) // 20m
- {
- double s1 = x1*dx + y1*dy;
- double s2 = x2*dx + y2*dy;
-
-
- if ( s1 < 0. ) { s1 = -s1; s2 = -s2; }
- if ( s2 > 0. )
- {
- radius = Math.sqrt( s1 < s2 ? r12 : r22 );
- if ( radius > nogo.radius ) continue;
- }
- if ( nogo.isNogo )
- {
- if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
- if (Double.isNaN(nogo.nogoWeight)) {
- // default nogo behaviour (ignore completely)
- nogoCost = -1;
- } else {
- // nogo weight, compute distance within the circle
- nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
- }
- }
- else if (((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
- {
- // nogo is a polyline/polygon, we have to check there is indeed
- // an intersection in this case (radius check is not enough).
- if (Double.isNaN(nogo.nogoWeight)) {
- // default nogo behaviour (ignore completely)
- nogoCost = -1;
- } else {
- if (((OsmNogoPolygon)nogo).isClosed) {
- // compute distance within the polygon
- nogoCost = ((OsmNogoPolygon)nogo).distanceWithinPolygon(lon1, lat1, lon2, lat2) * nogo.nogoWeight;
- } else {
- // for a polyline, just add a constant penalty
- nogoCost = nogo.nogoWeight;
- }
- }
- }
- }
- else
- {
- shortestmatch = true;
- nogo.radius = radius; // shortest distance to way
- // calculate remaining distance
- if ( s2 < 0. )
- {
- wayfraction = -s2 / (d*d);
- double xm = x2 - wayfraction*dx;
- double ym = y2 - wayfraction*dy;
- ilonshortest = (int)(xm / dlon2m + nogo.ilon);
- ilatshortest = (int)(ym / dlat2m + nogo.ilat);
- }
- else if ( s1 > s2 )
- {
- wayfraction = 0.;
- ilonshortest = lon2;
- ilatshortest = lat2;
- }
- else
- {
- wayfraction = 1.;
- ilonshortest = lon1;
- ilatshortest = lat1;
- }
-
- // here it gets nasty: there can be nogo-points in the list
- // *after* the shortest distance point. In case of a shortest-match
- // we use the reduced way segment for nogo-matching, in order not
- // to cut our escape-way if we placed a nogo just in front of where we are
- if ( isEndpoint )
- {
- wayfraction = 1. - wayfraction;
- lon2 = ilonshortest;
- lat2 = ilatshortest;
- }
- else
- {
- nogoCost = 0.;
- lon1 = ilonshortest;
- lat1 = ilatshortest;
- }
- dx = (lon2 - lon1 ) * dlon2m;
- dy = (lat2 - lat1 ) * dlat2m;
- d = Math.sqrt( dy*dy + dx*dx );
- }
- }
- }
- }
- return (int)(d + 1.0 );
- }
-
- public OsmPathModel pm;
-
- public OsmPrePath createPrePath( OsmPath origin, OsmLink link )
- {
- OsmPrePath p = pm.createPrePath();
- if ( p != null )
- {
- p.init( origin, link, this );
- }
- return p;
- }
-
- public OsmPath createPath( OsmLink link )
- {
- OsmPath p = pm.createPath();
- p.init( link );
- return p;
- }
-
- public OsmPath createPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode )
- {
- OsmPath p = pm.createPath();
- p.init( origin, link, refTrack, detailMode, this );
- return p;
- }
-
-}
+/**
+ * Container for routig configs
+ *
+ * @author ab
+ */
+package btools.router;
+
+import java.io.DataOutput;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import btools.expressions.BExpressionContext;
+import btools.expressions.BExpressionContextNode;
+import btools.expressions.BExpressionContextWay;
+import btools.mapaccess.GeometryDecoder;
+import btools.mapaccess.OsmLink;
+import btools.mapaccess.OsmNode;
+import btools.util.CheapAngleMeter;
+import btools.util.CheapRuler;
+
+public final class RoutingContext {
+ public void setAlternativeIdx(int idx) {
+ alternativeIdx = idx;
+ }
+
+ public int getAlternativeIdx(int min, int max) {
+ return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
+ }
+
+ public int alternativeIdx = 0;
+ public String localFunction;
+ public long profileTimestamp;
+
+ public Map keyValues;
+
+ public String rawTrackPath;
+
+ public String getProfileName() {
+ String name = localFunction == null ? "unknown" : localFunction;
+ if (name.endsWith(".brf")) name = name.substring(0, localFunction.length() - 4);
+ int idx = name.lastIndexOf(File.separatorChar);
+ if (idx >= 0) name = name.substring(idx + 1);
+ return name;
+ }
+
+ public BExpressionContextWay expctxWay;
+ public BExpressionContextNode expctxNode;
+
+ public GeometryDecoder geometryDecoder = new GeometryDecoder();
+
+ public int memoryclass = 64;
+
+ public int downhillcostdiv;
+ public int downhillcutoff;
+ public int uphillcostdiv;
+ public int uphillcutoff;
+ public boolean carMode;
+ public boolean bikeMode;
+ public boolean footMode;
+ public boolean considerTurnRestrictions;
+ public boolean processUnusedTags;
+ public boolean forceSecondaryData;
+ public double pass1coefficient;
+ public double pass2coefficient;
+ public int elevationpenaltybuffer;
+ public int elevationmaxbuffer;
+ public int elevationbufferreduce;
+
+ public double cost1speed;
+ public double additionalcostfactor;
+ public double changetime;
+ public double buffertime;
+ public double waittimeadjustment;
+ public double inittimeadjustment;
+ public double starttimeoffset;
+ public boolean transitonly;
+
+ public double waypointCatchingRange;
+
+ private void setModel(String className) {
+ if (className == null) {
+ pm = new StdModel();
+ } else {
+ try {
+ Class clazz = Class.forName(className);
+ pm = (OsmPathModel) clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot create path-model: " + e);
+ }
+ }
+ initModel();
+ }
+
+ public void initModel() {
+ pm.init(expctxWay, expctxNode, keyValues);
+ }
+
+ public long getKeyValueChecksum() {
+ long s = 0L;
+ if (keyValues != null) {
+ for (Map.Entry e : keyValues.entrySet()) {
+ s += e.getKey().hashCode() + e.getValue().hashCode();
+ }
+ }
+ return s;
+ }
+
+ public void readGlobalConfig() {
+ BExpressionContext expctxGlobal = expctxWay; // just one of them...
+
+ if (keyValues != null) {
+ // add parameter to context
+ for (Map.Entry e : keyValues.entrySet()) {
+ float f = Float.parseFloat(e.getValue());
+ expctxWay.setVariableValue(e.getKey(), f, false);
+ expctxNode.setVariableValue(e.getKey(), f, false);
+ }
+ }
+
+ setModel(expctxGlobal._modelClass);
+
+ downhillcostdiv = (int) expctxGlobal.getVariableValue("downhillcost", 0.f);
+ downhillcutoff = (int) (expctxGlobal.getVariableValue("downhillcutoff", 0.f) * 10000);
+ uphillcostdiv = (int) expctxGlobal.getVariableValue("uphillcost", 0.f);
+ uphillcutoff = (int) (expctxGlobal.getVariableValue("uphillcutoff", 0.f) * 10000);
+ if (downhillcostdiv != 0) downhillcostdiv = 1000000 / downhillcostdiv;
+ if (uphillcostdiv != 0) uphillcostdiv = 1000000 / uphillcostdiv;
+ carMode = 0.f != expctxGlobal.getVariableValue("validForCars", 0.f);
+ bikeMode = 0.f != expctxGlobal.getVariableValue("validForBikes", 0.f);
+ footMode = 0.f != expctxGlobal.getVariableValue("validForFoot", 0.f);
+
+ waypointCatchingRange = expctxGlobal.getVariableValue("waypointCatchingRange", 250.f);
+
+ // turn-restrictions not used per default for foot profiles
+ considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f);
+
+ // process tags not used in the profile (to have them in the data-tab)
+ processUnusedTags = 0.f != expctxGlobal.getVariableValue("processUnusedTags", 0.f);
+
+ forceSecondaryData = 0.f != expctxGlobal.getVariableValue("forceSecondaryData", 0.f);
+ pass1coefficient = expctxGlobal.getVariableValue("pass1coefficient", 1.5f);
+ pass2coefficient = expctxGlobal.getVariableValue("pass2coefficient", 0.f);
+ elevationpenaltybuffer = (int) (expctxGlobal.getVariableValue("elevationpenaltybuffer", 5.f) * 1000000);
+ elevationmaxbuffer = (int) (expctxGlobal.getVariableValue("elevationmaxbuffer", 10.f) * 1000000);
+ elevationbufferreduce = (int) (expctxGlobal.getVariableValue("elevationbufferreduce", 0.f) * 10000);
+
+ cost1speed = expctxGlobal.getVariableValue("cost1speed", 22.f);
+ additionalcostfactor = expctxGlobal.getVariableValue("additionalcostfactor", 1.5f);
+ changetime = expctxGlobal.getVariableValue("changetime", 180.f);
+ buffertime = expctxGlobal.getVariableValue("buffertime", 120.f);
+ waittimeadjustment = expctxGlobal.getVariableValue("waittimeadjustment", 0.9f);
+ inittimeadjustment = expctxGlobal.getVariableValue("inittimeadjustment", 0.2f);
+ starttimeoffset = expctxGlobal.getVariableValue("starttimeoffset", 0.f);
+ transitonly = expctxGlobal.getVariableValue("transitonly", 0.f) != 0.f;
+
+ farTrafficWeight = expctxGlobal.getVariableValue("farTrafficWeight", 2.f);
+ nearTrafficWeight = expctxGlobal.getVariableValue("nearTrafficWeight", 2.f);
+ farTrafficDecayLength = expctxGlobal.getVariableValue("farTrafficDecayLength", 30000.f);
+ nearTrafficDecayLength = expctxGlobal.getVariableValue("nearTrafficDecayLength", 3000.f);
+ trafficDirectionFactor = expctxGlobal.getVariableValue("trafficDirectionFactor", 0.9f);
+ trafficSourceExponent = expctxGlobal.getVariableValue("trafficSourceExponent", -0.7f);
+ trafficSourceMinDist = expctxGlobal.getVariableValue("trafficSourceMinDist", 3000.f);
+
+ showspeed = 0.f != expctxGlobal.getVariableValue("showspeed", 0.f);
+ showSpeedProfile = 0.f != expctxGlobal.getVariableValue("showSpeedProfile", 0.f);
+ inverseRouting = 0.f != expctxGlobal.getVariableValue("inverseRouting", 0.f);
+
+ int tiMode = (int) expctxGlobal.getVariableValue("turnInstructionMode", 0.f);
+ if (tiMode != 1) // automatic selection from coordinate source
+ {
+ turnInstructionMode = tiMode;
+ }
+ turnInstructionCatchingRange = expctxGlobal.getVariableValue("turnInstructionCatchingRange", 40.f);
+ turnInstructionRoundabouts = expctxGlobal.getVariableValue("turnInstructionRoundabouts", 1.f) != 0.f;
+
+ // Speed computation model (for bikes)
+ // Total mass (biker + bike + luggages or hiker), in kg
+ totalMass = expctxGlobal.getVariableValue("totalMass", 90.f);
+ // Max speed (before braking), in km/h in profile and m/s in code
+ if (footMode) {
+ maxSpeed = expctxGlobal.getVariableValue("maxSpeed", 6.f) / 3.6;
+ } else {
+ maxSpeed = expctxGlobal.getVariableValue("maxSpeed", 45.f) / 3.6;
+ }
+ // Equivalent surface for wind, S * C_x, F = -1/2 * S * C_x * v^2 = - S_C_x * v^2
+ S_C_x = expctxGlobal.getVariableValue("S_C_x", 0.5f * 0.45f);
+ // Default resistance of the road, F = - m * g * C_r (for good quality road)
+ defaultC_r = expctxGlobal.getVariableValue("C_r", 0.01f);
+ // Constant power of the biker (in W)
+ bikerPower = expctxGlobal.getVariableValue("bikerPower", 100.f);
+ }
+
+ public List poipoints;
+
+ public List nogopoints = null;
+ private List nogopoints_all = null; // full list not filtered for wayoints-in-nogos
+ private List keepnogopoints = null;
+ private OsmNodeNamed pendingEndpoint = null;
+
+ public Integer startDirection;
+ public boolean startDirectionValid;
+ public boolean forceUseStartDirection;
+
+ public CheapAngleMeter anglemeter = new CheapAngleMeter();
+
+ public double nogoCost = 0.;
+ public boolean isEndpoint = false;
+
+ public boolean shortestmatch = false;
+ public double wayfraction;
+ public int ilatshortest;
+ public int ilonshortest;
+
+ public boolean countTraffic;
+ public boolean inverseDirection;
+ public DataOutput trafficOutputStream;
+
+ public double farTrafficWeight;
+ public double nearTrafficWeight;
+ public double farTrafficDecayLength;
+ public double nearTrafficDecayLength;
+ public double trafficDirectionFactor;
+ public double trafficSourceExponent;
+ public double trafficSourceMinDist;
+
+ public boolean showspeed;
+ public boolean showSpeedProfile;
+ public boolean inverseRouting;
+
+ public OsmPrePath firstPrePath;
+
+ public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style
+ public double turnInstructionCatchingRange;
+ public boolean turnInstructionRoundabouts;
+
+ // Speed computation model (for bikes)
+ public double totalMass;
+ public double maxSpeed;
+ public double S_C_x;
+ public double defaultC_r;
+ public double bikerPower;
+
+ public static void prepareNogoPoints(List nogos) {
+ for (OsmNodeNamed nogo : nogos) {
+ if (nogo instanceof OsmNogoPolygon) {
+ continue;
+ }
+ String s = nogo.name;
+ int idx = s.indexOf(' ');
+ if (idx > 0) s = s.substring(0, idx);
+ int ir = 20; // default radius
+ if (s.length() > 4) {
+ try {
+ ir = Integer.parseInt(s.substring(4));
+ } catch (Exception e) { /* ignore */ }
+ }
+ // Radius of the nogo point in meters
+ nogo.radius = ir;
+ }
+ }
+
+ /**
+ * restore the full nogolist previously saved by cleanNogoList
+ */
+ public void restoreNogoList() {
+ nogopoints = nogopoints_all;
+ }
+
+ /**
+ * clean the nogolist (previoulsy saved by saveFullNogolist())
+ * by removing nogos with waypoints within
+ *
+ * @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
+ */
+ public void cleanNogoList(List waypoints) {
+ nogopoints_all = nogopoints;
+ if (nogopoints == null) return;
+ List nogos = new ArrayList();
+ for (OsmNodeNamed nogo : nogopoints) {
+ boolean goodGuy = true;
+ for (OsmNode wp : waypoints) {
+ if (wp.calcDistance(nogo) < nogo.radius
+ && (!(nogo instanceof OsmNogoPolygon)
+ || (((OsmNogoPolygon) nogo).isClosed
+ ? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
+ : ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
+ goodGuy = false;
+ }
+ }
+ if (goodGuy) nogos.add(nogo);
+ }
+ nogopoints = nogos.isEmpty() ? null : nogos;
+ }
+
+ public boolean allInOneNogo(List waypoints) {
+ if (nogopoints == null) return false;
+ boolean allInTotal = false;
+ for (OsmNodeNamed nogo : nogopoints) {
+ boolean allIn = Double.isNaN(nogo.nogoWeight);
+ for (OsmNode wp : waypoints) {
+ int dist = wp.calcDistance(nogo);
+ if (dist < nogo.radius
+ && (!(nogo instanceof OsmNogoPolygon)
+ || (((OsmNogoPolygon) nogo).isClosed
+ ? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
+ : ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
+ continue;
+ }
+ allIn = false;
+ }
+ allInTotal |= allIn;
+ }
+ return allInTotal;
+ }
+
+ public long[] getNogoChecksums() {
+ long[] cs = new long[3];
+ int n = nogopoints == null ? 0 : nogopoints.size();
+ for (int i = 0; i < n; i++) {
+ OsmNodeNamed nogo = nogopoints.get(i);
+ cs[0] += nogo.ilon;
+ cs[1] += nogo.ilat;
+ // 10 is an arbitrary constant to get sub-integer precision in the checksum
+ cs[2] += (long) (nogo.radius * 10.);
+ }
+ return cs;
+ }
+
+ public void setWaypoint(OsmNodeNamed wp, boolean endpoint) {
+ setWaypoint(wp, null, endpoint);
+ }
+
+ public void setWaypoint(OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint) {
+ keepnogopoints = nogopoints;
+ nogopoints = new ArrayList();
+ nogopoints.add(wp);
+ if (keepnogopoints != null) nogopoints.addAll(keepnogopoints);
+ isEndpoint = endpoint;
+ this.pendingEndpoint = pendingEndpoint;
+ }
+
+ public boolean checkPendingEndpoint() {
+ if (pendingEndpoint != null) {
+ isEndpoint = true;
+ nogopoints.set(0, pendingEndpoint);
+ pendingEndpoint = null;
+ return true;
+ }
+ return false;
+ }
+
+ public void unsetWaypoint() {
+ nogopoints = keepnogopoints;
+ pendingEndpoint = null;
+ isEndpoint = false;
+ }
+
+ public int calcDistance(int lon1, int lat1, int lon2, int lat2) {
+ double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
+ double dlon2m = lonlat2m[0];
+ double dlat2m = lonlat2m[1];
+ double dx = (lon2 - lon1) * dlon2m;
+ double dy = (lat2 - lat1) * dlat2m;
+ double d = Math.sqrt(dy * dy + dx * dx);
+
+ shortestmatch = false;
+
+ if (nogopoints != null && !nogopoints.isEmpty() && d > 0.) {
+ for (int ngidx = 0; ngidx < nogopoints.size(); ngidx++) {
+ OsmNodeNamed nogo = nogopoints.get(ngidx);
+ double x1 = (lon1 - nogo.ilon) * dlon2m;
+ double y1 = (lat1 - nogo.ilat) * dlat2m;
+ double x2 = (lon2 - nogo.ilon) * dlon2m;
+ double y2 = (lat2 - nogo.ilat) * dlat2m;
+ double r12 = x1 * x1 + y1 * y1;
+ double r22 = x2 * x2 + y2 * y2;
+ double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d;
+
+ if (radius < nogo.radius) // 20m
+ {
+ double s1 = x1 * dx + y1 * dy;
+ double s2 = x2 * dx + y2 * dy;
+
+
+ if (s1 < 0.) {
+ s1 = -s1;
+ s2 = -s2;
+ }
+ if (s2 > 0.) {
+ radius = Math.sqrt(s1 < s2 ? r12 : r22);
+ if (radius > nogo.radius) continue;
+ }
+ if (nogo.isNogo) {
+ if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
+ if (Double.isNaN(nogo.nogoWeight)) {
+ // default nogo behaviour (ignore completely)
+ nogoCost = -1;
+ } else {
+ // nogo weight, compute distance within the circle
+ nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
+ }
+ } else if (((OsmNogoPolygon) nogo).intersects(lon1, lat1, lon2, lat2)) {
+ // nogo is a polyline/polygon, we have to check there is indeed
+ // an intersection in this case (radius check is not enough).
+ if (Double.isNaN(nogo.nogoWeight)) {
+ // default nogo behaviour (ignore completely)
+ nogoCost = -1;
+ } else {
+ if (((OsmNogoPolygon) nogo).isClosed) {
+ // compute distance within the polygon
+ nogoCost = ((OsmNogoPolygon) nogo).distanceWithinPolygon(lon1, lat1, lon2, lat2) * nogo.nogoWeight;
+ } else {
+ // for a polyline, just add a constant penalty
+ nogoCost = nogo.nogoWeight;
+ }
+ }
+ }
+ } else {
+ shortestmatch = true;
+ nogo.radius = radius; // shortest distance to way
+ // calculate remaining distance
+ if (s2 < 0.) {
+ wayfraction = -s2 / (d * d);
+ double xm = x2 - wayfraction * dx;
+ double ym = y2 - wayfraction * dy;
+ ilonshortest = (int) (xm / dlon2m + nogo.ilon);
+ ilatshortest = (int) (ym / dlat2m + nogo.ilat);
+ } else if (s1 > s2) {
+ wayfraction = 0.;
+ ilonshortest = lon2;
+ ilatshortest = lat2;
+ } else {
+ wayfraction = 1.;
+ ilonshortest = lon1;
+ ilatshortest = lat1;
+ }
+
+ // here it gets nasty: there can be nogo-points in the list
+ // *after* the shortest distance point. In case of a shortest-match
+ // we use the reduced way segment for nogo-matching, in order not
+ // to cut our escape-way if we placed a nogo just in front of where we are
+ if (isEndpoint) {
+ wayfraction = 1. - wayfraction;
+ lon2 = ilonshortest;
+ lat2 = ilatshortest;
+ } else {
+ nogoCost = 0.;
+ lon1 = ilonshortest;
+ lat1 = ilatshortest;
+ }
+ dx = (lon2 - lon1) * dlon2m;
+ dy = (lat2 - lat1) * dlat2m;
+ d = Math.sqrt(dy * dy + dx * dx);
+ }
+ }
+ }
+ }
+ return (int) (d + 1.0);
+ }
+
+ public OsmPathModel pm;
+
+ public OsmPrePath createPrePath(OsmPath origin, OsmLink link) {
+ OsmPrePath p = pm.createPrePath();
+ if (p != null) {
+ p.init(origin, link, this);
+ }
+ return p;
+ }
+
+ public OsmPath createPath(OsmLink link) {
+ OsmPath p = pm.createPath();
+ p.init(link);
+ return p;
+ }
+
+ public OsmPath createPath(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode) {
+ OsmPath p = pm.createPath();
+ p.init(origin, link, refTrack, detailMode, this);
+ return p;
+ }
+
+}
diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java
index bf059fe..2311a75 100644
--- a/brouter-core/src/main/java/btools/router/RoutingEngine.java
+++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java
@@ -1,1377 +1,1155 @@
-package btools.router;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import btools.mapaccess.MatchedWaypoint;
-import btools.mapaccess.NodesCache;
-import btools.mapaccess.OsmLink;
-import btools.mapaccess.OsmLinkHolder;
-import btools.mapaccess.OsmNode;
-import btools.mapaccess.OsmNodePairSet;
-import btools.util.SortedHeap;
-import btools.util.StackSampler;
-
-public class RoutingEngine extends Thread
-{
- private NodesCache nodesCache;
- private SortedHeap openSet = new SortedHeap();
- private boolean finished = false;
-
- protected List waypoints = null;
- protected List matchedWaypoints;
- private int linksProcessed = 0;
-
- private int nodeLimit; // used for target island search
- private int MAXNODES_ISLAND_CHECK = 500;
- private OsmNodePairSet islandNodePairs = new OsmNodePairSet(MAXNODES_ISLAND_CHECK);
-
- protected OsmTrack foundTrack = new OsmTrack();
- private OsmTrack foundRawTrack = null;
- private int alternativeIndex = 0;
-
- protected String errorMessage = null;
-
- private volatile boolean terminated;
-
- protected File segmentDir;
- private String outfileBase;
- private String logfileBase;
- private boolean infoLogEnabled;
- private Writer infoLogWriter;
- private StackSampler stackSampler;
- protected RoutingContext routingContext;
-
- public double airDistanceCostFactor;
- private OsmTrack guideTrack;
-
- private OsmPathElement matchPath;
-
- private long startTime;
- private long maxRunningTime;
- public SearchBoundary boundary;
-
- public boolean quite = false;
-
- private Object[] extract;
-
- private boolean directWeaving = !Boolean.getBoolean( "disableDirectWeaving" );
- private String outfile;
-
- public RoutingEngine( String outfileBase, String logfileBase, File segmentDir,
- List waypoints, RoutingContext rc )
- {
- this.segmentDir = segmentDir;
- this.outfileBase = outfileBase;
- this.logfileBase = logfileBase;
- this.waypoints = waypoints;
- this.infoLogEnabled = outfileBase != null;
- this.routingContext = rc;
-
- File baseFolder = new File( routingContext.localFunction ).getParentFile();
- baseFolder = baseFolder == null ? null : baseFolder.getParentFile();
- if ( baseFolder != null )
- {
- try
- {
- File debugLog = new File( baseFolder, "debug.txt" );
- if ( debugLog.exists() )
- {
- infoLogWriter = new FileWriter( debugLog, true );
- logInfo( "********** start request at " );
- logInfo( "********** " + new Date() );
- }
- }
- catch( IOException ioe )
- {
- throw new RuntimeException( "cannot open debug-log:" + ioe );
- }
-
- File stackLog = new File( baseFolder, "stacks.txt" );
- if ( stackLog.exists() )
- {
- stackSampler = new StackSampler( stackLog, 1000 );
- stackSampler.start();
- logInfo( "********** started stacksampling" );
- }
- }
- boolean cachedProfile = ProfileCache.parseProfile( rc );
- if ( hasInfo() )
- {
- logInfo( "parsed profile " + rc.localFunction + " cached=" + cachedProfile );
- }
- }
-
- private boolean hasInfo()
- {
- return infoLogEnabled || infoLogWriter != null;
- }
-
- private void logInfo( String s )
- {
- if ( infoLogEnabled )
- {
- System.out.println( s );
- }
- if ( infoLogWriter != null )
- {
- try
- {
- infoLogWriter.write( s );
- infoLogWriter.write( '\n' );
- infoLogWriter.flush();
- }
- catch( IOException io )
- {
- infoLogWriter = null;
- }
- }
- }
-
- private void logThrowable( Throwable t )
- {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- t.printStackTrace(pw);
- logInfo( sw.toString() );
- }
-
- public void run()
- {
- doRun( 0 );
- }
-
- public void doRun( long maxRunningTime )
- {
- try
- {
- startTime = System.currentTimeMillis();
- long startTime0 = startTime;
- this.maxRunningTime = maxRunningTime;
- int nsections = waypoints.size() - 1;
- OsmTrack[] refTracks = new OsmTrack[nsections]; // used ways for alternatives
- OsmTrack[] lastTracks = new OsmTrack[nsections];
- OsmTrack track = null;
- ArrayList messageList = new ArrayList();
- for( int i=0;; i++ )
- {
- track = findTrack( refTracks, lastTracks );
- track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
- + " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
- if ( track.energy != 0 )
- {
- track.message += " energy=" + track.getFormattedEnergy() + " time=" + track.getFormattedTime2();
- }
- track.name = "brouter_" + routingContext.getProfileName() + "_" + i;
-
- messageList.add( track.message );
- track.messageList = messageList;
- if ( outfileBase != null )
- {
- String filename = outfileBase + i + ".gpx";
- OsmTrack oldTrack = new OsmTrack();
- oldTrack.readGpx(filename);
- if ( track.equalsTrack( oldTrack ) )
- {
- continue;
- }
- oldTrack = null;
- track.writeGpx( filename );
- foundTrack = track;
- alternativeIndex = i;
- outfile = filename;
- }
- else
- {
- if ( i == routingContext.getAlternativeIdx(0,3) )
- {
- if ( "CSV".equals( System.getProperty( "reportFormat" ) ) )
- {
- track.dumpMessages( null, routingContext );
- }
- else
- {
- if ( !quite )
- {
- System.out.println( track.formatAsGpx() );
- }
- }
- foundTrack = track;
- }
- else
- {
- continue;
- }
- }
- if ( logfileBase != null )
- {
- String logfilename = logfileBase + i + ".csv";
- track.dumpMessages( logfilename, routingContext );
- }
- break;
- }
- long endTime = System.currentTimeMillis();
- logInfo( "execution time = " + (endTime-startTime0)/1000. + " seconds" );
- }
- catch( IllegalArgumentException e)
- {
- logException( e );
- }
- catch( Exception e)
- {
- logException( e );
- logThrowable( e );
- }
- catch( Error e)
- {
- cleanOnOOM();
- logException( e );
- logThrowable( e );
- }
- finally
- {
- if ( hasInfo() && routingContext.expctxWay != null )
- {
- logInfo( "expression cache stats=" + routingContext.expctxWay.cacheStats() );
- }
-
- ProfileCache.releaseProfile( routingContext );
-
- if ( nodesCache != null )
- {
- if ( hasInfo() && nodesCache != null )
- {
- logInfo( "NodesCache status before close=" + nodesCache.formatStatus() );
- }
- nodesCache.close();
- nodesCache = null;
- }
- openSet.clear();
- finished = true; // this signals termination to outside
-
- if ( infoLogWriter != null )
- {
- try { infoLogWriter.close(); } catch( Exception e ) {}
- infoLogWriter = null;
- }
-
- if ( stackSampler != null )
- {
- try { stackSampler.close(); } catch( Exception e ) {}
- stackSampler = null;
- }
-
- }
- }
-
- private void logException( Throwable t )
- {
- errorMessage = t instanceof IllegalArgumentException ? t.getMessage() : t.toString();
- logInfo( "Error (linksProcessed=" + linksProcessed + " open paths: " + openSet.getSize() + "): " + errorMessage );
- }
-
-
- public void doSearch()
- {
- try
- {
- MatchedWaypoint seedPoint = new MatchedWaypoint();
- seedPoint.waypoint = waypoints.get(0);
- List listOne = new ArrayList();
- listOne.add( seedPoint );
- matchWaypointsToNodes( listOne );
-
- findTrack( "seededSearch", seedPoint, null, null, null, false );
- }
- catch( IllegalArgumentException e)
- {
- logException( e );
- }
- catch( Exception e)
- {
- logException( e );
- logThrowable( e );
- }
- catch( Error e)
- {
- cleanOnOOM();
- logException( e );
- logThrowable( e );
- }
- finally
- {
- ProfileCache.releaseProfile( routingContext );
- if ( nodesCache != null )
- {
- nodesCache.close();
- nodesCache = null;
- }
- openSet.clear();
- finished = true; // this signals termination to outside
-
- if ( infoLogWriter != null )
- {
- try { infoLogWriter.close(); } catch( Exception e ) {}
- infoLogWriter = null;
- }
- }
- }
-
- public void cleanOnOOM()
- {
- terminate();
- }
-
- private OsmTrack findTrack( OsmTrack[] refTracks, OsmTrack[] lastTracks )
- {
- for(;;)
- {
- try
- {
- return tryFindTrack( refTracks, lastTracks );
- }
- catch( RoutingIslandException rie )
- {
- islandNodePairs.freezeTempPairs();
- nodesCache.clean( true );
- matchedWaypoints = null;
- }
- }
- }
-
- private OsmTrack tryFindTrack( OsmTrack[] refTracks, OsmTrack[] lastTracks )
- {
- OsmTrack totaltrack = new OsmTrack();
- int nUnmatched = waypoints.size();
-
- if ( hasInfo() )
- {
- for( OsmNodeNamed wp : waypoints )
- {
- logInfo( "wp=" + wp );
- }
- }
-
- // check for a track for that target
- OsmTrack nearbyTrack = null;
- if ( lastTracks[waypoints.size()-2] == null )
- {
- StringBuilder debugInfo = hasInfo() ? new StringBuilder() : null;
- nearbyTrack = OsmTrack.readBinary( routingContext.rawTrackPath, waypoints.get( waypoints.size()-1), routingContext.getNogoChecksums(), routingContext.profileTimestamp, debugInfo );
- if ( nearbyTrack != null )
- {
- nUnmatched--;
- }
- if ( hasInfo() )
- {
- boolean found = nearbyTrack != null;
- boolean dirty = found ? nearbyTrack.isDirty : false;
- logInfo( "read referenceTrack, found=" + found + " dirty=" + dirty + " " + debugInfo );
- }
- }
-
- if ( matchedWaypoints == null ) // could exist from the previous alternative level
- {
- matchedWaypoints = new ArrayList();
- for( int i=0; i 0 )
- {
- throw new IllegalArgumentException( "start island detected for section " + i );
- }
- }
- else
- {
- OsmTrack seg = findTrack( "target-island-check", matchedWaypoints.get(i+1), matchedWaypoints.get(i), null, null, false );
- if ( seg == null && nodeLimit > 0 )
- {
- throw new IllegalArgumentException( "target island detected for section " + i );
- }
- }
- }
- routingContext.inverseDirection = false;
- nodeLimit = 0;
-
- if ( nearbyTrack != null )
- {
- matchedWaypoints.add( nearbyTrack.endPoint );
- }
- }
-
- for( int i=0; i unmatchedWaypoints )
- {
- resetCache( false );
- nodesCache.matchWaypointsToNodes( unmatchedWaypoints, routingContext.waypointCatchingRange, islandNodePairs );
- }
-
- private OsmTrack searchTrack( MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack )
- {
- // remove nogos with waypoints inside
- try
- {
- List wpts2 = new ArrayList();
- wpts2.add( startWp.waypoint );
- wpts2.add( endWp.waypoint );
- boolean calcBeeline = routingContext.allInOneNogo(wpts2);
-
- if ( !calcBeeline ) return searchRoutedTrack( startWp, endWp, nearbyTrack, refTrack );
-
- // we want a beeline-segment
- OsmPath path = routingContext.createPath( new OsmLink( null, startWp.crosspoint ) );
- path = routingContext.createPath( path, new OsmLink( startWp.crosspoint, endWp.crosspoint ), null, false );
- return compileTrack( path, false );
- }
- finally
- {
- routingContext.restoreNogoList();
- }
- }
-
- private OsmTrack searchRoutedTrack( MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack )
- {
- OsmTrack track = null;
- double[] airDistanceCostFactors = new double[]{ routingContext.pass1coefficient, routingContext.pass2coefficient };
- boolean isDirty = false;
- IllegalArgumentException dirtyMessage = null;
-
- if ( nearbyTrack != null )
- {
- airDistanceCostFactor = 0.;
- try
- {
- track = findTrack( "re-routing", startWp, endWp, nearbyTrack , refTrack, true );
- }
- catch( IllegalArgumentException iae )
- {
- if ( terminated ) throw iae;
-
- // fast partial recalcs: if that timed out, but we had a match,
- // build the concatenation from the partial and the nearby track
- if ( matchPath != null )
- {
- track = mergeTrack( matchPath, nearbyTrack );
- isDirty = true;
- dirtyMessage = iae;
- logInfo( "using fast partial recalc" );
- }
- if ( maxRunningTime > 0 )
- {
- maxRunningTime += System.currentTimeMillis() - startTime; // reset timeout...
- }
- }
- }
-
- if ( track == null )
- {
- for( int cfi = 0; cfi < airDistanceCostFactors.length; cfi++ )
- {
- airDistanceCostFactor = airDistanceCostFactors[cfi];
-
- if ( airDistanceCostFactor < 0. )
- {
- continue;
- }
-
- OsmTrack t;
- try
- {
- t = findTrack( cfi == 0 ? "pass0" : "pass1", startWp, endWp, track , refTrack, false );
- }
- catch( IllegalArgumentException iae )
- {
- if ( !terminated && matchPath != null ) // timeout, but eventually prepare a dirty ref track
- {
- logInfo( "supplying dirty reference track after timeout" );
- foundRawTrack = mergeTrack( matchPath, track );
- foundRawTrack.endPoint = endWp;
- foundRawTrack.nogoChecksums = routingContext.getNogoChecksums();
- foundRawTrack.profileTimestamp = routingContext.profileTimestamp;
- foundRawTrack.isDirty = true;
- }
- throw iae;
- }
-
- if ( t == null && track != null && matchPath != null )
- {
- // ups, didn't find it, use a merge
- t = mergeTrack( matchPath, track );
- logInfo( "using sloppy merge cause pass1 didn't reach destination" );
- }
- if ( t != null )
- {
- track = t;
- }
- else
- {
- throw new IllegalArgumentException( "no track found at pass=" + cfi );
- }
- }
- }
- if ( track == null ) throw new IllegalArgumentException( "no track found" );
-
- boolean wasClean = nearbyTrack != null && !nearbyTrack.isDirty;
- if ( refTrack == null && !(wasClean && isDirty) ) // do not overwrite a clean with a dirty track
- {
- logInfo( "supplying new reference track, dirty=" + isDirty );
- track.endPoint = endWp;
- track.nogoChecksums = routingContext.getNogoChecksums();
- track.profileTimestamp = routingContext.profileTimestamp;
- track.isDirty = isDirty;
- foundRawTrack = track;
- }
-
- if ( !wasClean && isDirty )
- {
- throw dirtyMessage;
- }
-
- // final run for verbose log info and detail nodes
- airDistanceCostFactor = 0.;
- guideTrack = track;
- startTime = System.currentTimeMillis(); // reset timeout...
- try
- {
- OsmTrack tt = findTrack( "re-tracking", startWp, endWp, null , refTrack, false );
- if ( tt == null ) throw new IllegalArgumentException( "error re-tracking track" );
- return tt;
- }
- finally
- {
- guideTrack = null;
- }
- }
-
-
- private void resetCache( boolean detailed )
- {
- if ( hasInfo() && nodesCache != null )
- {
- logInfo( "NodesCache status before reset=" + nodesCache.formatStatus() );
- }
- long maxmem = routingContext.memoryclass * 1024L *1024L; // in MB
-
- nodesCache = new NodesCache(segmentDir, routingContext.expctxWay, routingContext.forceSecondaryData, maxmem, nodesCache, detailed );
- islandNodePairs.clearTempPairs();
- }
-
- private OsmPath getStartPath( OsmNode n1, OsmNode n2, MatchedWaypoint mwp, OsmNodeNamed endPos, boolean sameSegmentSearch )
- {
- if ( endPos != null )
- {
- endPos.radius = 1.5;
- }
- OsmPath p = getStartPath( n1, n2, new OsmNodeNamed( mwp.crosspoint ), endPos, sameSegmentSearch );
-
- // special case: start+end on same segment
- if ( p.cost >= 0 && sameSegmentSearch && endPos != null && endPos.radius < 1.5 )
- {
- p.treedepth = 0; // hack: mark for the final-check
- }
- return p;
- }
-
-
-
- private OsmPath getStartPath( OsmNode n1, OsmNode n2, OsmNodeNamed wp, OsmNodeNamed endPos, boolean sameSegmentSearch )
- {
- try
- {
- routingContext.setWaypoint( wp, sameSegmentSearch ? endPos : null, false );
- OsmPath bestPath = null;
- OsmLink bestLink = null;
- OsmLink startLink = new OsmLink( null, n1 );
- OsmPath startPath = routingContext.createPath( startLink );
- startLink.addLinkHolder( startPath, null );
- double minradius = 1e10;
- for( OsmLink link = n1.firstlink; link != null; link = link.getNext( n1 ) )
- {
- OsmNode nextNode = link.getTarget( n1 );
- if ( nextNode.isHollow() ) continue; // border node?
- if ( nextNode.firstlink == null ) continue; // don't care about dead ends
- if ( nextNode == n1 ) continue; // ?
- if ( nextNode != n2 ) continue; // just that link
-
- wp.radius = 1.5;
- OsmPath testPath = routingContext.createPath( startPath, link, null, guideTrack != null );
- testPath.airdistance = endPos == null ? 0 : nextNode.calcDistance( endPos );
- if ( wp.radius < minradius )
- {
- bestPath = testPath;
- minradius = wp.radius;
- bestLink = link;
- }
- }
- if ( bestLink != null )
- {
- bestLink.addLinkHolder( bestPath, n1 );
- }
- bestPath.treedepth = 1;
-
- return bestPath;
- }
- finally
- {
- routingContext.unsetWaypoint();
- }
- }
-
- private OsmTrack findTrack( String operationName, MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack costCuttingTrack, OsmTrack refTrack, boolean fastPartialRecalc )
- {
- try
- {
- List wpts2 = new ArrayList();
- if ( startWp != null ) wpts2.add( startWp.waypoint );
- if ( endWp != null ) wpts2.add( endWp.waypoint );
- routingContext.cleanNogoList(wpts2);
-
- boolean detailed = guideTrack != null;
- resetCache( detailed );
- nodesCache.nodesMap.cleanupMode = detailed ? 0 : ( routingContext.considerTurnRestrictions ? 2 : 1 );
- return _findTrack( operationName, startWp, endWp, costCuttingTrack, refTrack, fastPartialRecalc );
- }
- finally
- {
- routingContext.restoreNogoList();
- nodesCache.clean( false ); // clean only non-virgin caches
- }
- }
-
-
- private OsmTrack _findTrack( String operationName, MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack costCuttingTrack, OsmTrack refTrack, boolean fastPartialRecalc )
- {
- boolean verbose = guideTrack != null;
-
- int maxTotalCost = guideTrack != null ? guideTrack.cost + 5000 : 1000000000;
- int firstMatchCost = 1000000000;
-
- logInfo( "findtrack with airDistanceCostFactor=" + airDistanceCostFactor );
- if (costCuttingTrack != null ) logInfo( "costCuttingTrack.cost=" + costCuttingTrack.cost );
-
- matchPath = null;
- int nodesVisited = 0;
-
- long startNodeId1 = startWp.node1.getIdFromPos();
- long startNodeId2 = startWp.node2.getIdFromPos();
- long endNodeId1 = endWp == null ? -1L : endWp.node1.getIdFromPos();
- long endNodeId2 = endWp == null ? -1L : endWp.node2.getIdFromPos();
- OsmNode end1 = null;
- OsmNode end2 = null;
- OsmNodeNamed endPos = null;
-
- boolean sameSegmentSearch = false;
- OsmNode start1 = nodesCache.getGraphNode( startWp.node1 );
- OsmNode start2 = nodesCache.getGraphNode( startWp.node2 );
- if ( endWp != null )
- {
- end1 = nodesCache.getGraphNode( endWp.node1 );
- end2 = nodesCache.getGraphNode( endWp.node2 );
- nodesCache.nodesMap.endNode1 = end1;
- nodesCache.nodesMap.endNode2 = end2;
- endPos = new OsmNodeNamed( endWp.crosspoint );
- sameSegmentSearch = ( start1 == end1 && start2 == end2 ) || ( start1 == end2 && start2 == end1 );
- }
- if ( !nodesCache.obtainNonHollowNode( start1 ) )
- {
- return null;
- }
- nodesCache.expandHollowLinkTargets( start1 );
- if ( !nodesCache.obtainNonHollowNode( start2 ) )
- {
- return null;
- }
- nodesCache.expandHollowLinkTargets( start2 );
-
-
- routingContext.startDirectionValid = routingContext.forceUseStartDirection || fastPartialRecalc;
- routingContext.startDirectionValid &= routingContext.startDirection != null && !routingContext.inverseDirection;
- if ( routingContext.startDirectionValid )
- {
- logInfo( "using start direction " + routingContext.startDirection );
- }
-
- OsmPath startPath1 = getStartPath( start1, start2, startWp, endPos, sameSegmentSearch );
- OsmPath startPath2 = getStartPath( start2, start1, startWp, endPos, sameSegmentSearch );
-
- // check for an INITIAL match with the cost-cutting-track
- if ( costCuttingTrack != null )
- {
- OsmPathElement pe1 = costCuttingTrack.getLink( startNodeId1, startNodeId2 );
- if ( pe1 != null ) { logInfo( "initialMatch pe1.cost=" + pe1.cost );
- int c = startPath1.cost - pe1.cost; if ( c < 0 ) c = 0; if ( c < firstMatchCost ) firstMatchCost = c; }
-
- OsmPathElement pe2 = costCuttingTrack.getLink( startNodeId2, startNodeId1 );
- if ( pe2 != null ) { logInfo( "initialMatch pe2.cost=" + pe2.cost );
- int c = startPath2.cost - pe2.cost; if ( c < 0 ) c = 0; if ( c < firstMatchCost ) firstMatchCost = c; }
-
- if ( firstMatchCost < 1000000000 ) logInfo( "firstMatchCost from initial match=" + firstMatchCost );
- }
-
- synchronized( openSet )
- {
- openSet.clear();
- addToOpenset( startPath1 );
- addToOpenset( startPath2 );
- }
- ArrayList openBorderList = new ArrayList(4096);
- boolean memoryPanicMode = false;
- boolean needNonPanicProcessing = false;
-
- for(;;)
- {
- if ( terminated )
- {
- throw new IllegalArgumentException( "operation killed by thread-priority-watchdog after " + ( System.currentTimeMillis() - startTime)/1000 + " seconds" );
- }
-
- if ( maxRunningTime > 0 )
- {
- long timeout = ( matchPath == null && fastPartialRecalc ) ? maxRunningTime/3 : maxRunningTime;
- if ( System.currentTimeMillis() - startTime > timeout )
- {
- throw new IllegalArgumentException( operationName + " timeout after " + (timeout/1000) + " seconds" );
- }
- }
-
- synchronized( openSet )
- {
-
- OsmPath path = openSet.popLowestKeyValue();
- if ( path == null )
- {
- if ( openBorderList.isEmpty() )
- {
- break;
- }
- for( OsmPath p : openBorderList )
- {
- openSet.add( p.cost + (int)(p.airdistance*airDistanceCostFactor), p );
- }
- openBorderList.clear();
- memoryPanicMode = false;
- needNonPanicProcessing = true;
- continue;
- }
-
- if ( path.airdistance == -1 )
- {
- path.unregisterUpTree( routingContext );
- continue;
- }
-
- if ( directWeaving && nodesCache.hasHollowLinkTargets( path.getTargetNode() ) )
- {
- if ( !memoryPanicMode )
- {
- if ( !nodesCache.nodesMap.isInMemoryBounds( openSet.getSize(), false ) )
- {
-// System.out.println( "collecting..." );
- int nodesBefore = nodesCache.nodesMap.nodesCreated;
- int pathsBefore = openSet.getSize();
-
- nodesCache.nodesMap.collectOutreachers();
- for(;;)
- {
- OsmPath p3 = openSet.popLowestKeyValue();
- if ( p3 == null ) break;
- if ( p3.airdistance != -1 && nodesCache.nodesMap.canEscape( p3.getTargetNode() ) )
- {
- openBorderList.add( p3 );
- }
- }
- nodesCache.nodesMap.clearTemp();
- for( OsmPath p : openBorderList )
- {
- openSet.add( p.cost + (int)(p.airdistance*airDistanceCostFactor), p );
- }
- openBorderList.clear();
- logInfo( "collected, nodes/paths before=" + nodesBefore + "/" + pathsBefore + " after=" + nodesCache.nodesMap.nodesCreated + "/" + openSet.getSize() + " maxTotalCost=" + maxTotalCost );
- if ( !nodesCache.nodesMap.isInMemoryBounds( openSet.getSize(), true ) )
- {
- if ( maxTotalCost < 1000000000 || needNonPanicProcessing || fastPartialRecalc )
- {
- throw new IllegalArgumentException( "memory limit reached" );
- }
- memoryPanicMode = true;
- logInfo( "************************ memory limit reached, enabled memory panic mode *************************" );
- }
- }
- }
- if ( memoryPanicMode )
- {
- openBorderList.add( path );
- continue;
- }
- }
- needNonPanicProcessing = false;
-
-
- if ( fastPartialRecalc && matchPath != null && path.cost > 30L*firstMatchCost && !costCuttingTrack.isDirty )
- {
- logInfo( "early exit: firstMatchCost=" + firstMatchCost + " path.cost=" + path.cost );
-
- // use an early exit, unless there's a realistc chance to complete within the timeout
- if ( path.cost > maxTotalCost/2 && System.currentTimeMillis() - startTime < maxRunningTime/3 )
- {
- logInfo( "early exit supressed, running for completion, resetting timeout" );
- startTime = System.currentTimeMillis();
- fastPartialRecalc = false;
- }
- else
- {
- throw new IllegalArgumentException( "early exit for a close recalc" );
- }
- }
-
- if ( nodeLimit > 0 ) // check node-limit for target island search
- {
- if ( --nodeLimit == 0 )
- {
- return null;
- }
- }
-
- nodesVisited++;
- linksProcessed++;
-
- OsmLink currentLink = path.getLink();
- OsmNode sourceNode = path.getSourceNode();
- OsmNode currentNode = path.getTargetNode();
-
- if ( currentLink.isLinkUnused() )
- {
- path.unregisterUpTree( routingContext );
- continue;
- }
-
- long currentNodeId = currentNode.getIdFromPos();
- long sourceNodeId = sourceNode.getIdFromPos();
-
- if ( !path.didEnterDestinationArea() )
- {
- islandNodePairs.addTempPair( sourceNodeId, currentNodeId );
- }
-
- if ( path.treedepth != 1 )
- {
- if ( path.treedepth == 0 ) // hack: sameSegment Paths marked treedepth=0 to pass above check
- {
- path.treedepth = 1;
- }
-
- if ( ( sourceNodeId == endNodeId1 && currentNodeId == endNodeId2 )
- || ( sourceNodeId == endNodeId2 && currentNodeId == endNodeId1 ) )
- {
- // track found, compile
- logInfo( "found track at cost " + path.cost + " nodesVisited = " + nodesVisited );
- OsmTrack t = compileTrack( path, verbose );
- t.showspeed = routingContext.showspeed;
- t.showSpeedProfile = routingContext.showSpeedProfile;
- return t;
- }
-
- // check for a match with the cost-cutting-track
- if ( costCuttingTrack != null )
- {
- OsmPathElement pe = costCuttingTrack.getLink( sourceNodeId, currentNodeId );
- if ( pe != null )
- {
- // remember first match cost for fast termination of partial recalcs
- int parentcost = path.originElement == null ? 0 : path.originElement.cost;
-
- // hitting start-element of costCuttingTrack?
- int c = path.cost - parentcost - pe.cost;
- if ( c > 0 ) parentcost += c;
-
- if ( parentcost < firstMatchCost ) firstMatchCost = parentcost;
-
- int costEstimate = path.cost
- + path.elevationCorrection( routingContext )
- + ( costCuttingTrack.cost - pe.cost );
- if ( costEstimate <= maxTotalCost )
- {
- matchPath = OsmPathElement.create( path, routingContext.countTraffic );
- }
- if ( costEstimate < maxTotalCost )
- {
- logInfo( "maxcost " + maxTotalCost + " -> " + costEstimate );
- maxTotalCost = costEstimate;
- }
- }
- }
- }
-
- int keepPathAirdistance = path.airdistance;
- OsmLinkHolder firstLinkHolder = currentLink.getFirstLinkHolder( sourceNode );
- for( OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink() )
- {
- ((OsmPath)linkHolder).airdistance = -1; // invalidate the entry in the open set;
- }
-
- if ( path.treedepth > 1 )
- {
- boolean isBidir = currentLink.isBidirectional();
- sourceNode.unlinkLink( currentLink );
-
- // if the counterlink is alive and does not yet have a path, remove it
- if ( isBidir && currentLink.getFirstLinkHolder( currentNode ) == null && !routingContext.considerTurnRestrictions )
- {
- currentNode.unlinkLink( currentLink );
- }
- }
-
- // recheck cutoff before doing expensive stuff
- if ( path.cost + path.airdistance > maxTotalCost + 100 )
- {
- path.unregisterUpTree( routingContext );
- continue;
- }
-
- nodesCache.nodesMap.currentMaxCost = maxTotalCost;
- nodesCache.nodesMap.currentPathCost = path.cost;
- nodesCache.nodesMap.destination = endPos;
-
- routingContext.firstPrePath = null;
-
- for( OsmLink link = currentNode.firstlink; link != null; link = link.getNext( currentNode) )
- {
- OsmNode nextNode = link.getTarget( currentNode );
-
- if ( ! nodesCache.obtainNonHollowNode( nextNode ) )
- {
- continue; // border node?
- }
- if ( nextNode.firstlink == null )
- {
- continue; // don't care about dead ends
- }
- if ( nextNode == sourceNode )
- {
- continue; // border node?
- }
-
- OsmPrePath prePath = routingContext.createPrePath( path, link );
- if ( prePath != null )
- {
- prePath.next = routingContext.firstPrePath;
- routingContext.firstPrePath = prePath;
- }
- }
-
- for( OsmLink link = currentNode.firstlink; link != null; link = link.getNext( currentNode) )
- {
- OsmNode nextNode = link.getTarget( currentNode );
-
- if ( ! nodesCache.obtainNonHollowNode( nextNode ) )
- {
- continue; // border node?
- }
- if ( nextNode.firstlink == null )
- {
- continue; // don't care about dead ends
- }
- if ( nextNode == sourceNode )
- {
- continue; // border node?
- }
-
- if ( guideTrack != null )
- {
- int gidx = path.treedepth + 1;
- if ( gidx >= guideTrack.nodes.size() )
- {
- continue;
- }
- OsmPathElement guideNode = guideTrack.nodes.get( routingContext.inverseRouting ? guideTrack.nodes.size() - 1 - gidx : gidx );
- long nextId = nextNode.getIdFromPos();
- if ( nextId != guideNode.getIdFromPos() )
- {
- // not along the guide-track, discard, but register for voice-hint processing
- if ( routingContext.turnInstructionMode > 0 )
- {
- OsmPath detour = routingContext.createPath( path, link, refTrack, true );
- if ( detour.cost >= 0. && nextId != startNodeId1 && nextId != startNodeId2 )
- {
- guideTrack.registerDetourForId( currentNode.getIdFromPos(), OsmPathElement.create( detour, false ) );
- }
- }
- continue;
- }
- }
-
- OsmPath bestPath = null;
-
- boolean isFinalLink = false;
- long targetNodeId = nextNode.getIdFromPos();
- if ( currentNodeId == endNodeId1 || currentNodeId == endNodeId2 )
- {
- if ( targetNodeId == endNodeId1 || targetNodeId == endNodeId2 )
- {
- isFinalLink = true;
- }
- }
-
- for( OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink() )
- {
- OsmPath otherPath = (OsmPath)linkHolder;
- try
- {
- if ( isFinalLink )
- {
- endPos.radius = 1.5; // 1.5 meters is the upper limit that will not change the unit-test result..
- routingContext.setWaypoint( endPos, true );
- }
- OsmPath testPath = routingContext.createPath( otherPath, link, refTrack, guideTrack != null );
- if ( testPath.cost >= 0 && ( bestPath == null || testPath.cost < bestPath.cost ) )
- {
- bestPath = testPath;
- }
- }
- finally
- {
- if ( isFinalLink )
- {
- routingContext.unsetWaypoint();
- }
- }
- }
- if ( bestPath != null )
- {
- boolean trafficSim = endPos == null;
-
- bestPath.airdistance = trafficSim ? keepPathAirdistance : ( isFinalLink ? 0 : nextNode.calcDistance( endPos ) );
-
- boolean inRadius = boundary == null || boundary.isInBoundary( nextNode, bestPath.cost );
-
- if ( inRadius && ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 100 ) )
- {
- // add only if this may beat an existing path for that link
- OsmLinkHolder dominator = link.getFirstLinkHolder( currentNode );
- while( !trafficSim && dominator != null )
- {
- OsmPath dp = (OsmPath)dominator;
- if ( dp.airdistance != -1 && bestPath.definitlyWorseThan( dp, routingContext ) )
- {
- break;
- }
- dominator = dominator.getNextForLink();
- }
-
- if ( dominator == null )
- {
- if ( trafficSim && boundary != null && path.cost == 0 && bestPath.cost > 0 )
- {
- bestPath.airdistance += boundary.getBoundaryDistance( nextNode );
- }
- bestPath.treedepth = path.treedepth + 1;
- link.addLinkHolder( bestPath, currentNode );
- addToOpenset( bestPath );
- }
- }
- }
- }
-
- path.unregisterUpTree( routingContext );
- }
- }
-
- if ( nodesVisited < MAXNODES_ISLAND_CHECK && islandNodePairs.getFreezeCount() < 5 )
- {
- throw new RoutingIslandException();
- }
-
- return null;
- }
-
- private void addToOpenset( OsmPath path )
- {
- if ( path.cost >= 0 )
- {
- openSet.add( path.cost + (int)(path.airdistance*airDistanceCostFactor), path );
- path.registerUpTree();
- }
- }
-
- private OsmTrack compileTrack( OsmPath path, boolean verbose )
- {
- OsmPathElement element = OsmPathElement.create( path, false );
-
- // for final track, cut endnode
- if ( guideTrack != null )
- {
- element = element.origin;
- }
-
- float totalTime = element.getTime();
- float totalEnergy = element.getEnergy();
-
- OsmTrack track = new OsmTrack();
- track.cost = path.cost;
- track.energy = (int)path.getTotalEnergy();
-
- int distance = 0;
- double ascend = 0;
- double ehb = 0.;
-
- short ele_start = Short.MIN_VALUE;
- short ele_end = Short.MIN_VALUE;
-
- double eleFactor = routingContext.inverseRouting ? -0.25 : 0.25;
- while ( element != null )
- {
- if ( guideTrack != null && element.message == null )
- {
- element.message = new MessageData();
- }
-
- if ( routingContext.inverseRouting )
- {
- element.setTime( totalTime - element.getTime() );
- element.setEnergy( totalEnergy - element.getEnergy() );
- track.nodes.add( element );
- }
- else
- {
- track.nodes.add( 0, element );
- }
-
- OsmPathElement nextElement = element.origin;
-
- short ele = element.getSElev();
- if ( ele != Short.MIN_VALUE ) ele_start = ele;
- if ( ele_end == Short.MIN_VALUE ) ele_end = ele;
-
- if ( nextElement != null )
- {
- distance += element.calcDistance( nextElement );
- short ele_next = nextElement.getSElev();
- if ( ele_next != Short.MIN_VALUE )
- {
- ehb = ehb + (ele - ele_next)*eleFactor;
- }
- if ( ehb > 10. )
- {
- ascend += ehb-10.;
- ehb = 10.;
- }
- else if ( ehb < 0. )
- {
- ehb = 0.;
- }
- }
- element = nextElement ;
- }
- ascend += ehb;
- track.distance = distance;
- track.ascend = (int)ascend;
- track.plainAscend = (int)(( ele_end - ele_start )*eleFactor+0.5);
- logInfo( "track-length = " + track.distance );
- logInfo( "filtered ascend = " + track.ascend );
- track.buildMap();
-
- // for final track..
- if ( guideTrack != null )
- {
- track.copyDetours( guideTrack );
- track.processVoiceHints( routingContext );
- track.prepareSpeedProfile( routingContext );
- }
- return track;
- }
-
- private OsmTrack mergeTrack( OsmPathElement match, OsmTrack oldTrack )
- {
- logInfo( "**************** merging match=" + match.cost + " with oldTrack=" + oldTrack.cost );
- OsmPathElement element = match;
- OsmTrack track = new OsmTrack();
- track.cost = oldTrack.cost;
-
- while ( element != null )
- {
- track.addNode( element );
- element = element.origin ;
- }
- long lastId = 0;
- long id1 = match.getIdFromPos();
- long id0 = match.origin == null ? 0 : match.origin.getIdFromPos();
- boolean appending = false;
- for( OsmPathElement n : oldTrack.nodes )
- {
- if ( appending )
- {
- track.nodes.add( n );
- }
-
- long id = n.getIdFromPos();
- if ( id == id1 && lastId == id0 )
- {
- appending = true;
- }
- lastId = id;
- }
-
-
- track.buildMap();
- return track;
- }
-
- public int getPathPeak()
- {
- synchronized( openSet )
- {
- return openSet.getPeakSize();
- }
- }
-
- public int[] getOpenSet()
- {
- if ( extract == null )
- {
- extract = new Object[500];
- }
-
- synchronized( openSet )
- {
- if ( guideTrack != null )
- {
- ArrayList nodes = guideTrack.nodes;
- int[] res = new int[nodes.size() * 2];
- int i = 0;
- for( OsmPathElement n : nodes )
- {
- res[i++] = n.getILon();
- res[i++] = n.getILat();
- }
- return res;
- }
-
- int size = openSet.getExtract(extract);
- int[] res = new int[size * 2];
- for( int i=0, j=0; i openSet = new SortedHeap();
+ private boolean finished = false;
+
+ protected List waypoints = null;
+ protected List matchedWaypoints;
+ private int linksProcessed = 0;
+
+ private int nodeLimit; // used for target island search
+ private int MAXNODES_ISLAND_CHECK = 500;
+ private OsmNodePairSet islandNodePairs = new OsmNodePairSet(MAXNODES_ISLAND_CHECK);
+
+ protected OsmTrack foundTrack = new OsmTrack();
+ private OsmTrack foundRawTrack = null;
+ private int alternativeIndex = 0;
+
+ protected String errorMessage = null;
+
+ private volatile boolean terminated;
+
+ protected File segmentDir;
+ private String outfileBase;
+ private String logfileBase;
+ private boolean infoLogEnabled;
+ private Writer infoLogWriter;
+ private StackSampler stackSampler;
+ protected RoutingContext routingContext;
+
+ public double airDistanceCostFactor;
+ private OsmTrack guideTrack;
+
+ private OsmPathElement matchPath;
+
+ private long startTime;
+ private long maxRunningTime;
+ public SearchBoundary boundary;
+
+ public boolean quite = false;
+
+ private Object[] extract;
+
+ private boolean directWeaving = !Boolean.getBoolean("disableDirectWeaving");
+ private String outfile;
+
+ public RoutingEngine(String outfileBase, String logfileBase, File segmentDir,
+ List waypoints, RoutingContext rc) {
+ this.segmentDir = segmentDir;
+ this.outfileBase = outfileBase;
+ this.logfileBase = logfileBase;
+ this.waypoints = waypoints;
+ this.infoLogEnabled = outfileBase != null;
+ this.routingContext = rc;
+
+ File baseFolder = new File(routingContext.localFunction).getParentFile();
+ baseFolder = baseFolder == null ? null : baseFolder.getParentFile();
+ if (baseFolder != null) {
+ try {
+ File debugLog = new File(baseFolder, "debug.txt");
+ if (debugLog.exists()) {
+ infoLogWriter = new FileWriter(debugLog, true);
+ logInfo("********** start request at ");
+ logInfo("********** " + new Date());
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException("cannot open debug-log:" + ioe);
+ }
+
+ File stackLog = new File(baseFolder, "stacks.txt");
+ if (stackLog.exists()) {
+ stackSampler = new StackSampler(stackLog, 1000);
+ stackSampler.start();
+ logInfo("********** started stacksampling");
+ }
+ }
+ boolean cachedProfile = ProfileCache.parseProfile(rc);
+ if (hasInfo()) {
+ logInfo("parsed profile " + rc.localFunction + " cached=" + cachedProfile);
+ }
+ }
+
+ private boolean hasInfo() {
+ return infoLogEnabled || infoLogWriter != null;
+ }
+
+ private void logInfo(String s) {
+ if (infoLogEnabled) {
+ System.out.println(s);
+ }
+ if (infoLogWriter != null) {
+ try {
+ infoLogWriter.write(s);
+ infoLogWriter.write('\n');
+ infoLogWriter.flush();
+ } catch (IOException io) {
+ infoLogWriter = null;
+ }
+ }
+ }
+
+ private void logThrowable(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ logInfo(sw.toString());
+ }
+
+ public void run() {
+ doRun(0);
+ }
+
+ public void doRun(long maxRunningTime) {
+ try {
+ startTime = System.currentTimeMillis();
+ long startTime0 = startTime;
+ this.maxRunningTime = maxRunningTime;
+ int nsections = waypoints.size() - 1;
+ OsmTrack[] refTracks = new OsmTrack[nsections]; // used ways for alternatives
+ OsmTrack[] lastTracks = new OsmTrack[nsections];
+ OsmTrack track = null;
+ ArrayList messageList = new ArrayList();
+ for (int i = 0; ; i++) {
+ track = findTrack(refTracks, lastTracks);
+ track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
+ + " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
+ if (track.energy != 0) {
+ track.message += " energy=" + track.getFormattedEnergy() + " time=" + track.getFormattedTime2();
+ }
+ track.name = "brouter_" + routingContext.getProfileName() + "_" + i;
+
+ messageList.add(track.message);
+ track.messageList = messageList;
+ if (outfileBase != null) {
+ String filename = outfileBase + i + ".gpx";
+ OsmTrack oldTrack = new OsmTrack();
+ oldTrack.readGpx(filename);
+ if (track.equalsTrack(oldTrack)) {
+ continue;
+ }
+ oldTrack = null;
+ track.writeGpx(filename);
+ foundTrack = track;
+ alternativeIndex = i;
+ outfile = filename;
+ } else {
+ if (i == routingContext.getAlternativeIdx(0, 3)) {
+ if ("CSV".equals(System.getProperty("reportFormat"))) {
+ track.dumpMessages(null, routingContext);
+ } else {
+ if (!quite) {
+ System.out.println(track.formatAsGpx());
+ }
+ }
+ foundTrack = track;
+ } else {
+ continue;
+ }
+ }
+ if (logfileBase != null) {
+ String logfilename = logfileBase + i + ".csv";
+ track.dumpMessages(logfilename, routingContext);
+ }
+ break;
+ }
+ long endTime = System.currentTimeMillis();
+ logInfo("execution time = " + (endTime - startTime0) / 1000. + " seconds");
+ } catch (IllegalArgumentException e) {
+ logException(e);
+ } catch (Exception e) {
+ logException(e);
+ logThrowable(e);
+ } catch (Error e) {
+ cleanOnOOM();
+ logException(e);
+ logThrowable(e);
+ } finally {
+ if (hasInfo() && routingContext.expctxWay != null) {
+ logInfo("expression cache stats=" + routingContext.expctxWay.cacheStats());
+ }
+
+ ProfileCache.releaseProfile(routingContext);
+
+ if (nodesCache != null) {
+ if (hasInfo() && nodesCache != null) {
+ logInfo("NodesCache status before close=" + nodesCache.formatStatus());
+ }
+ nodesCache.close();
+ nodesCache = null;
+ }
+ openSet.clear();
+ finished = true; // this signals termination to outside
+
+ if (infoLogWriter != null) {
+ try {
+ infoLogWriter.close();
+ } catch (Exception e) {
+ }
+ infoLogWriter = null;
+ }
+
+ if (stackSampler != null) {
+ try {
+ stackSampler.close();
+ } catch (Exception e) {
+ }
+ stackSampler = null;
+ }
+
+ }
+ }
+
+ private void logException(Throwable t) {
+ errorMessage = t instanceof IllegalArgumentException ? t.getMessage() : t.toString();
+ logInfo("Error (linksProcessed=" + linksProcessed + " open paths: " + openSet.getSize() + "): " + errorMessage);
+ }
+
+
+ public void doSearch() {
+ try {
+ MatchedWaypoint seedPoint = new MatchedWaypoint();
+ seedPoint.waypoint = waypoints.get(0);
+ List listOne = new ArrayList();
+ listOne.add(seedPoint);
+ matchWaypointsToNodes(listOne);
+
+ findTrack("seededSearch", seedPoint, null, null, null, false);
+ } catch (IllegalArgumentException e) {
+ logException(e);
+ } catch (Exception e) {
+ logException(e);
+ logThrowable(e);
+ } catch (Error e) {
+ cleanOnOOM();
+ logException(e);
+ logThrowable(e);
+ } finally {
+ ProfileCache.releaseProfile(routingContext);
+ if (nodesCache != null) {
+ nodesCache.close();
+ nodesCache = null;
+ }
+ openSet.clear();
+ finished = true; // this signals termination to outside
+
+ if (infoLogWriter != null) {
+ try {
+ infoLogWriter.close();
+ } catch (Exception e) {
+ }
+ infoLogWriter = null;
+ }
+ }
+ }
+
+ public void cleanOnOOM() {
+ terminate();
+ }
+
+ private OsmTrack findTrack(OsmTrack[] refTracks, OsmTrack[] lastTracks) {
+ for (; ; ) {
+ try {
+ return tryFindTrack(refTracks, lastTracks);
+ } catch (RoutingIslandException rie) {
+ islandNodePairs.freezeTempPairs();
+ nodesCache.clean(true);
+ matchedWaypoints = null;
+ }
+ }
+ }
+
+ private OsmTrack tryFindTrack(OsmTrack[] refTracks, OsmTrack[] lastTracks) {
+ OsmTrack totaltrack = new OsmTrack();
+ int nUnmatched = waypoints.size();
+
+ if (hasInfo()) {
+ for (OsmNodeNamed wp : waypoints) {
+ logInfo("wp=" + wp);
+ }
+ }
+
+ // check for a track for that target
+ OsmTrack nearbyTrack = null;
+ if (lastTracks[waypoints.size() - 2] == null) {
+ StringBuilder debugInfo = hasInfo() ? new StringBuilder() : null;
+ nearbyTrack = OsmTrack.readBinary(routingContext.rawTrackPath, waypoints.get(waypoints.size() - 1), routingContext.getNogoChecksums(), routingContext.profileTimestamp, debugInfo);
+ if (nearbyTrack != null) {
+ nUnmatched--;
+ }
+ if (hasInfo()) {
+ boolean found = nearbyTrack != null;
+ boolean dirty = found ? nearbyTrack.isDirty : false;
+ logInfo("read referenceTrack, found=" + found + " dirty=" + dirty + " " + debugInfo);
+ }
+ }
+
+ if (matchedWaypoints == null) // could exist from the previous alternative level
+ {
+ matchedWaypoints = new ArrayList();
+ for (int i = 0; i < nUnmatched; i++) {
+ MatchedWaypoint mwp = new MatchedWaypoint();
+ mwp.waypoint = waypoints.get(i);
+ mwp.name = waypoints.get(i).name;
+ matchedWaypoints.add(mwp);
+ }
+ matchWaypointsToNodes(matchedWaypoints);
+
+ // detect target islands: restricted search in inverse direction
+ routingContext.inverseDirection = !routingContext.inverseRouting;
+ airDistanceCostFactor = 0.;
+ for (int i = 0; i < matchedWaypoints.size() - 1; i++) {
+ nodeLimit = MAXNODES_ISLAND_CHECK;
+ if (routingContext.inverseRouting) {
+ OsmTrack seg = findTrack("start-island-check", matchedWaypoints.get(i), matchedWaypoints.get(i + 1), null, null, false);
+ if (seg == null && nodeLimit > 0) {
+ throw new IllegalArgumentException("start island detected for section " + i);
+ }
+ } else {
+ OsmTrack seg = findTrack("target-island-check", matchedWaypoints.get(i + 1), matchedWaypoints.get(i), null, null, false);
+ if (seg == null && nodeLimit > 0) {
+ throw new IllegalArgumentException("target island detected for section " + i);
+ }
+ }
+ }
+ routingContext.inverseDirection = false;
+ nodeLimit = 0;
+
+ if (nearbyTrack != null) {
+ matchedWaypoints.add(nearbyTrack.endPoint);
+ }
+ }
+
+ for (int i = 0; i < matchedWaypoints.size() - 1; i++) {
+ if (lastTracks[i] != null) {
+ if (refTracks[i] == null) refTracks[i] = new OsmTrack();
+ refTracks[i].addNodes(lastTracks[i]);
+ }
+
+ OsmTrack seg;
+ if (routingContext.inverseRouting) {
+ routingContext.inverseDirection = true;
+ seg = searchTrack(matchedWaypoints.get(i + 1), matchedWaypoints.get(i), null, refTracks[i]);
+ routingContext.inverseDirection = false;
+ } else {
+ seg = searchTrack(matchedWaypoints.get(i), matchedWaypoints.get(i + 1), i == matchedWaypoints.size() - 2 ? nearbyTrack : null, refTracks[i]);
+ }
+
+ if (seg == null) return null;
+ totaltrack.appendTrack(seg);
+ lastTracks[i] = seg;
+ }
+ if (routingContext.poipoints != null) totaltrack.pois = routingContext.poipoints;
+ totaltrack.matchedWaypoints = matchedWaypoints;
+ return totaltrack;
+ }
+
+ // geometric position matching finding the nearest routable way-section
+ private void matchWaypointsToNodes(List unmatchedWaypoints) {
+ resetCache(false);
+ nodesCache.matchWaypointsToNodes(unmatchedWaypoints, routingContext.waypointCatchingRange, islandNodePairs);
+ }
+
+ private OsmTrack searchTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) {
+ // remove nogos with waypoints inside
+ try {
+ List wpts2 = new ArrayList();
+ wpts2.add(startWp.waypoint);
+ wpts2.add(endWp.waypoint);
+ boolean calcBeeline = routingContext.allInOneNogo(wpts2);
+
+ if (!calcBeeline) return searchRoutedTrack(startWp, endWp, nearbyTrack, refTrack);
+
+ // we want a beeline-segment
+ OsmPath path = routingContext.createPath(new OsmLink(null, startWp.crosspoint));
+ path = routingContext.createPath(path, new OsmLink(startWp.crosspoint, endWp.crosspoint), null, false);
+ return compileTrack(path, false);
+ } finally {
+ routingContext.restoreNogoList();
+ }
+ }
+
+ private OsmTrack searchRoutedTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) {
+ OsmTrack track = null;
+ double[] airDistanceCostFactors = new double[]{routingContext.pass1coefficient, routingContext.pass2coefficient};
+ boolean isDirty = false;
+ IllegalArgumentException dirtyMessage = null;
+
+ if (nearbyTrack != null) {
+ airDistanceCostFactor = 0.;
+ try {
+ track = findTrack("re-routing", startWp, endWp, nearbyTrack, refTrack, true);
+ } catch (IllegalArgumentException iae) {
+ if (terminated) throw iae;
+
+ // fast partial recalcs: if that timed out, but we had a match,
+ // build the concatenation from the partial and the nearby track
+ if (matchPath != null) {
+ track = mergeTrack(matchPath, nearbyTrack);
+ isDirty = true;
+ dirtyMessage = iae;
+ logInfo("using fast partial recalc");
+ }
+ if (maxRunningTime > 0) {
+ maxRunningTime += System.currentTimeMillis() - startTime; // reset timeout...
+ }
+ }
+ }
+
+ if (track == null) {
+ for (int cfi = 0; cfi < airDistanceCostFactors.length; cfi++) {
+ airDistanceCostFactor = airDistanceCostFactors[cfi];
+
+ if (airDistanceCostFactor < 0.) {
+ continue;
+ }
+
+ OsmTrack t;
+ try {
+ t = findTrack(cfi == 0 ? "pass0" : "pass1", startWp, endWp, track, refTrack, false);
+ } catch (IllegalArgumentException iae) {
+ if (!terminated && matchPath != null) // timeout, but eventually prepare a dirty ref track
+ {
+ logInfo("supplying dirty reference track after timeout");
+ foundRawTrack = mergeTrack(matchPath, track);
+ foundRawTrack.endPoint = endWp;
+ foundRawTrack.nogoChecksums = routingContext.getNogoChecksums();
+ foundRawTrack.profileTimestamp = routingContext.profileTimestamp;
+ foundRawTrack.isDirty = true;
+ }
+ throw iae;
+ }
+
+ if (t == null && track != null && matchPath != null) {
+ // ups, didn't find it, use a merge
+ t = mergeTrack(matchPath, track);
+ logInfo("using sloppy merge cause pass1 didn't reach destination");
+ }
+ if (t != null) {
+ track = t;
+ } else {
+ throw new IllegalArgumentException("no track found at pass=" + cfi);
+ }
+ }
+ }
+ if (track == null) throw new IllegalArgumentException("no track found");
+
+ boolean wasClean = nearbyTrack != null && !nearbyTrack.isDirty;
+ if (refTrack == null && !(wasClean && isDirty)) // do not overwrite a clean with a dirty track
+ {
+ logInfo("supplying new reference track, dirty=" + isDirty);
+ track.endPoint = endWp;
+ track.nogoChecksums = routingContext.getNogoChecksums();
+ track.profileTimestamp = routingContext.profileTimestamp;
+ track.isDirty = isDirty;
+ foundRawTrack = track;
+ }
+
+ if (!wasClean && isDirty) {
+ throw dirtyMessage;
+ }
+
+ // final run for verbose log info and detail nodes
+ airDistanceCostFactor = 0.;
+ guideTrack = track;
+ startTime = System.currentTimeMillis(); // reset timeout...
+ try {
+ OsmTrack tt = findTrack("re-tracking", startWp, endWp, null, refTrack, false);
+ if (tt == null) throw new IllegalArgumentException("error re-tracking track");
+ return tt;
+ } finally {
+ guideTrack = null;
+ }
+ }
+
+
+ private void resetCache(boolean detailed) {
+ if (hasInfo() && nodesCache != null) {
+ logInfo("NodesCache status before reset=" + nodesCache.formatStatus());
+ }
+ long maxmem = routingContext.memoryclass * 1024L * 1024L; // in MB
+
+ nodesCache = new NodesCache(segmentDir, routingContext.expctxWay, routingContext.forceSecondaryData, maxmem, nodesCache, detailed);
+ islandNodePairs.clearTempPairs();
+ }
+
+ private OsmPath getStartPath(OsmNode n1, OsmNode n2, MatchedWaypoint mwp, OsmNodeNamed endPos, boolean sameSegmentSearch) {
+ if (endPos != null) {
+ endPos.radius = 1.5;
+ }
+ OsmPath p = getStartPath(n1, n2, new OsmNodeNamed(mwp.crosspoint), endPos, sameSegmentSearch);
+
+ // special case: start+end on same segment
+ if (p.cost >= 0 && sameSegmentSearch && endPos != null && endPos.radius < 1.5) {
+ p.treedepth = 0; // hack: mark for the final-check
+ }
+ return p;
+ }
+
+
+ private OsmPath getStartPath(OsmNode n1, OsmNode n2, OsmNodeNamed wp, OsmNodeNamed endPos, boolean sameSegmentSearch) {
+ try {
+ routingContext.setWaypoint(wp, sameSegmentSearch ? endPos : null, false);
+ OsmPath bestPath = null;
+ OsmLink bestLink = null;
+ OsmLink startLink = new OsmLink(null, n1);
+ OsmPath startPath = routingContext.createPath(startLink);
+ startLink.addLinkHolder(startPath, null);
+ double minradius = 1e10;
+ for (OsmLink link = n1.firstlink; link != null; link = link.getNext(n1)) {
+ OsmNode nextNode = link.getTarget(n1);
+ if (nextNode.isHollow()) continue; // border node?
+ if (nextNode.firstlink == null) continue; // don't care about dead ends
+ if (nextNode == n1) continue; // ?
+ if (nextNode != n2) continue; // just that link
+
+ wp.radius = 1.5;
+ OsmPath testPath = routingContext.createPath(startPath, link, null, guideTrack != null);
+ testPath.airdistance = endPos == null ? 0 : nextNode.calcDistance(endPos);
+ if (wp.radius < minradius) {
+ bestPath = testPath;
+ minradius = wp.radius;
+ bestLink = link;
+ }
+ }
+ if (bestLink != null) {
+ bestLink.addLinkHolder(bestPath, n1);
+ }
+ bestPath.treedepth = 1;
+
+ return bestPath;
+ } finally {
+ routingContext.unsetWaypoint();
+ }
+ }
+
+ private OsmTrack findTrack(String operationName, MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack costCuttingTrack, OsmTrack refTrack, boolean fastPartialRecalc) {
+ try {
+ List wpts2 = new ArrayList();
+ if (startWp != null) wpts2.add(startWp.waypoint);
+ if (endWp != null) wpts2.add(endWp.waypoint);
+ routingContext.cleanNogoList(wpts2);
+
+ boolean detailed = guideTrack != null;
+ resetCache(detailed);
+ nodesCache.nodesMap.cleanupMode = detailed ? 0 : (routingContext.considerTurnRestrictions ? 2 : 1);
+ return _findTrack(operationName, startWp, endWp, costCuttingTrack, refTrack, fastPartialRecalc);
+ } finally {
+ routingContext.restoreNogoList();
+ nodesCache.clean(false); // clean only non-virgin caches
+ }
+ }
+
+
+ private OsmTrack _findTrack(String operationName, MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack costCuttingTrack, OsmTrack refTrack, boolean fastPartialRecalc) {
+ boolean verbose = guideTrack != null;
+
+ int maxTotalCost = guideTrack != null ? guideTrack.cost + 5000 : 1000000000;
+ int firstMatchCost = 1000000000;
+
+ logInfo("findtrack with airDistanceCostFactor=" + airDistanceCostFactor);
+ if (costCuttingTrack != null) logInfo("costCuttingTrack.cost=" + costCuttingTrack.cost);
+
+ matchPath = null;
+ int nodesVisited = 0;
+
+ long startNodeId1 = startWp.node1.getIdFromPos();
+ long startNodeId2 = startWp.node2.getIdFromPos();
+ long endNodeId1 = endWp == null ? -1L : endWp.node1.getIdFromPos();
+ long endNodeId2 = endWp == null ? -1L : endWp.node2.getIdFromPos();
+ OsmNode end1 = null;
+ OsmNode end2 = null;
+ OsmNodeNamed endPos = null;
+
+ boolean sameSegmentSearch = false;
+ OsmNode start1 = nodesCache.getGraphNode(startWp.node1);
+ OsmNode start2 = nodesCache.getGraphNode(startWp.node2);
+ if (endWp != null) {
+ end1 = nodesCache.getGraphNode(endWp.node1);
+ end2 = nodesCache.getGraphNode(endWp.node2);
+ nodesCache.nodesMap.endNode1 = end1;
+ nodesCache.nodesMap.endNode2 = end2;
+ endPos = new OsmNodeNamed(endWp.crosspoint);
+ sameSegmentSearch = (start1 == end1 && start2 == end2) || (start1 == end2 && start2 == end1);
+ }
+ if (!nodesCache.obtainNonHollowNode(start1)) {
+ return null;
+ }
+ nodesCache.expandHollowLinkTargets(start1);
+ if (!nodesCache.obtainNonHollowNode(start2)) {
+ return null;
+ }
+ nodesCache.expandHollowLinkTargets(start2);
+
+
+ routingContext.startDirectionValid = routingContext.forceUseStartDirection || fastPartialRecalc;
+ routingContext.startDirectionValid &= routingContext.startDirection != null && !routingContext.inverseDirection;
+ if (routingContext.startDirectionValid) {
+ logInfo("using start direction " + routingContext.startDirection);
+ }
+
+ OsmPath startPath1 = getStartPath(start1, start2, startWp, endPos, sameSegmentSearch);
+ OsmPath startPath2 = getStartPath(start2, start1, startWp, endPos, sameSegmentSearch);
+
+ // check for an INITIAL match with the cost-cutting-track
+ if (costCuttingTrack != null) {
+ OsmPathElement pe1 = costCuttingTrack.getLink(startNodeId1, startNodeId2);
+ if (pe1 != null) {
+ logInfo("initialMatch pe1.cost=" + pe1.cost);
+ int c = startPath1.cost - pe1.cost;
+ if (c < 0) c = 0;
+ if (c < firstMatchCost) firstMatchCost = c;
+ }
+
+ OsmPathElement pe2 = costCuttingTrack.getLink(startNodeId2, startNodeId1);
+ if (pe2 != null) {
+ logInfo("initialMatch pe2.cost=" + pe2.cost);
+ int c = startPath2.cost - pe2.cost;
+ if (c < 0) c = 0;
+ if (c < firstMatchCost) firstMatchCost = c;
+ }
+
+ if (firstMatchCost < 1000000000)
+ logInfo("firstMatchCost from initial match=" + firstMatchCost);
+ }
+
+ synchronized (openSet) {
+ openSet.clear();
+ addToOpenset(startPath1);
+ addToOpenset(startPath2);
+ }
+ ArrayList openBorderList = new ArrayList(4096);
+ boolean memoryPanicMode = false;
+ boolean needNonPanicProcessing = false;
+
+ for (; ; ) {
+ if (terminated) {
+ throw new IllegalArgumentException("operation killed by thread-priority-watchdog after " + (System.currentTimeMillis() - startTime) / 1000 + " seconds");
+ }
+
+ if (maxRunningTime > 0) {
+ long timeout = (matchPath == null && fastPartialRecalc) ? maxRunningTime / 3 : maxRunningTime;
+ if (System.currentTimeMillis() - startTime > timeout) {
+ throw new IllegalArgumentException(operationName + " timeout after " + (timeout / 1000) + " seconds");
+ }
+ }
+
+ synchronized (openSet) {
+
+ OsmPath path = openSet.popLowestKeyValue();
+ if (path == null) {
+ if (openBorderList.isEmpty()) {
+ break;
+ }
+ for (OsmPath p : openBorderList) {
+ openSet.add(p.cost + (int) (p.airdistance * airDistanceCostFactor), p);
+ }
+ openBorderList.clear();
+ memoryPanicMode = false;
+ needNonPanicProcessing = true;
+ continue;
+ }
+
+ if (path.airdistance == -1) {
+ path.unregisterUpTree(routingContext);
+ continue;
+ }
+
+ if (directWeaving && nodesCache.hasHollowLinkTargets(path.getTargetNode())) {
+ if (!memoryPanicMode) {
+ if (!nodesCache.nodesMap.isInMemoryBounds(openSet.getSize(), false)) {
+// System.out.println( "collecting..." );
+ int nodesBefore = nodesCache.nodesMap.nodesCreated;
+ int pathsBefore = openSet.getSize();
+
+ nodesCache.nodesMap.collectOutreachers();
+ for (; ; ) {
+ OsmPath p3 = openSet.popLowestKeyValue();
+ if (p3 == null) break;
+ if (p3.airdistance != -1 && nodesCache.nodesMap.canEscape(p3.getTargetNode())) {
+ openBorderList.add(p3);
+ }
+ }
+ nodesCache.nodesMap.clearTemp();
+ for (OsmPath p : openBorderList) {
+ openSet.add(p.cost + (int) (p.airdistance * airDistanceCostFactor), p);
+ }
+ openBorderList.clear();
+ logInfo("collected, nodes/paths before=" + nodesBefore + "/" + pathsBefore + " after=" + nodesCache.nodesMap.nodesCreated + "/" + openSet.getSize() + " maxTotalCost=" + maxTotalCost);
+ if (!nodesCache.nodesMap.isInMemoryBounds(openSet.getSize(), true)) {
+ if (maxTotalCost < 1000000000 || needNonPanicProcessing || fastPartialRecalc) {
+ throw new IllegalArgumentException("memory limit reached");
+ }
+ memoryPanicMode = true;
+ logInfo("************************ memory limit reached, enabled memory panic mode *************************");
+ }
+ }
+ }
+ if (memoryPanicMode) {
+ openBorderList.add(path);
+ continue;
+ }
+ }
+ needNonPanicProcessing = false;
+
+
+ if (fastPartialRecalc && matchPath != null && path.cost > 30L * firstMatchCost && !costCuttingTrack.isDirty) {
+ logInfo("early exit: firstMatchCost=" + firstMatchCost + " path.cost=" + path.cost);
+
+ // use an early exit, unless there's a realistc chance to complete within the timeout
+ if (path.cost > maxTotalCost / 2 && System.currentTimeMillis() - startTime < maxRunningTime / 3) {
+ logInfo("early exit supressed, running for completion, resetting timeout");
+ startTime = System.currentTimeMillis();
+ fastPartialRecalc = false;
+ } else {
+ throw new IllegalArgumentException("early exit for a close recalc");
+ }
+ }
+
+ if (nodeLimit > 0) // check node-limit for target island search
+ {
+ if (--nodeLimit == 0) {
+ return null;
+ }
+ }
+
+ nodesVisited++;
+ linksProcessed++;
+
+ OsmLink currentLink = path.getLink();
+ OsmNode sourceNode = path.getSourceNode();
+ OsmNode currentNode = path.getTargetNode();
+
+ if (currentLink.isLinkUnused()) {
+ path.unregisterUpTree(routingContext);
+ continue;
+ }
+
+ long currentNodeId = currentNode.getIdFromPos();
+ long sourceNodeId = sourceNode.getIdFromPos();
+
+ if (!path.didEnterDestinationArea()) {
+ islandNodePairs.addTempPair(sourceNodeId, currentNodeId);
+ }
+
+ if (path.treedepth != 1) {
+ if (path.treedepth == 0) // hack: sameSegment Paths marked treedepth=0 to pass above check
+ {
+ path.treedepth = 1;
+ }
+
+ if ((sourceNodeId == endNodeId1 && currentNodeId == endNodeId2)
+ || (sourceNodeId == endNodeId2 && currentNodeId == endNodeId1)) {
+ // track found, compile
+ logInfo("found track at cost " + path.cost + " nodesVisited = " + nodesVisited);
+ OsmTrack t = compileTrack(path, verbose);
+ t.showspeed = routingContext.showspeed;
+ t.showSpeedProfile = routingContext.showSpeedProfile;
+ return t;
+ }
+
+ // check for a match with the cost-cutting-track
+ if (costCuttingTrack != null) {
+ OsmPathElement pe = costCuttingTrack.getLink(sourceNodeId, currentNodeId);
+ if (pe != null) {
+ // remember first match cost for fast termination of partial recalcs
+ int parentcost = path.originElement == null ? 0 : path.originElement.cost;
+
+ // hitting start-element of costCuttingTrack?
+ int c = path.cost - parentcost - pe.cost;
+ if (c > 0) parentcost += c;
+
+ if (parentcost < firstMatchCost) firstMatchCost = parentcost;
+
+ int costEstimate = path.cost
+ + path.elevationCorrection(routingContext)
+ + (costCuttingTrack.cost - pe.cost);
+ if (costEstimate <= maxTotalCost) {
+ matchPath = OsmPathElement.create(path, routingContext.countTraffic);
+ }
+ if (costEstimate < maxTotalCost) {
+ logInfo("maxcost " + maxTotalCost + " -> " + costEstimate);
+ maxTotalCost = costEstimate;
+ }
+ }
+ }
+ }
+
+ int keepPathAirdistance = path.airdistance;
+ OsmLinkHolder firstLinkHolder = currentLink.getFirstLinkHolder(sourceNode);
+ for (OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink()) {
+ ((OsmPath) linkHolder).airdistance = -1; // invalidate the entry in the open set;
+ }
+
+ if (path.treedepth > 1) {
+ boolean isBidir = currentLink.isBidirectional();
+ sourceNode.unlinkLink(currentLink);
+
+ // if the counterlink is alive and does not yet have a path, remove it
+ if (isBidir && currentLink.getFirstLinkHolder(currentNode) == null && !routingContext.considerTurnRestrictions) {
+ currentNode.unlinkLink(currentLink);
+ }
+ }
+
+ // recheck cutoff before doing expensive stuff
+ if (path.cost + path.airdistance > maxTotalCost + 100) {
+ path.unregisterUpTree(routingContext);
+ continue;
+ }
+
+ nodesCache.nodesMap.currentMaxCost = maxTotalCost;
+ nodesCache.nodesMap.currentPathCost = path.cost;
+ nodesCache.nodesMap.destination = endPos;
+
+ routingContext.firstPrePath = null;
+
+ for (OsmLink link = currentNode.firstlink; link != null; link = link.getNext(currentNode)) {
+ OsmNode nextNode = link.getTarget(currentNode);
+
+ if (!nodesCache.obtainNonHollowNode(nextNode)) {
+ continue; // border node?
+ }
+ if (nextNode.firstlink == null) {
+ continue; // don't care about dead ends
+ }
+ if (nextNode == sourceNode) {
+ continue; // border node?
+ }
+
+ OsmPrePath prePath = routingContext.createPrePath(path, link);
+ if (prePath != null) {
+ prePath.next = routingContext.firstPrePath;
+ routingContext.firstPrePath = prePath;
+ }
+ }
+
+ for (OsmLink link = currentNode.firstlink; link != null; link = link.getNext(currentNode)) {
+ OsmNode nextNode = link.getTarget(currentNode);
+
+ if (!nodesCache.obtainNonHollowNode(nextNode)) {
+ continue; // border node?
+ }
+ if (nextNode.firstlink == null) {
+ continue; // don't care about dead ends
+ }
+ if (nextNode == sourceNode) {
+ continue; // border node?
+ }
+
+ if (guideTrack != null) {
+ int gidx = path.treedepth + 1;
+ if (gidx >= guideTrack.nodes.size()) {
+ continue;
+ }
+ OsmPathElement guideNode = guideTrack.nodes.get(routingContext.inverseRouting ? guideTrack.nodes.size() - 1 - gidx : gidx);
+ long nextId = nextNode.getIdFromPos();
+ if (nextId != guideNode.getIdFromPos()) {
+ // not along the guide-track, discard, but register for voice-hint processing
+ if (routingContext.turnInstructionMode > 0) {
+ OsmPath detour = routingContext.createPath(path, link, refTrack, true);
+ if (detour.cost >= 0. && nextId != startNodeId1 && nextId != startNodeId2) {
+ guideTrack.registerDetourForId(currentNode.getIdFromPos(), OsmPathElement.create(detour, false));
+ }
+ }
+ continue;
+ }
+ }
+
+ OsmPath bestPath = null;
+
+ boolean isFinalLink = false;
+ long targetNodeId = nextNode.getIdFromPos();
+ if (currentNodeId == endNodeId1 || currentNodeId == endNodeId2) {
+ if (targetNodeId == endNodeId1 || targetNodeId == endNodeId2) {
+ isFinalLink = true;
+ }
+ }
+
+ for (OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink()) {
+ OsmPath otherPath = (OsmPath) linkHolder;
+ try {
+ if (isFinalLink) {
+ endPos.radius = 1.5; // 1.5 meters is the upper limit that will not change the unit-test result..
+ routingContext.setWaypoint(endPos, true);
+ }
+ OsmPath testPath = routingContext.createPath(otherPath, link, refTrack, guideTrack != null);
+ if (testPath.cost >= 0 && (bestPath == null || testPath.cost < bestPath.cost)) {
+ bestPath = testPath;
+ }
+ } finally {
+ if (isFinalLink) {
+ routingContext.unsetWaypoint();
+ }
+ }
+ }
+ if (bestPath != null) {
+ boolean trafficSim = endPos == null;
+
+ bestPath.airdistance = trafficSim ? keepPathAirdistance : (isFinalLink ? 0 : nextNode.calcDistance(endPos));
+
+ boolean inRadius = boundary == null || boundary.isInBoundary(nextNode, bestPath.cost);
+
+ if (inRadius && (isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 100)) {
+ // add only if this may beat an existing path for that link
+ OsmLinkHolder dominator = link.getFirstLinkHolder(currentNode);
+ while (!trafficSim && dominator != null) {
+ OsmPath dp = (OsmPath) dominator;
+ if (dp.airdistance != -1 && bestPath.definitlyWorseThan(dp, routingContext)) {
+ break;
+ }
+ dominator = dominator.getNextForLink();
+ }
+
+ if (dominator == null) {
+ if (trafficSim && boundary != null && path.cost == 0 && bestPath.cost > 0) {
+ bestPath.airdistance += boundary.getBoundaryDistance(nextNode);
+ }
+ bestPath.treedepth = path.treedepth + 1;
+ link.addLinkHolder(bestPath, currentNode);
+ addToOpenset(bestPath);
+ }
+ }
+ }
+ }
+
+ path.unregisterUpTree(routingContext);
+ }
+ }
+
+ if (nodesVisited < MAXNODES_ISLAND_CHECK && islandNodePairs.getFreezeCount() < 5) {
+ throw new RoutingIslandException();
+ }
+
+ return null;
+ }
+
+ private void addToOpenset(OsmPath path) {
+ if (path.cost >= 0) {
+ openSet.add(path.cost + (int) (path.airdistance * airDistanceCostFactor), path);
+ path.registerUpTree();
+ }
+ }
+
+ private OsmTrack compileTrack(OsmPath path, boolean verbose) {
+ OsmPathElement element = OsmPathElement.create(path, false);
+
+ // for final track, cut endnode
+ if (guideTrack != null) {
+ element = element.origin;
+ }
+
+ float totalTime = element.getTime();
+ float totalEnergy = element.getEnergy();
+
+ OsmTrack track = new OsmTrack();
+ track.cost = path.cost;
+ track.energy = (int) path.getTotalEnergy();
+
+ int distance = 0;
+ double ascend = 0;
+ double ehb = 0.;
+
+ short ele_start = Short.MIN_VALUE;
+ short ele_end = Short.MIN_VALUE;
+
+ double eleFactor = routingContext.inverseRouting ? -0.25 : 0.25;
+ while (element != null) {
+ if (guideTrack != null && element.message == null) {
+ element.message = new MessageData();
+ }
+
+ if (routingContext.inverseRouting) {
+ element.setTime(totalTime - element.getTime());
+ element.setEnergy(totalEnergy - element.getEnergy());
+ track.nodes.add(element);
+ } else {
+ track.nodes.add(0, element);
+ }
+
+ OsmPathElement nextElement = element.origin;
+
+ short ele = element.getSElev();
+ if (ele != Short.MIN_VALUE) ele_start = ele;
+ if (ele_end == Short.MIN_VALUE) ele_end = ele;
+
+ if (nextElement != null) {
+ distance += element.calcDistance(nextElement);
+ short ele_next = nextElement.getSElev();
+ if (ele_next != Short.MIN_VALUE) {
+ ehb = ehb + (ele - ele_next) * eleFactor;
+ }
+ if (ehb > 10.) {
+ ascend += ehb - 10.;
+ ehb = 10.;
+ } else if (ehb < 0.) {
+ ehb = 0.;
+ }
+ }
+ element = nextElement;
+ }
+ ascend += ehb;
+ track.distance = distance;
+ track.ascend = (int) ascend;
+ track.plainAscend = (int) ((ele_end - ele_start) * eleFactor + 0.5);
+ logInfo("track-length = " + track.distance);
+ logInfo("filtered ascend = " + track.ascend);
+ track.buildMap();
+
+ // for final track..
+ if (guideTrack != null) {
+ track.copyDetours(guideTrack);
+ track.processVoiceHints(routingContext);
+ track.prepareSpeedProfile(routingContext);
+ }
+ return track;
+ }
+
+ private OsmTrack mergeTrack(OsmPathElement match, OsmTrack oldTrack) {
+ logInfo("**************** merging match=" + match.cost + " with oldTrack=" + oldTrack.cost);
+ OsmPathElement element = match;
+ OsmTrack track = new OsmTrack();
+ track.cost = oldTrack.cost;
+
+ while (element != null) {
+ track.addNode(element);
+ element = element.origin;
+ }
+ long lastId = 0;
+ long id1 = match.getIdFromPos();
+ long id0 = match.origin == null ? 0 : match.origin.getIdFromPos();
+ boolean appending = false;
+ for (OsmPathElement n : oldTrack.nodes) {
+ if (appending) {
+ track.nodes.add(n);
+ }
+
+ long id = n.getIdFromPos();
+ if (id == id1 && lastId == id0) {
+ appending = true;
+ }
+ lastId = id;
+ }
+
+
+ track.buildMap();
+ return track;
+ }
+
+ public int getPathPeak() {
+ synchronized (openSet) {
+ return openSet.getPeakSize();
+ }
+ }
+
+ public int[] getOpenSet() {
+ if (extract == null) {
+ extract = new Object[500];
+ }
+
+ synchronized (openSet) {
+ if (guideTrack != null) {
+ ArrayList nodes = guideTrack.nodes;
+ int[] res = new int[nodes.size() * 2];
+ int i = 0;
+ for (OsmPathElement n : nodes) {
+ res[i++] = n.getILon();
+ res[i++] = n.getILat();
+ }
+ return res;
+ }
+
+ int size = openSet.getExtract(extract);
+ int[] res = new int[size * 2];
+ for (int i = 0, j = 0; i < size; i++) {
+ OsmPath p = (OsmPath) extract[i];
+ extract[i] = null;
+ OsmNode n = p.getTargetNode();
+ res[j++] = n.ilon;
+ res[j++] = n.ilat;
+ }
+ return res;
+ }
+ }
+
+ public boolean isFinished() {
+ return finished;
+ }
+
+ public int getLinksProcessed() {
+ return linksProcessed;
+ }
+
+ public int getDistance() {
+ return foundTrack.distance;
+ }
+
+ public int getAscend() {
+ return foundTrack.ascend;
+ }
+
+ public int getPlainAscend() {
+ return foundTrack.plainAscend;
+ }
+
+ public String getTime() {
+ return foundTrack.getFormattedTime2();
+ }
+
+ public OsmTrack getFoundTrack() {
+ return foundTrack;
+ }
+
+ public int getAlternativeIndex() {
+ return alternativeIndex;
+ }
+
+ public OsmTrack getFoundRawTrack() {
+ return foundRawTrack;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void terminate() {
+ terminated = true;
+ }
+
+ public boolean isTerminated() {
+ return terminated;
+ }
+
+ public String getOutfile() {
+ return outfile;
+ }
+}
diff --git a/brouter-core/src/main/java/btools/router/RoutingHelper.java b/brouter-core/src/main/java/btools/router/RoutingHelper.java
index ae723ea..4db5654 100644
--- a/brouter-core/src/main/java/btools/router/RoutingHelper.java
+++ b/brouter-core/src/main/java/btools/router/RoutingHelper.java
@@ -9,41 +9,33 @@ import java.io.File;
import btools.mapaccess.StorageConfigHelper;
-public final class RoutingHelper
-{
- public static File getAdditionalMaptoolDir( File segmentDir )
- {
- return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
- }
+public final class RoutingHelper {
+ public static File getAdditionalMaptoolDir(File segmentDir) {
+ return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
+ }
- public static File getSecondarySegmentDir( File segmentDir )
- {
- return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
- }
-
-
- public static boolean hasDirectoryAnyDatafiles( File segmentDir )
- {
- if ( hasAnyDatafiles( segmentDir ) )
- {
- return true;
- }
- // check secondary, too
- File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
- if ( secondary != null )
- {
- return hasAnyDatafiles( secondary );
- }
- return false;
- }
+ public static File getSecondarySegmentDir(File segmentDir) {
+ return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
+ }
- private static boolean hasAnyDatafiles( File dir )
- {
- String[] fileNames = dir.list();
- for( String fileName : fileNames )
- {
- if ( fileName.endsWith( ".rd5" ) ) return true;
- }
- return false;
+
+ public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
+ if (hasAnyDatafiles(segmentDir)) {
+ return true;
}
+ // check secondary, too
+ File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
+ if (secondary != null) {
+ return hasAnyDatafiles(secondary);
+ }
+ return false;
+ }
+
+ private static boolean hasAnyDatafiles(File dir) {
+ String[] fileNames = dir.list();
+ for (String fileName : fileNames) {
+ if (fileName.endsWith(".rd5")) return true;
+ }
+ return false;
+ }
}
diff --git a/brouter-core/src/main/java/btools/router/RoutingIslandException.java b/brouter-core/src/main/java/btools/router/RoutingIslandException.java
index 2475227..cbb5334 100644
--- a/brouter-core/src/main/java/btools/router/RoutingIslandException.java
+++ b/brouter-core/src/main/java/btools/router/RoutingIslandException.java
@@ -1,5 +1,4 @@
package btools.router;
-public class RoutingIslandException extends RuntimeException
-{
+public class RoutingIslandException extends RuntimeException {
}
diff --git a/brouter-core/src/main/java/btools/router/SearchBoundary.java b/brouter-core/src/main/java/btools/router/SearchBoundary.java
index 1d976f9..bccda73 100644
--- a/brouter-core/src/main/java/btools/router/SearchBoundary.java
+++ b/brouter-core/src/main/java/btools/router/SearchBoundary.java
@@ -8,83 +8,80 @@ package btools.router;
import btools.mapaccess.OsmNode;
-public final class SearchBoundary
-{
+public final class SearchBoundary {
- private int minlon0;
- private int minlat0;
- private int maxlon0;
- private int maxlat0;
+ private int minlon0;
+ private int minlat0;
+ private int maxlon0;
+ private int maxlat0;
- private int minlon;
- private int minlat;
- private int maxlon;
- private int maxlat;
- private int radius;
- private OsmNode p;
+ private int minlon;
+ private int minlat;
+ private int maxlon;
+ private int maxlat;
+ private int radius;
+ private OsmNode p;
- int direction;
+ int direction;
- /**
- * @param radius Search radius in meters.
- */
- public SearchBoundary( OsmNode n, int radius, int direction )
- {
- this.radius = radius;
- this.direction = direction;
+ /**
+ * @param radius Search radius in meters.
+ */
+ public SearchBoundary(OsmNode n, int radius, int direction) {
+ this.radius = radius;
+ this.direction = direction;
- p = new OsmNode( n.ilon, n.ilat );
+ p = new OsmNode(n.ilon, n.ilat);
- int lon = (n.ilon / 5000000 ) * 5000000;
- int lat = (n.ilat / 5000000 ) * 5000000;
+ int lon = (n.ilon / 5000000) * 5000000;
+ int lat = (n.ilat / 5000000) * 5000000;
- minlon0 = lon - 5000000;
- minlat0 = lat - 5000000;
- maxlon0 = lon + 10000000;
- maxlat0 = lat + 10000000;
+ minlon0 = lon - 5000000;
+ minlat0 = lat - 5000000;
+ maxlon0 = lon + 10000000;
+ maxlat0 = lat + 10000000;
- minlon = lon - 1000000;
- minlat = lat - 1000000;
- maxlon = lon + 6000000;
- maxlat = lat + 6000000;
+ minlon = lon - 1000000;
+ minlat = lat - 1000000;
+ maxlon = lon + 6000000;
+ maxlat = lat + 6000000;
+ }
+
+ public static String getFileName(OsmNode n) {
+ int lon = (n.ilon / 5000000) * 5000000;
+ int lat = (n.ilat / 5000000) * 5000000;
+
+ int dlon = lon / 1000000 - 180;
+ int dlat = lat / 1000000 - 90;
+
+ String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
+ String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
+ return slon + "_" + slat + ".trf";
+ }
+
+ public boolean isInBoundary(OsmNode n, int cost) {
+ if (radius > 0) {
+ return n.calcDistance(p) < radius;
}
-
- public static String getFileName( OsmNode n )
- {
- int lon = (n.ilon / 5000000 ) * 5000000;
- int lat = (n.ilat / 5000000 ) * 5000000;
-
- int dlon = lon / 1000000 -180;
- int dlat = lat / 1000000 - 90;
-
- String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
- String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
- return slon + "_" + slat + ".trf";
+ if (cost == 0) {
+ return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
}
+ return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
+ }
- public boolean isInBoundary( OsmNode n, int cost )
- {
- if ( radius > 0 )
- {
- return n.calcDistance( p ) < radius;
- }
- if ( cost == 0 )
- {
- return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
- }
- return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
- }
-
- public int getBoundaryDistance( OsmNode n )
- {
- switch( direction )
- {
- case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
- case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
- case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
- case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
- default: throw new IllegalArgumentException( "undefined direction: "+ direction );
- }
+ public int getBoundaryDistance(OsmNode n) {
+ switch (direction) {
+ case 0:
+ return n.calcDistance(new OsmNode(n.ilon, minlat));
+ case 1:
+ return n.calcDistance(new OsmNode(minlon, n.ilat));
+ case 2:
+ return n.calcDistance(new OsmNode(n.ilon, maxlat));
+ case 3:
+ return n.calcDistance(new OsmNode(maxlon, n.ilat));
+ default:
+ throw new IllegalArgumentException("undefined direction: " + direction);
}
+ }
}
diff --git a/brouter-core/src/main/java/btools/router/StdModel.java b/brouter-core/src/main/java/btools/router/StdModel.java
index 84d360e..d596d68 100644
--- a/brouter-core/src/main/java/btools/router/StdModel.java
+++ b/brouter-core/src/main/java/btools/router/StdModel.java
@@ -12,15 +12,12 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
-final class StdModel extends OsmPathModel
-{
- public OsmPrePath createPrePath()
- {
+final class StdModel extends OsmPathModel {
+ public OsmPrePath createPrePath() {
return null;
}
- public OsmPath createPath()
- {
+ public OsmPath createPath() {
return new StdPath();
}
@@ -29,11 +26,10 @@ final class StdModel extends OsmPathModel
@Override
- public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map keyValues )
- {
+ public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map keyValues) {
ctxWay = expctxWay;
ctxNode = expctxNode;
-
+
BExpressionContext expctxGlobal = expctxWay; // just one of them...
}
diff --git a/brouter-core/src/main/java/btools/router/StdPath.java b/brouter-core/src/main/java/btools/router/StdPath.java
index b6b527f..0d73b40 100644
--- a/brouter-core/src/main/java/btools/router/StdPath.java
+++ b/brouter-core/src/main/java/btools/router/StdPath.java
@@ -7,8 +7,7 @@ package btools.router;
import btools.util.FastMath;
-final class StdPath extends OsmPath
-{
+final class StdPath extends OsmPath {
/**
* The elevation-hysteresis-buffer (0-10 m)
*/
@@ -23,9 +22,8 @@ final class StdPath extends OsmPath
private static final double GRAVITY = 9.81; // in meters per second^(-2)
@Override
- public void init( OsmPath orig )
- {
- StdPath origin = (StdPath)orig;
+ public void init(OsmPath orig) {
+ StdPath origin = (StdPath) orig;
this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu;
this.totalTime = origin.totalTime;
@@ -34,8 +32,7 @@ final class StdPath extends OsmPath
}
@Override
- protected void resetState()
- {
+ protected void resetState() {
ehbd = 0;
ehbu = 0;
totalTime = 0.f;
@@ -44,8 +41,7 @@ final class StdPath extends OsmPath
}
@Override
- protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
- {
+ protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
// calculate the costfactor inputs
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
@@ -54,14 +50,13 @@ final class StdPath extends OsmPath
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
- int dist = (int)distance; // legacy arithmetics needs int
+ int dist = (int) distance; // legacy arithmetics needs int
// penalty for turning angle
- int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
- if ( message != null )
- {
+ int turncost = (int) ((1. - cosangle) * turncostbase + 0.2); // e.g. turncost=90 -> 90 degree = 90m penalty
+ if (message != null) {
message.linkturncost += turncost;
- message.turnangle = (float)angle;
+ message.turnangle = (float) angle;
}
double sectionCost = turncost;
@@ -70,81 +65,66 @@ final class StdPath extends OsmPath
// only the part of the descend that does not fit into the elevation-hysteresis-buffers
// leads to an immediate penalty
- int delta_h_micros = (int)(1000000. * delta_h);
+ int delta_h_micros = (int) (1000000. * delta_h);
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
- ehbu += delta_h_micros - dist * rc.uphillcutoff;
+ ehbu += delta_h_micros - dist * rc.uphillcutoff;
float downweight = 0.f;
- if ( ehbd > rc.elevationpenaltybuffer )
- {
+ if (ehbd > rc.elevationpenaltybuffer) {
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
- if ( reduce > excess )
- {
- downweight = ((float)excess)/reduce;
+ if (reduce > excess) {
+ downweight = ((float) excess) / reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
- if ( reduce < excess )
- {
+ if (reduce < excess) {
reduce = excess;
}
ehbd -= reduce;
- if ( rc.downhillcostdiv > 0 )
- {
- int elevationCost = reduce/rc.downhillcostdiv;
+ if (rc.downhillcostdiv > 0) {
+ int elevationCost = reduce / rc.downhillcostdiv;
sectionCost += elevationCost;
- if ( message != null )
- {
+ if (message != null) {
message.linkelevationcost += elevationCost;
}
}
- }
- else if ( ehbd < 0 )
- {
+ } else if (ehbd < 0) {
ehbd = 0;
}
float upweight = 0.f;
- if ( ehbu > rc.elevationpenaltybuffer )
- {
+ if (ehbu > rc.elevationpenaltybuffer) {
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
- if ( reduce > excess )
- {
- upweight = ((float)excess)/reduce;
+ if (reduce > excess) {
+ upweight = ((float) excess) / reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
- if ( reduce < excess )
- {
+ if (reduce < excess) {
reduce = excess;
}
ehbu -= reduce;
- if ( rc.uphillcostdiv > 0 )
- {
- int elevationCost = reduce/rc.uphillcostdiv;
+ if (rc.uphillcostdiv > 0) {
+ int elevationCost = reduce / rc.uphillcostdiv;
sectionCost += elevationCost;
- if ( message != null )
- {
+ if (message != null) {
message.linkelevationcost += elevationCost;
}
}
- }
- else if ( ehbu < 0 )
- {
+ } else if (ehbu < 0) {
ehbu = 0;
}
// get the effective costfactor (slope dependent)
- float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
+ float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
- if ( message != null )
- {
+ if (message != null) {
message.costfactor = costfactor;
}
@@ -154,22 +134,18 @@ final class StdPath extends OsmPath
}
@Override
- protected double processTargetNode( RoutingContext rc )
- {
+ protected double processTargetNode(RoutingContext rc) {
// finally add node-costs for target node
- if ( targetNode.nodeDescription != null )
- {
+ if (targetNode.nodeDescription != null) {
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
- rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
+ rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
float initialcost = rc.expctxNode.getInitialcost();
- if ( initialcost >= 1000000. )
- {
+ if (initialcost >= 1000000.) {
return -1.;
}
- if ( message != null )
- {
- message.linknodecost += (int)initialcost;
- message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
+ if (message != null) {
+ message.linknodecost += (int) initialcost;
+ message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
}
return initialcost;
}
@@ -177,118 +153,96 @@ final class StdPath extends OsmPath
}
@Override
- public int elevationCorrection( RoutingContext rc )
- {
- return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
- + ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
+ public int elevationCorrection(RoutingContext rc) {
+ return (rc.downhillcostdiv > 0 ? ehbd / rc.downhillcostdiv : 0)
+ + (rc.uphillcostdiv > 0 ? ehbu / rc.uphillcostdiv : 0);
}
@Override
- public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
- {
- StdPath p = (StdPath)path;
+ public boolean definitlyWorseThan(OsmPath path, RoutingContext rc) {
+ StdPath p = (StdPath) path;
- int c = p.cost;
- if ( rc.downhillcostdiv > 0 )
- {
- int delta = p.ehbd - ehbd;
- if ( delta > 0 ) c += delta/rc.downhillcostdiv;
- }
- if ( rc.uphillcostdiv > 0 )
- {
- int delta = p.ehbu - ehbu;
- if ( delta > 0 ) c += delta/rc.uphillcostdiv;
- }
+ int c = p.cost;
+ if (rc.downhillcostdiv > 0) {
+ int delta = p.ehbd - ehbd;
+ if (delta > 0) c += delta / rc.downhillcostdiv;
+ }
+ if (rc.uphillcostdiv > 0) {
+ int delta = p.ehbu - ehbu;
+ if (delta > 0) c += delta / rc.uphillcostdiv;
+ }
- return cost > c;
+ return cost > c;
}
- private double calcIncline( double dist )
- {
+ private double calcIncline(double dist) {
double min_delta = 3.;
double shift;
- if ( elevation_buffer > min_delta )
- {
+ if (elevation_buffer > min_delta) {
shift = -min_delta;
- }
- else if ( elevation_buffer < min_delta )
- {
+ } else if (elevation_buffer < min_delta) {
shift = -min_delta;
- }
- else
- {
+ } else {
return 0.;
}
- double decayFactor = FastMath.exp( - dist / 100. );
- float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
- double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
+ double decayFactor = FastMath.exp(-dist / 100.);
+ float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
+ double incline = (elevation_buffer - new_elevation_buffer) / dist;
elevation_buffer = new_elevation_buffer;
return incline;
}
@Override
- protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
- {
- if ( !detailMode )
- {
+ protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
+ if (!detailMode) {
return;
}
// compute incline
elevation_buffer += delta_h;
- double incline = calcIncline( dist );
+ double incline = calcIncline(dist);
double wayMaxspeed;
-
+
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
- if (wayMaxspeed == 0)
- {
- wayMaxspeed = rc.maxSpeed;
+ if (wayMaxspeed == 0) {
+ wayMaxspeed = rc.maxSpeed;
}
- wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
-
+ wayMaxspeed = Math.min(wayMaxspeed, rc.maxSpeed);
+
double speed; // Travel speed
- double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
- if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
- {
+ double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
+ if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9) {
// Use Tobler's hiking function for walking sections
speed = rc.maxSpeed * 3.6;
- speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
- }
- else if (rc.bikeMode)
- {
- speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
+ speed = (speed * FastMath.exp(-3.5 * Math.abs(incline + 0.05))) / 3.6;
+ } else if (rc.bikeMode) {
+ speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
speed = Math.min(speed, wayMaxspeed);
- }
- else // all other
+ } else // all other
{
speed = wayMaxspeed;
}
- float dt = (float) ( dist / speed );
+ float dt = (float) (dist / speed);
totalTime += dt;
// Calc energy assuming biking (no good model yet for hiking)
// (Count only positive, negative would mean breaking to enforce maxspeed)
- double energy = dist*(rc.S_C_x*speed*speed + f_roll);
- if ( energy > 0. )
- {
+ double energy = dist * (rc.S_C_x * speed * speed + f_roll);
+ if (energy > 0.) {
totalEnergy += energy;
}
}
- private static double solveCubic( double a, double c, double d )
- {
+ private static double solveCubic(double a, double c, double d) {
// Solves a * v^3 + c * v = d with a Newton method
// to get the speed v for the section.
double v = 8.;
boolean findingStartvalue = true;
- for ( int i = 0; i < 10; i++ )
- {
- double y = ( a * v * v + c ) * v - d;
- if ( y < .1 )
- {
- if ( findingStartvalue )
- {
+ for (int i = 0; i < 10; i++) {
+ double y = (a * v * v + c) * v - d;
+ if (y < .1) {
+ if (findingStartvalue) {
v *= 2.;
continue;
}
@@ -302,14 +256,12 @@ final class StdPath extends OsmPath
}
@Override
- public double getTotalTime()
- {
+ public double getTotalTime() {
return totalTime;
}
@Override
- public double getTotalEnergy()
- {
+ public double getTotalEnergy() {
return totalEnergy;
}
}
diff --git a/brouter-core/src/main/java/btools/router/SuspectInfo.java b/brouter-core/src/main/java/btools/router/SuspectInfo.java
index db86592..b1f4cfe 100644
--- a/brouter-core/src/main/java/btools/router/SuspectInfo.java
+++ b/brouter-core/src/main/java/btools/router/SuspectInfo.java
@@ -2,65 +2,58 @@ 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 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 static final int TRIGGER_SHARP_LINK = 128;
+ public static final int TRIGGER_BAD_TR = 256;
public int prio;
public int triggers;
-
- public static void addSuspect( Map map, long id, int prio, int trigger )
- {
- Long iD = Long.valueOf( id );
- SuspectInfo info = map.get( iD );
- if ( info == null )
- {
+
+ public static void addSuspect(Map map, long id, int prio, int trigger) {
+ Long iD = Long.valueOf(id);
+ SuspectInfo info = map.get(iD);
+ if (info == null) {
info = new SuspectInfo();
- map.put( iD, info );
+ map.put(iD, info);
}
- info.prio = Math.max( info.prio, prio );
+ info.prio = Math.max(info.prio, prio);
info.triggers |= trigger;
}
-
- public static SuspectInfo addTrigger( SuspectInfo old, int prio, int trigger )
- {
- if ( old == null )
- {
+
+ public static SuspectInfo addTrigger(SuspectInfo old, int prio, int trigger) {
+ if (old == null) {
old = new SuspectInfo();
}
- old.prio = Math.max( old.prio, prio );
+ old.prio = Math.max(old.prio, prio);
old.triggers |= trigger;
return old;
}
-
- public static String getTriggerText( int triggers )
- {
+
+ 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 );
+ 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 );
+ private static void addText(StringBuilder sb, String text, int mask, int bit) {
+ if ((bit & mask) == 0) return;
+ if (sb.length() > 0) sb.append(",");
+ sb.append(text);
}
-
+
}
diff --git a/brouter-core/src/main/java/btools/router/VoiceHint.java b/brouter-core/src/main/java/btools/router/VoiceHint.java
index c3ec1a4..18b2d54 100644
--- a/brouter-core/src/main/java/btools/router/VoiceHint.java
+++ b/brouter-core/src/main/java/btools/router/VoiceHint.java
@@ -9,8 +9,7 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
-public class VoiceHint
-{
+public class VoiceHint {
static final int C = 1; // continue (go straight)
static final int TL = 2; // turn left
static final int TSLL = 3; // turn slightly left
@@ -36,8 +35,7 @@ public class VoiceHint
double distanceToNext;
int indexInTrack;
- public float getTime()
- {
+ public float getTime() {
return oldWay == null ? 0.f : oldWay.time;
}
@@ -47,269 +45,281 @@ public class VoiceHint
int roundaboutExit;
- boolean isRoundabout()
- {
+ boolean isRoundabout() {
return roundaboutExit != 0;
}
-
- public void addBadWay( MessageData badWay )
- {
- if ( badWay == null )
- {
+
+ public void addBadWay(MessageData badWay) {
+ if (badWay == null) {
return;
}
- if ( badWays == null )
- {
+ if (badWays == null) {
badWays = new ArrayList();
}
- badWays.add( badWay );
+ badWays.add(badWay);
}
- public int getCommand()
- {
+ public int getCommand() {
return cmd;
}
- public int getExitNumber()
- {
+ public int getExitNumber() {
return roundaboutExit;
}
- public String getCommandString()
- {
- switch ( cmd )
- {
- case TU : return "TU";
- case TSHL : return "TSHL";
- case TL : return "TL";
- case TSLL : return "TSLL";
- case KL : return "KL";
- case C : return "C";
- case KR : return "KR";
- case TSLR : return "TSLR";
- case TR : return "TR";
- case TSHR : return "TSHR";
- case TRU : return "TRU";
- case RNDB : return "RNDB" + roundaboutExit;
- case RNLB : return "RNLB" + (-roundaboutExit);
- default : throw new IllegalArgumentException( "unknown command: " + cmd );
- }
- }
-
- public String getSymbolString()
- {
- switch ( cmd )
- {
- case TU : return "TU";
- case TSHL : return "TSHL";
- case TL : return "Left";
- case TSLL : return "TSLL";
- case KL : return "TSLL"; // ?
- case C : return "Straight";
- case KR : return "TSLR"; // ?
- case TSLR : return "TSLR";
- case TR : return "Right";
- case TSHR : return "TSHR";
- case TRU : return "TU";
- case RNDB : return "RNDB" + roundaboutExit;
- case RNLB : return "RNLB" + (-roundaboutExit);
- default : throw new IllegalArgumentException( "unknown command: " + cmd );
+ public String getCommandString() {
+ switch (cmd) {
+ case TU:
+ return "TU";
+ case TSHL:
+ return "TSHL";
+ case TL:
+ return "TL";
+ case TSLL:
+ return "TSLL";
+ case KL:
+ return "KL";
+ case C:
+ return "C";
+ case KR:
+ return "KR";
+ case TSLR:
+ return "TSLR";
+ case TR:
+ return "TR";
+ case TSHR:
+ return "TSHR";
+ case TRU:
+ return "TRU";
+ case RNDB:
+ return "RNDB" + roundaboutExit;
+ case RNLB:
+ return "RNLB" + (-roundaboutExit);
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
}
}
- public String getMessageString()
- {
- switch ( cmd )
- {
- case TU : return "u-turn";
- case TSHL : return "sharp left";
- case TL : return "left";
- case TSLL : return "slight left";
- case KL : return "keep left";
- case C : return "straight";
- case KR : return "keep right";
- case TSLR : return "slight right";
- case TR : return "right";
- case TSHR : return "sharp right";
- case TRU : return "u-turn";
- case RNDB : return "Take exit " + roundaboutExit;
- case RNLB : return "Take exit " + (-roundaboutExit);
- default : throw new IllegalArgumentException( "unknown command: " + cmd );
+ public String getSymbolString() {
+ switch (cmd) {
+ case TU:
+ return "TU";
+ case TSHL:
+ return "TSHL";
+ case TL:
+ return "Left";
+ case TSLL:
+ return "TSLL";
+ case KL:
+ return "TSLL"; // ?
+ case C:
+ return "Straight";
+ case KR:
+ return "TSLR"; // ?
+ case TSLR:
+ return "TSLR";
+ case TR:
+ return "Right";
+ case TSHR:
+ return "TSHR";
+ case TRU:
+ return "TU";
+ case RNDB:
+ return "RNDB" + roundaboutExit;
+ case RNLB:
+ return "RNLB" + (-roundaboutExit);
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
}
}
- public int getLocusAction()
- {
- switch ( cmd )
- {
- case TU : return 13;
- case TSHL : return 5;
- case TL : return 4;
- case TSLL : return 3;
- case KL : return 9; // ?
- case C : return 1;
- case KR : return 10; // ?
- case TSLR : return 6;
- case TR : return 7;
- case TSHR : return 8;
- case TRU : return 14;
- case RNDB : return 26 + roundaboutExit;
- case RNLB : return 26 - roundaboutExit;
- default : throw new IllegalArgumentException( "unknown command: " + cmd );
+ public String getMessageString() {
+ switch (cmd) {
+ case TU:
+ return "u-turn";
+ case TSHL:
+ return "sharp left";
+ case TL:
+ return "left";
+ case TSLL:
+ return "slight left";
+ case KL:
+ return "keep left";
+ case C:
+ return "straight";
+ case KR:
+ return "keep right";
+ case TSLR:
+ return "slight right";
+ case TR:
+ return "right";
+ case TSHR:
+ return "sharp right";
+ case TRU:
+ return "u-turn";
+ case RNDB:
+ return "Take exit " + roundaboutExit;
+ case RNLB:
+ return "Take exit " + (-roundaboutExit);
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
}
}
-
- public int getOruxAction()
- {
- switch ( cmd )
- {
- case TU : return 1003;
- case TSHL : return 1019;
- case TL : return 1000;
- case TSLL : return 1017;
- case KL : return 1015; // ?
- case C : return 1002;
- case KR : return 1014; // ?
- case TSLR : return 1016;
- case TR : return 1001;
- case TSHR : return 1018;
- case TRU : return 1003;
- case RNDB : return 1008 + roundaboutExit;
- case RNLB : return 1008 + roundaboutExit;
- default : throw new IllegalArgumentException( "unknown command: " + cmd );
- }
+
+ public int getLocusAction() {
+ switch (cmd) {
+ case TU:
+ return 13;
+ case TSHL:
+ return 5;
+ case TL:
+ return 4;
+ case TSLL:
+ return 3;
+ case KL:
+ return 9; // ?
+ case C:
+ return 1;
+ case KR:
+ return 10; // ?
+ case TSLR:
+ return 6;
+ case TR:
+ return 7;
+ case TSHR:
+ return 8;
+ case TRU:
+ return 14;
+ case RNDB:
+ return 26 + roundaboutExit;
+ case RNLB:
+ return 26 - roundaboutExit;
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
+ }
}
- public void calcCommand()
- {
+ public int getOruxAction() {
+ switch (cmd) {
+ case TU:
+ return 1003;
+ case TSHL:
+ return 1019;
+ case TL:
+ return 1000;
+ case TSLL:
+ return 1017;
+ case KL:
+ return 1015; // ?
+ case C:
+ return 1002;
+ case KR:
+ return 1014; // ?
+ case TSLR:
+ return 1016;
+ case TR:
+ return 1001;
+ case TSHR:
+ return 1018;
+ case TRU:
+ return 1003;
+ case RNDB:
+ return 1008 + roundaboutExit;
+ case RNLB:
+ return 1008 + roundaboutExit;
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
+ }
+ }
+
+ public void calcCommand() {
float lowerBadWayAngle = -181;
float higherBadWayAngle = 181;
- if ( badWays != null )
- {
- for ( MessageData badWay : badWays )
- {
- if ( badWay.isBadOneway() )
- {
+ if (badWays != null) {
+ for (MessageData badWay : badWays) {
+ if (badWay.isBadOneway()) {
continue;
}
- if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
- {
+ if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
lowerBadWayAngle = badWay.turnangle;
}
- if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
- {
+ if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
higherBadWayAngle = badWay.turnangle;
}
}
}
- float cmdAngle= angle;
+ float cmdAngle = angle;
// fall back to local angle if otherwise inconsistent
- if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
- {
+ if (lowerBadWayAngle > angle || higherBadWayAngle < angle) {
cmdAngle = goodWay.turnangle;
}
- if (roundaboutExit > 0)
- {
+ if (roundaboutExit > 0) {
cmd = RNDB;
- }
- else if (roundaboutExit < 0)
- {
+ } else if (roundaboutExit < 0) {
cmd = RNLB;
- }
- else if ( cmdAngle < -159. )
- {
+ } else if (cmdAngle < -159.) {
cmd = TU;
- }
- else if ( cmdAngle < -135. )
- {
+ } else if (cmdAngle < -135.) {
cmd = TSHL;
- }
- else if ( cmdAngle < -45. )
- {
+ } else if (cmdAngle < -45.) {
// a TL can be pushed in either direction by a close-by alternative
- if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
- {
+ if (higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180.) {
cmd = TSHL;
- }
- else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
- {
+ } else if (lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0.) {
cmd = TSLL;
- }
- else
- {
+ } else {
cmd = TL;
}
- }
- else if ( cmdAngle < -21. )
- {
- if ( cmd != KR ) // don't overwrite KR with TSLL
+ } else if (cmdAngle < -21.) {
+ if (cmd != KR) // don't overwrite KR with TSLL
{
cmd = TSLL;
}
- }
- else if ( cmdAngle < 21. )
- {
- if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
+ } else if (cmdAngle < 21.) {
+ if (cmd != KR && cmd != KL) // don't overwrite KL/KR hints!
{
cmd = C;
}
- }
- else if ( cmdAngle < 45. )
- {
- if ( cmd != KL ) // don't overwrite KL with TSLR
+ } else if (cmdAngle < 45.) {
+ if (cmd != KL) // don't overwrite KL with TSLR
{
cmd = TSLR;
}
- }
- else if ( cmdAngle < 135. )
- {
+ } else if (cmdAngle < 135.) {
// a TR can be pushed in either direction by a close-by alternative
- if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
- {
+ if (higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0.) {
cmd = TSLR;
- }
- else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
- {
+ } else if (lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180.) {
cmd = TSHR;
- }
- else
- {
+ } else {
cmd = TR;
}
- }
- else if ( cmdAngle < 159. )
- {
+ } else if (cmdAngle < 159.) {
cmd = TSHR;
- }
- else
- {
+ } else {
cmd = TRU;
}
}
- public String formatGeometry()
- {
+ public String formatGeometry() {
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
StringBuilder sb = new StringBuilder(30);
- sb.append( ' ' ).append( (int)oldPrio );
- appendTurnGeometry(sb,goodWay);
- if ( badWays != null )
- {
- for ( MessageData badWay : badWays )
- {
- sb.append( " " );
- appendTurnGeometry( sb, badWay );
+ sb.append(' ').append((int) oldPrio);
+ appendTurnGeometry(sb, goodWay);
+ if (badWays != null) {
+ for (MessageData badWay : badWays) {
+ sb.append(" ");
+ appendTurnGeometry(sb, badWay);
}
}
return sb.toString();
}
- private void appendTurnGeometry( StringBuilder sb, MessageData msg )
- {
- sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
+ private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
+ sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
}
}
diff --git a/brouter-core/src/main/java/btools/router/VoiceHintList.java b/brouter-core/src/main/java/btools/router/VoiceHintList.java
index 1b717b7..61534bc 100644
--- a/brouter-core/src/main/java/btools/router/VoiceHintList.java
+++ b/brouter-core/src/main/java/btools/router/VoiceHintList.java
@@ -9,30 +9,24 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
-public class VoiceHintList
-{
+public class VoiceHintList {
private String transportMode;
int turnInstructionMode;
ArrayList list = new ArrayList();
- public void setTransportMode( boolean isCar, boolean isBike )
- {
- transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
+ public void setTransportMode(boolean isCar, boolean isBike) {
+ transportMode = isCar ? "car" : (isBike ? "bike" : "foot");
}
- public String getTransportMode()
- {
+ public String getTransportMode() {
return transportMode;
}
- public int getLocusRouteType()
- {
- if ( "car".equals( transportMode ) )
- {
+ public int getLocusRouteType() {
+ if ("car".equals(transportMode)) {
return 0;
}
- if ( "bike".equals( transportMode ) )
- {
+ if ("bike".equals(transportMode)) {
return 5;
}
return 3; // foot
diff --git a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java
index 247c840..7ae23a7 100644
--- a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java
+++ b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java
@@ -8,26 +8,21 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
-public final class VoiceHintProcessor
-{
+public final class VoiceHintProcessor {
private double catchingRange; // range to catch angles and merge turns
private boolean explicitRoundabouts;
- public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
- {
+ public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) {
this.catchingRange = catchingRange;
this.explicitRoundabouts = explicitRoundabouts;
}
- private float sumNonConsumedWithinCatchingRange( List inputs, int offset )
- {
+ private float sumNonConsumedWithinCatchingRange(List inputs, int offset) {
double distance = 0.;
float angle = 0.f;
- while( offset >= 0 && distance < catchingRange )
- {
- VoiceHint input = inputs.get( offset-- );
- if ( input.turnAngleConsumed )
- {
+ while (offset >= 0 && distance < catchingRange) {
+ VoiceHint input = inputs.get(offset--);
+ if (input.turnAngleConsumed) {
break;
}
angle += input.goodWay.turnangle;
@@ -44,10 +39,10 @@ public final class VoiceHintProcessor
* order (from target to start), but output is
* returned in travel-direction and only for
* those nodes that trigger a voice hint.
- *
+ *
* Input objects are expected for every segment
* of the track, also for those without a junction
- *
+ *
* VoiceHint objects in the output list are enriched
* by the voice-command, the total angle and the distance
* to the next hint
@@ -55,54 +50,46 @@ public final class VoiceHintProcessor
* @param inputs tracknodes, un reverse order
* @return voice hints, in forward order
*/
- public List process( List inputs )
- {
+ public List process(List inputs) {
List results = new ArrayList();
double distance = 0.;
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
int roundaboutExit = 0;
- for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
- {
- VoiceHint input = inputs.get( hintIdx );
+ for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
+ VoiceHint input = inputs.get(hintIdx);
float turnAngle = input.goodWay.turnangle;
distance += input.goodWay.linkdist;
int currentPrio = input.goodWay.getPrio();
int oldPrio = input.oldWay.getPrio();
- int minPrio = Math.min( oldPrio, currentPrio );
+ int minPrio = Math.min(oldPrio, currentPrio);
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
- if ( input.oldWay.isRoundabout() )
- {
- roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
+ if (input.oldWay.isRoundabout()) {
+ roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
boolean isExit = roundaboutExit == 0; // exit point is always exit
- if ( input.badWays != null )
- {
- for ( MessageData badWay : input.badWays )
- {
- if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
- {
+ if (input.badWays != null) {
+ for (MessageData badWay : input.badWays) {
+ if (!badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs(badWay.turnangle) < 120.) {
isExit = true;
}
}
}
- if ( isExit )
- {
+ if (isExit) {
roundaboutExit++;
}
continue;
}
- if ( roundaboutExit > 0 )
- {
- roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
+ if (roundaboutExit > 0) {
+ roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
input.angle = roundAboutTurnAngle;
input.distanceToNext = distance;
- input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
+ input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
distance = 0.;
- results.add( input );
+ results.add(input);
roundAboutTurnAngle = 0.f;
roundaboutExit = 0;
continue;
@@ -114,91 +101,77 @@ public final class VoiceHintProcessor
float minAngle = 180.f;
float minAbsAngeRaw = 180.f;
- if ( input.badWays != null )
- {
- for ( MessageData badWay : input.badWays )
- {
+ if (input.badWays != null) {
+ for (MessageData badWay : input.badWays) {
int badPrio = badWay.getPrio();
float badTurn = badWay.turnangle;
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
- if ( badPrio > maxPrioAll && !isHighway2Link )
- {
+ if (badPrio > maxPrioAll && !isHighway2Link) {
maxPrioAll = badPrio;
}
- if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
- {
- minAbsAngeRaw = Math.abs( badTurn );
+ if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) {
+ minAbsAngeRaw = Math.abs(badTurn);
}
- if ( badPrio < minPrio )
- {
+ if (badPrio < minPrio) {
continue; // ignore low prio ways
}
- if ( badWay.isBadOneway() )
- {
+ if (badWay.isBadOneway()) {
continue; // ignore wrong oneways
}
- if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
- {
+ if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
continue; // ways from the back should not trigger a slight turn
}
- if ( badPrio > maxPrioCandidates )
- {
+ if (badPrio > maxPrioCandidates) {
maxPrioCandidates = badPrio;
}
- if ( badTurn > maxAngle )
- {
+ if (badTurn > maxAngle) {
maxAngle = badTurn;
}
- if ( badTurn < minAngle )
- {
+ if (badTurn < minAngle) {
minAngle = badTurn;
}
}
}
- boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
+ boolean hasSomethingMoreStraight = Math.abs(turnAngle) - minAbsAngeRaw > 20.;
// unconditional triggers are all junctions with
// - higher detour prios than the minimum route prio (except link->highway junctions)
// - or candidate detours with higher prio then the route exit leg
- boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
+ boolean unconditionalTrigger = hasSomethingMoreStraight || (maxPrioAll > minPrio && !isLink2Highway) || (maxPrioCandidates > currentPrio);
// conditional triggers (=real turning angle required) are junctions
// with candidate detours equal in priority than the route exit leg
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
- if ( unconditionalTrigger || conditionalTrigger )
- {
+ if (unconditionalTrigger || conditionalTrigger) {
input.angle = turnAngle;
input.calcCommand();
boolean isStraight = input.cmd == VoiceHint.C;
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
// check for KR/KL
- if ( maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f ) )
- {
+ if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f)) {
input.cmd = VoiceHint.KR;
}
- if ( minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f ) )
- {
+ if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f)) {
input.cmd = VoiceHint.KL;
}
- input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
+ input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
input.distanceToNext = distance;
distance = 0.;
- results.add( input );
+ results.add(input);
}
- if ( results.size() > 0 && distance < catchingRange )
- {
- results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
+ if (results.size() > 0 && distance < catchingRange) {
+ results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
}
}
@@ -207,25 +180,21 @@ public final class VoiceHintProcessor
List results2 = new ArrayList();
int i = results.size();
- while( i > 0 )
- {
+ while (i > 0) {
VoiceHint hint = results.get(--i);
- if ( hint.cmd == 0 )
- {
+ if (hint.cmd == 0) {
hint.calcCommand();
}
- if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
- {
+ if (!(hint.needsRealTurn && hint.cmd == VoiceHint.C)) {
double dist = hint.distanceToNext;
// sum up other hints within the catching range (e.g. 40m)
- while( dist < catchingRange && i > 0 )
- {
- VoiceHint h2 = results.get(i-1);
+ while (dist < catchingRange && i > 0) {
+ VoiceHint h2 = results.get(i - 1);
dist = h2.distanceToNext;
- hint.distanceToNext+= dist;
+ hint.distanceToNext += dist;
hint.angle += h2.angle;
i--;
- if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
+ if (h2.isRoundabout()) // if we hit a roundabout, use that as the trigger
{
h2.angle = hint.angle;
hint = h2;
@@ -233,12 +202,11 @@ public final class VoiceHintProcessor
}
}
- if ( !explicitRoundabouts )
- {
+ if (!explicitRoundabouts) {
hint.roundaboutExit = 0; // use an angular hint instead
}
hint.calcCommand();
- results2.add( hint );
+ results2.add(hint);
}
}
return results2;
diff --git a/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java b/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java
index f8826ae..49acae9 100644
--- a/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java
+++ b/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java
@@ -12,11 +12,11 @@ import btools.util.CheapRuler;
public class OsmNodeNamedTest {
static int toOsmLon(double lon) {
- return (int)( ( lon + 180. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
+ return (int) ((lon + 180.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
static int toOsmLat(double lat) {
- return (int)( ( lat + 90. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
+ return (int) ((lat + 90.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
@Test
diff --git a/brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java b/brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java
index 3dea402..76c07b3 100644
--- a/brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java
+++ b/brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java
@@ -1,6 +1,6 @@
/**********************************************************************************************
- Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
-**********************************************************************************************/
+ Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
+ **********************************************************************************************/
package btools.router;
import static org.junit.Assert.assertEquals;
@@ -22,26 +22,26 @@ public class OsmNogoPolygonTest {
static OsmNogoPolygon polygon;
static OsmNogoPolygon polyline;
- static final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0 };
- static final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
+ static final double[] lons = {1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0};
+ static final double[] lats = {-1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0};
static int toOsmLon(double lon, int offset_x) {
- return (int)( ( lon + 180. ) *1000000. + 0.5)+offset_x; // see ServerHandler.readPosition()
+ return (int) ((lon + 180.) * 1000000. + 0.5) + offset_x; // see ServerHandler.readPosition()
}
static int toOsmLat(double lat, int offset_y) {
- return (int)( ( lat + 90. ) *1000000. + 0.5)+offset_y;
+ return (int) ((lat + 90.) * 1000000. + 0.5) + offset_y;
}
@BeforeClass
public static void setUp() throws Exception {
polygon = new OsmNogoPolygon(true);
- for (int i = 0; i= r1("+r1+")", diff >= 0);
+ double diff = r - r1;
+ assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
}
polyline.calcBoundingCircle();
r = polyline.radius;
- for (int i=0; i= r1("+r1+")", diff >= 0);
+ double diff = r - r1;
+ assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
}
}
@Test
public void testIsWithin() {
- double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
- double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
- boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
+ double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5,};
+ double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1,};
+ boolean[] within = {true, false, false, false, false, true, true, true, true, true,};
- for (int i=0; i 0 ) throw new IllegalArgumentException( "assign operator within expression" );
- exp.typ = ASSIGN_EXP;
- String variable = ctx.parseToken();
- if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
- if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
- if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
- exp.variableIdx = ctx.getVariableIdx( variable, true );
- if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
- }
- else if ( "not".equals( operator ) )
- {
- exp.typ = NOT_EXP;
- }
- else
- {
- nops = 0; // check elemantary expressions
- int idx = operator.indexOf( '=' );
- if ( idx >= 0 )
- {
- exp.typ = LOOKUP_EXP;
- String name = operator.substring( 0, idx );
- String values = operator.substring( idx+1 );
-
- exp.lookupNameIdx = ctx.getLookupNameIdx( name );
- if ( exp.lookupNameIdx < 0 )
- {
- throw new IllegalArgumentException( "unknown lookup name: " + name );
- }
- ctx.markLookupIdxUsed( exp.lookupNameIdx );
- StringTokenizer tk = new StringTokenizer( values, "|" );
- int nt = tk.countTokens();
- int nt2 = nt == 0 ? 1 : nt;
- exp.lookupValueIdxArray = new int[nt2];
- for( int ti=0; ti= 0 )
- {
- /*
- use of variable values
- assign no_height
- switch and not maxheight=
- lesser v:maxheight my_height true
- false
- */
- if (operator.startsWith("v:")) {
- String name = operator.substring(2);
- exp.typ = VARIABLE_GET_EXP;
- exp.lookupNameIdx = ctx.getLookupNameIdx( name );
- } else {
- String context = operator.substring( 0, idx );
- String varname = operator.substring( idx+1 );
- exp.typ = FOREIGN_VARIABLE_EXP;
- exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
- }
- }
- else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
- {
- exp.typ = VARIABLE_EXP;
- exp.variableIdx = idx;
- }
- else if ( "true".equals( operator ) )
- {
- exp.numberValue = 1.f;
- exp.typ = NUMBER_EXP;
- }
- else if ( "false".equals( operator ) )
- {
- exp.numberValue = 0.f;
- exp.typ = NUMBER_EXP;
- }
- else
- {
- try
- {
- exp.numberValue = Float.parseFloat( operator );
- exp.typ = NUMBER_EXP;
- }
- catch( NumberFormatException nfe )
- {
- throw new IllegalArgumentException( "unknown expression: " + operator );
- }
- }
- }
- }
- }
- // parse operands
- if ( nops > 0 )
- {
- exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null );
- }
- if ( nops > 1 )
- {
- if ( ifThenElse ) checkExpectedToken( ctx, "then" );
- exp.op2 = BExpression.parse( ctx, level+1, null );
- }
- if ( nops > 2 )
- {
- if ( ifThenElse ) checkExpectedToken( ctx, "else" );
- exp.op3 = BExpression.parse( ctx, level+1, null );
- }
- if ( brackets )
- {
- checkExpectedToken( ctx, ")" );
- }
- return exp;
- }
-
- private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
- {
- String token = ctx.parseToken();
- if ( ! expected.equals( token ) )
- {
- throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
- }
- }
-
- // Evaluate the expression
- public float evaluate( BExpressionContext ctx )
- {
- switch( typ )
- {
- case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
- case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f );
- case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
- case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
- case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
- case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
- case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
- case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
- case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
- case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
- case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
- case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
- case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
- case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
- case NUMBER_EXP: return numberValue;
- case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
- case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
- case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
- case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
- default: throw new IllegalArgumentException( "unknown op-code: " + typ );
- }
- }
-
- private float max( float v1, float v2 )
- {
- return v1 > v2 ? v1 : v2;
- }
-
- private float min( float v1, float v2 )
- {
- return v1 < v2 ? v1 : v2;
- }
-}
+package btools.expressions;
+
+import java.util.StringTokenizer;
+
+final class BExpression {
+ private static final int OR_EXP = 10;
+ private static final int AND_EXP = 11;
+ private static final int NOT_EXP = 12;
+
+ private static final int ADD_EXP = 20;
+ private static final int MULTIPLY_EXP = 21;
+ private static final int MAX_EXP = 22;
+ private static final int EQUAL_EXP = 23;
+ private static final int GREATER_EXP = 24;
+ private static final int MIN_EXP = 25;
+
+ private static final int SUB_EXP = 26;
+ private static final int LESSER_EXP = 27;
+ private static final int XOR_EXP = 28;
+
+ private static final int SWITCH_EXP = 30;
+ private static final int ASSIGN_EXP = 31;
+ private static final int LOOKUP_EXP = 32;
+ private static final int NUMBER_EXP = 33;
+ private static final int VARIABLE_EXP = 34;
+ private static final int FOREIGN_VARIABLE_EXP = 35;
+ private static final int VARIABLE_GET_EXP = 36;
+
+ private int typ;
+ private BExpression op1;
+ private BExpression op2;
+ private BExpression op3;
+ private float numberValue;
+ private int variableIdx;
+ private int lookupNameIdx;
+ private int[] lookupValueIdxArray;
+
+ // Parse the expression and all subexpression
+ public static BExpression parse(BExpressionContext ctx, int level) throws Exception {
+ return parse(ctx, level, null);
+ }
+
+ private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception {
+ boolean brackets = false;
+ String operator = ctx.parseToken();
+ if (optionalToken != null && optionalToken.equals(operator)) {
+ operator = ctx.parseToken();
+ }
+ if ("(".equals(operator)) {
+ brackets = true;
+ operator = ctx.parseToken();
+ }
+
+ if (operator == null) {
+ if (level == 0) return null;
+ else throw new IllegalArgumentException("unexpected end of file");
+ }
+
+ if (level == 0) {
+ if (!"assign".equals(operator)) {
+ throw new IllegalArgumentException("operator " + operator + " is invalid on toplevel (only 'assign' allowed)");
+ }
+ }
+
+ BExpression exp = new BExpression();
+ int nops = 3;
+ boolean ifThenElse = false;
+
+ if ("switch".equals(operator)) {
+ exp.typ = SWITCH_EXP;
+ } else if ("if".equals(operator)) {
+ exp.typ = SWITCH_EXP;
+ ifThenElse = true;
+ } else {
+ nops = 2; // check binary expressions
+
+ if ("or".equals(operator)) {
+ exp.typ = OR_EXP;
+ } else if ("and".equals(operator)) {
+ exp.typ = AND_EXP;
+ } else if ("multiply".equals(operator)) {
+ exp.typ = MULTIPLY_EXP;
+ } else if ("add".equals(operator)) {
+ exp.typ = ADD_EXP;
+ } else if ("max".equals(operator)) {
+ exp.typ = MAX_EXP;
+ } else if ("min".equals(operator)) {
+ exp.typ = MIN_EXP;
+ } else if ("equal".equals(operator)) {
+ exp.typ = EQUAL_EXP;
+ } else if ("greater".equals(operator)) {
+ exp.typ = GREATER_EXP;
+ } else if ("sub".equals(operator)) {
+ exp.typ = SUB_EXP;
+ } else if ("lesser".equals(operator)) {
+ exp.typ = LESSER_EXP;
+ } else if ("xor".equals(operator)) {
+ exp.typ = XOR_EXP;
+ } else {
+ nops = 1; // check unary expressions
+ if ("assign".equals(operator)) {
+ if (level > 0) throw new IllegalArgumentException("assign operator within expression");
+ exp.typ = ASSIGN_EXP;
+ String variable = ctx.parseToken();
+ if (variable == null) throw new IllegalArgumentException("unexpected end of file");
+ if (variable.indexOf('=') >= 0)
+ throw new IllegalArgumentException("variable name cannot contain '=': " + variable);
+ if (variable.indexOf(':') >= 0)
+ throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
+ exp.variableIdx = ctx.getVariableIdx(variable, true);
+ if (exp.variableIdx < ctx.getMinWriteIdx())
+ throw new IllegalArgumentException("cannot assign to readonly variable " + variable);
+ } else if ("not".equals(operator)) {
+ exp.typ = NOT_EXP;
+ } else {
+ nops = 0; // check elemantary expressions
+ int idx = operator.indexOf('=');
+ if (idx >= 0) {
+ exp.typ = LOOKUP_EXP;
+ String name = operator.substring(0, idx);
+ String values = operator.substring(idx + 1);
+
+ exp.lookupNameIdx = ctx.getLookupNameIdx(name);
+ if (exp.lookupNameIdx < 0) {
+ throw new IllegalArgumentException("unknown lookup name: " + name);
+ }
+ ctx.markLookupIdxUsed(exp.lookupNameIdx);
+ StringTokenizer tk = new StringTokenizer(values, "|");
+ int nt = tk.countTokens();
+ int nt2 = nt == 0 ? 1 : nt;
+ exp.lookupValueIdxArray = new int[nt2];
+ for (int ti = 0; ti < nt2; ti++) {
+ String value = ti < nt ? tk.nextToken() : "";
+ exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx(exp.lookupNameIdx, value);
+ if (exp.lookupValueIdxArray[ti] < 0) {
+ throw new IllegalArgumentException("unknown lookup value: " + value);
+ }
+ }
+ } else if ((idx = operator.indexOf(':')) >= 0) {
+ /*
+ use of variable values
+ assign no_height
+ switch and not maxheight=
+ lesser v:maxheight my_height true
+ false
+ */
+ if (operator.startsWith("v:")) {
+ String name = operator.substring(2);
+ exp.typ = VARIABLE_GET_EXP;
+ exp.lookupNameIdx = ctx.getLookupNameIdx(name);
+ } else {
+ String context = operator.substring(0, idx);
+ String varname = operator.substring(idx + 1);
+ exp.typ = FOREIGN_VARIABLE_EXP;
+ exp.variableIdx = ctx.getForeignVariableIdx(context, varname);
+ }
+ } else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) {
+ exp.typ = VARIABLE_EXP;
+ exp.variableIdx = idx;
+ } else if ("true".equals(operator)) {
+ exp.numberValue = 1.f;
+ exp.typ = NUMBER_EXP;
+ } else if ("false".equals(operator)) {
+ exp.numberValue = 0.f;
+ exp.typ = NUMBER_EXP;
+ } else {
+ try {
+ exp.numberValue = Float.parseFloat(operator);
+ exp.typ = NUMBER_EXP;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("unknown expression: " + operator);
+ }
+ }
+ }
+ }
+ }
+ // parse operands
+ if (nops > 0) {
+ exp.op1 = BExpression.parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null);
+ }
+ if (nops > 1) {
+ if (ifThenElse) checkExpectedToken(ctx, "then");
+ exp.op2 = BExpression.parse(ctx, level + 1, null);
+ }
+ if (nops > 2) {
+ if (ifThenElse) checkExpectedToken(ctx, "else");
+ exp.op3 = BExpression.parse(ctx, level + 1, null);
+ }
+ if (brackets) {
+ checkExpectedToken(ctx, ")");
+ }
+ return exp;
+ }
+
+ private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception {
+ String token = ctx.parseToken();
+ if (!expected.equals(token)) {
+ throw new IllegalArgumentException("unexpected token: " + token + ", expected: " + expected);
+ }
+ }
+
+ // Evaluate the expression
+ public float evaluate(BExpressionContext ctx) {
+ switch (typ) {
+ case OR_EXP:
+ return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f);
+ case XOR_EXP:
+ return ((op1.evaluate(ctx) != 0.f) ^ (op2.evaluate(ctx) != 0.f) ? 1.f : 0.f);
+ case AND_EXP:
+ return op1.evaluate(ctx) != 0.f ? (op2.evaluate(ctx) != 0.f ? 1.f : 0.f) : 0.f;
+ case ADD_EXP:
+ return op1.evaluate(ctx) + op2.evaluate(ctx);
+ case SUB_EXP:
+ return op1.evaluate(ctx) - op2.evaluate(ctx);
+ case MULTIPLY_EXP:
+ return op1.evaluate(ctx) * op2.evaluate(ctx);
+ case MAX_EXP:
+ return max(op1.evaluate(ctx), op2.evaluate(ctx));
+ case MIN_EXP:
+ return min(op1.evaluate(ctx), op2.evaluate(ctx));
+ case EQUAL_EXP:
+ return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
+ case GREATER_EXP:
+ return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
+ case LESSER_EXP:
+ return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
+ case SWITCH_EXP:
+ return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
+ case ASSIGN_EXP:
+ return ctx.assign(variableIdx, op1.evaluate(ctx));
+ case LOOKUP_EXP:
+ return ctx.getLookupMatch(lookupNameIdx, lookupValueIdxArray);
+ case NUMBER_EXP:
+ return numberValue;
+ case VARIABLE_EXP:
+ return ctx.getVariableValue(variableIdx);
+ case FOREIGN_VARIABLE_EXP:
+ return ctx.getForeignVariableValue(variableIdx);
+ case VARIABLE_GET_EXP:
+ return ctx.getLookupValue(lookupNameIdx);
+ case NOT_EXP:
+ return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
+ default:
+ throw new IllegalArgumentException("unknown op-code: " + typ);
+ }
+ }
+
+ private float max(float v1, float v2) {
+ return v1 > v2 ? v1 : v2;
+ }
+
+ private float min(float v1, float v2) {
+ return v1 < v2 ? v1 : v2;
+ }
+}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
index 8620e12..141595f 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java
@@ -1,1087 +1,950 @@
-// context for simple expression
-// context means:
-// - the local variables
-// - the local variable names
-// - the lookup-input variables
-
-package btools.expressions;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.Locale;
-
-import btools.util.BitCoderContext;
-import btools.util.Crc32;
-import btools.util.IByteArrayUnifier;
-import btools.util.LruMap;
-
-
-public abstract class BExpressionContext implements IByteArrayUnifier
-{
- private static final String CONTEXT_TAG = "---context:";
- private static final String MODEL_TAG = "---model:";
-
- private String context;
- private boolean _inOurContext = false;
- private BufferedReader _br = null;
- private boolean _readerDone = false;
-
- public String _modelClass;
-
- private Map lookupNumbers = new HashMap();
- private ArrayList lookupValues = new ArrayList();
- private ArrayList lookupNames = new ArrayList();
- private ArrayList lookupHistograms = new ArrayList();
- private boolean[] lookupIdxUsed;
-
- private boolean lookupDataFrozen = false;
-
- private int[] lookupData = new int[0];
-
- private byte[] abBuf = new byte[256];
- private BitCoderContext ctxEndode = new BitCoderContext( abBuf );
- private BitCoderContext ctxDecode = new BitCoderContext( new byte[0] );
-
- private Map variableNumbers = new HashMap();
-
- private float[] variableData;
-
-
- // hash-cache for function results
- private CacheNode probeCacheNode = new CacheNode();
- private LruMap cache;
-
- private VarWrapper probeVarSet = new VarWrapper();
- private LruMap resultVarCache;
-
- private List expressionList;
-
- private int minWriteIdx;
-
- // build-in variable indexes for fast access
- private int[] buildInVariableIdx;
- private int nBuildInVars;
-
- private float[] currentVars;
- private int currentVarOffset;
-
- private BExpressionContext foreignContext;
-
- protected void setInverseVars()
- {
- currentVarOffset = nBuildInVars;
- }
-
- abstract String[] getBuildInVariableNames();
-
- public final float getBuildInVariable( int idx )
- {
- return currentVars[idx+currentVarOffset];
- }
-
- private int linenr;
-
- public BExpressionMetaData meta;
- private boolean lookupDataValid = false;
-
- protected BExpressionContext( String context, BExpressionMetaData meta )
- {
- this( context, 4096, meta );
- }
-
- /**
- * Create an Expression-Context for the given node
- *
- * @param context global, way or node - context of that instance
- * @param hashSize size of hashmap for result caching
- */
- protected BExpressionContext( String context, int hashSize, BExpressionMetaData meta )
- {
- this.context = context;
- this.meta = meta;
-
- if ( meta != null ) meta.registerListener(context, this );
-
- if ( Boolean.getBoolean( "disableExpressionCache" ) ) hashSize = 1;
-
- // create the expression cache
- if ( hashSize > 0 )
- {
- cache = new LruMap( 4*hashSize, hashSize );
- resultVarCache = new LruMap( 4096, 4096 );
- }
- }
-
- /**
- * encode internal lookup data to a byte array
- */
- public byte[] encode()
- {
- if ( !lookupDataValid ) throw new IllegalArgumentException( "internal error: encoding undefined data?" );
- return encode( lookupData );
- }
-
- public byte[] encode( int[] ld )
- {
- BitCoderContext ctx = ctxEndode;
- ctx.reset();
-
- int skippedTags = 0;
- int nonNullTags= 0;
-
- // (skip first bit ("reversedirection") )
-
- // all others are generic
- for( int inum = 1; inum < lookupValues.size(); inum++ ) // loop over lookup names
- {
- int d = ld[inum];
- if ( d == 0 )
- {
- skippedTags++;
- continue;
- }
- ctx.encodeVarBits( skippedTags+1 );
- nonNullTags++;
- skippedTags = 0;
-
- // 0 excluded already, 1 (=unknown) we rotate up to 8
- // to have the good code space for the popular values
- int dd = d < 2 ? 7 : ( d < 9 ? d - 2 : d - 1);
- ctx.encodeVarBits( dd );
- }
- ctx.encodeVarBits( 0 );
-
- if ( nonNullTags == 0) return null;
-
- int len = ctx.closeAndGetEncodedLength();
- byte[] ab = new byte[len];
- System.arraycopy( abBuf, 0, ab, 0, len );
-
-
- // crosscheck: decode and compare
- int[] ld2 = new int[lookupValues.size()];
- decode( ld2, false, ab );
- for( int inum = 1; inum < lookupValues.size(); inum++ ) // loop over lookup names (except reverse dir)
- {
- if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding inum=" + inum + " val=" + ld[inum] + " " + getKeyValueDescription(false, ab) );
- }
-
- return ab;
- }
-
-
- /**
- * decode byte array to internal lookup data
- */
- public void decode( byte[] ab )
- {
- decode( lookupData, false, ab );
- lookupDataValid = true;
- }
-
-
-
- /**
- * decode a byte-array into a lookup data array
- */
- private void decode( int[] ld, boolean inverseDirection, byte[] ab )
- {
- BitCoderContext ctx = ctxDecode;
- ctx.reset( ab );
-
- // start with first bit hardwired ("reversedirection")
- ld[0] = inverseDirection ? 2 : 0;
-
- // all others are generic
- int inum = 1;
- for(;;)
- {
- int delta = ctx.decodeVarBits();
- if ( delta == 0) break;
- if ( inum + delta > ld.length ) break; // higher minor version is o.k.
-
- while ( delta-- > 1 ) ld[inum++] = 0;
-
- // see encoder for value rotation
- int dd = ctx.decodeVarBits();
- int d = dd == 7 ? 1 : ( dd < 7 ? dd + 2 : dd + 1);
- if ( d >= lookupValues.get(inum).length && d < 1000) d = 1; // map out-of-range to unknown
- ld[inum++] = d;
- }
- while( inum < ld.length ) ld[inum++] = 0;
- }
-
- public String getKeyValueDescription( boolean inverseDirection, byte[] ab )
- {
- StringBuilder sb = new StringBuilder( 200 );
- decode( lookupData, inverseDirection, ab );
- for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names
- {
- BExpressionLookupValue[] va = lookupValues.get(inum);
- int val = lookupData[inum];
- String value = (val>=1000) ? Float.toString((val-1000)/100f) : va[val].toString();
- if ( value != null && value.length() > 0 )
- {
- if ( sb.length() > 0 ) sb.append( ' ' );
- sb.append(lookupNames.get( inum ) + "=" + value );
- }
- }
- return sb.toString();
- }
-
- public List getKeyValueList( boolean inverseDirection, byte[] ab )
- {
- ArrayList res = new ArrayList();
- decode( lookupData, inverseDirection, ab );
- for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names
- {
- BExpressionLookupValue[] va = lookupValues.get(inum);
- int val = lookupData[inum];
- // no negative values
- String value = (val>=1000) ? Float.toString((val-1000)/100f) : va[val].toString();
- if ( value != null && value.length() > 0 )
- {
- res.add( lookupNames.get( inum ) );
- res.add( value );
- }
- }
- return res;
- }
-
- public int getLookupKey(String name) {
- int res = -1;
- try {
- res = lookupNumbers.get(name).intValue();
- } catch (Exception e ) {}
- return res;
- }
-
- public float getLookupValue(int key) {
- float res = 0f;
- int val = lookupData[key];
- if (val == 0) return Float.NaN;
- res = (val-1000)/100f;
- return res;
- }
-
- public float getLookupValue(boolean inverseDirection, byte[] ab, int key) {
- float res = 0f;
- decode( lookupData, inverseDirection, ab );
- int val = lookupData[key];
- if (val == 0) return Float.NaN;
- res = (val-1000)/100f;
- return res;
- }
-
- private int parsedLines = 0;
- private boolean fixTagsWritten = false;
-
- public void parseMetaLine( String line )
- {
- parsedLines++;
- StringTokenizer tk = new StringTokenizer( line, " " );
- String name = tk.nextToken();
- String value = tk.nextToken();
- int idx = name.indexOf( ';' );
- if ( idx >= 0 ) name = name.substring( 0, idx );
-
- if ( !fixTagsWritten )
- {
- fixTagsWritten = true;
- if ( "way".equals( context ) ) addLookupValue( "reversedirection", "yes", null );
- else if ( "node".equals( context ) ) addLookupValue( "nodeaccessgranted", "yes", null );
- }
- if ( "reversedirection".equals( name ) ) return; // this is hardcoded
- if ( "nodeaccessgranted".equals( name ) ) return; // this is hardcoded
- BExpressionLookupValue newValue = addLookupValue( name, value, null );
-
- // add aliases
- while( newValue != null && tk.hasMoreTokens() ) newValue.addAlias( tk.nextToken() );
- }
-
- public void finishMetaParsing()
- {
- if ( parsedLines == 0 && !"global".equals(context) )
- {
- throw new IllegalArgumentException( "lookup table does not contain data for context " + context + " (old version?)" );
- }
-
- // post-process metadata:
- lookupDataFrozen = true;
-
- lookupIdxUsed = new boolean[lookupValues.size()];
- }
-
- public final void evaluate( int[] lookupData2 )
- {
- lookupData = lookupData2;
- evaluate();
- }
-
- private void evaluate()
- {
- int n = expressionList.size();
- for( int expidx = 0; expidx < n; expidx++ )
- {
- expressionList.get( expidx ).evaluate( this );
- }
- }
-
- private long requests;
- private long requests2;
- private long cachemisses;
-
- public String cacheStats()
- {
- return "requests=" + requests + " requests2=" + requests2 + " cachemisses=" + cachemisses;
- }
-
- private CacheNode lastCacheNode = new CacheNode();
-
- // @Override
- public final byte[] unify( byte[] ab, int offset, int len )
- {
- probeCacheNode.ab = null; // crc based cache lookup only
- probeCacheNode.hash = Crc32.crc( ab, offset, len );
-
- CacheNode cn = (CacheNode)cache.get( probeCacheNode );
- if ( cn != null )
- {
- byte[] cab = cn.ab;
- if ( cab.length == len )
- {
- for( int i=0; i counts = new TreeMap();
- // first count
- for( String name: lookupNumbers.keySet() )
- {
- int cnt = 0;
- int inum = lookupNumbers.get(name).intValue();
- int[] histo = lookupHistograms.get(inum);
-// if ( histo.length == 500 ) continue;
- for( int i=2; i 0 )
- {
- String key = counts.lastEntry().getKey();
- String name = counts.get(key);
- counts.remove( key );
- int inum = lookupNumbers.get(name).intValue();
- BExpressionLookupValue[] values = lookupValues.get(inum);
- int[] histo = lookupHistograms.get(inum);
- if ( values.length == 1000 ) continue;
- String[] svalues = new String[values.length];
- for( int i=0; i=0; i-- )
- {
- System.out.println( name + ";" + svalues[i] );
- }
- }
- }
-
- /**
- * @return a new lookupData array, or null if no metadata defined
- */
- public int[] createNewLookupData()
- {
- if ( lookupDataFrozen )
- {
- return new int[lookupValues.size()];
- }
- return null;
- }
-
- /**
- * generate random values for regression testing
- */
- public int[] generateRandomValues( Random rnd )
- {
- int[] data = createNewLookupData();
- data[0] = 2*rnd.nextInt( 2 ); // reverse-direction = 0 or 2
- for( int inum = 1; inum < data.length; inum++ )
- {
- int nvalues = lookupValues.get( inum ).length;
- data[inum] = 0;
- if ( inum > 1 && rnd.nextInt( 10 ) > 0 ) continue; // tags other than highway only 10%
- data[inum] = rnd.nextInt( nvalues );
- }
- lookupDataValid = true;
- return data;
- }
-
- public void assertAllVariablesEqual( BExpressionContext other )
- {
- int nv = variableData.length;
- int nv2 = other.variableData.length;
- if ( nv != nv2 ) throw new RuntimeException( "mismatch in variable-count: " + nv + "<->" + nv2 );
- for( int i=0; i" + other.variableData[i]
- + "\ntags = " + getKeyValueDescription( false, encode() ) );
- }
- }
- }
-
- public String variableName( int idx )
- {
- for( Map.Entry e : variableNumbers.entrySet() )
- {
- if ( e.getValue().intValue() == idx )
- {
- return e.getKey();
- }
- }
- throw new RuntimeException( "no variable for index" + idx );
- }
-
- /**
- * add a new lookup-value for the given name to the given lookupData array.
- * If no array is given (null value passed), the value is added to
- * the context-binded array. In that case, unknown names and values are
- * created dynamically.
- *
- * @return a newly created value element, if any, to optionally add aliases
- */
- public BExpressionLookupValue addLookupValue( String name, String value, int[] lookupData2 )
- {
- BExpressionLookupValue newValue = null;
- Integer num = lookupNumbers.get( name );
- if ( num == null )
- {
- if ( lookupData2 != null )
- {
- // do not create unknown name for external data array
- return newValue;
- }
-
- // unknown name, create
- num = Integer.valueOf( lookupValues.size() );
- lookupNumbers.put( name, num );
- lookupNames.add( name );
- lookupValues.add( new BExpressionLookupValue[]{ new BExpressionLookupValue( "" )
- , new BExpressionLookupValue( "unknown" ) } );
- lookupHistograms.add( new int[2] );
- int[] ndata = new int[lookupData.length+1];
- System.arraycopy( lookupData, 0, ndata, 0, lookupData.length );
- lookupData = ndata;
- }
-
- // look for that value
- int inum = num.intValue();
- BExpressionLookupValue[] values = lookupValues.get( inum );
- int[] histo = lookupHistograms.get( inum );
- int i=0;
- boolean bFoundAsterix = false;
- for( ; i", "");
- value = value.replace("_", "");
- if (value.indexOf("-") == 0) value = value.substring(1);
- if (value.indexOf("~") == 0) value = value.substring(1);
- if (value.contains("-")) { // replace eg. 1.4-1.6 m
- String tmp = value.substring(value.indexOf("-")+1).replaceAll("[0-9.,-]", "");
- value = value.substring(0, value.indexOf("-")) + tmp;
- }
- // do some value conversion
- if (value.toLowerCase().contains("ft")) {
- float foot = 0f;
- int inch = 0;
- String[] sa = value.toLowerCase().trim().split("ft");
- if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim());
- if (sa.length == 2) {
- value = sa[1];
- if (value.indexOf("in") > 0) value = value.substring(0,value.indexOf("in"));
- inch = Integer.parseInt(value.trim());
- foot += inch/12f;
- }
- value = String.format(Locale.US, "%3.1f", foot*0.3048f);
- }
- if (value.toLowerCase().contains("'")) {
- float foot = 0f;
- int inch = 0;
- String[] sa = value.toLowerCase().trim().split("'");
- if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim());
- if (sa.length == 2) {
- value = sa[1];
- if (value.indexOf("''") > 0) value = value.substring(0,value.indexOf("''"));
- if (value.indexOf("\"") > 0) value = value.substring(0,value.indexOf("\""));
- inch = Integer.parseInt(value.trim());
- foot += inch/12f;
- }
- value = String.format(Locale.US, "%3.1f", foot*0.3048f);
- }
- else if (value.contains("in") || value.contains("\"")) {
- float inch = 0f;
- if (value.indexOf("in") > 0) value = value.substring(0,value.indexOf("in"));
- if (value.indexOf("\"") > 0) value = value.substring(0,value.indexOf("\""));
- inch = Float.parseFloat(value.trim());
- value = String.format(Locale.US, "%3.1f",inch*0.0254f);
- }
- else if (value.toLowerCase().contains("feet") || value.toLowerCase().contains("foot")) {
- float feet = 0f;
- String s = value.substring(0, value.toLowerCase().indexOf("f") );
- feet = Float.parseFloat(s.trim());
- value = String.format(Locale.US, "%3.1f", feet*0.3048f);
- }
- else if (value.toLowerCase().contains("fathom") || value.toLowerCase().contains("fm")) {
- float fathom = 0f;
- String s = value.substring(0, value.toLowerCase().indexOf("f") );
- fathom = Float.parseFloat(s.trim());
- value = String.format(Locale.US, "%3.1f", fathom*1.8288f);
- }
- else if (value.contains("cm")) {
- String[] sa = value.trim().split("cm");
- if (sa.length == 1) value = sa[0].trim();
- float cm = Float.parseFloat(value.trim());
- value = String.format(Locale.US, "%3.1f", cm*100f);
- }
- else if (value.toLowerCase().contains("meter")) {
- String s = value.substring(0, value.toLowerCase().indexOf("m") );
- value = s.trim();
- }
- else if (value.toLowerCase().contains("mph")) {
- value = value.replace("_", "");
- String[] sa = value.trim().toLowerCase().split("mph");
- if (sa.length >= 1) value = sa[0].trim();
- float mph = Float.parseFloat(value.trim());
- value = String.format(Locale.US, "%3.1f", mph*1.609344f);
- }
- else if (value.toLowerCase().contains("knot")) {
- String[] sa = value.trim().toLowerCase().split("knot");
- if (sa.length >= 1) value = sa[0].trim();
- float nm = Float.parseFloat(value.trim());
- value = String.format(Locale.US, "%3.1f", nm*1.852f);
- }
- else if (value.contains("kmh") || value.contains("km/h") || value.contains("kph")) {
- String[] sa = value.trim().split("k");
- if (sa.length == 1) value = sa[0].trim();
- }
- else if (value.contains("m")) {
- String s = value.substring(0, value.toLowerCase().indexOf("m") );
- value = s.trim();
- }
- else if (value.contains("(")) {
- String s = value.substring(0, value.toLowerCase().indexOf("(") );
- value = s.trim();
- }
- // found negative maxdraft values
- // no negative values
- // values are float with 2 decimals
- lookupData2[inum] = 1000 + (int)(Math.abs(Float.parseFloat(value))*100f);
- } catch ( Exception e) {
- // ignore errors
- System.err.println( "error for " + name + " " + org + " trans " + value + " " + e.getMessage());
- lookupData2[inum] = 0;
- }
- }
- return newValue;
- }
-
- if ( i == 499 )
- {
- // System.out.println( "value limit reached for: " + name );
- }
- if ( i == 500 )
- {
- return newValue;
- }
- // unknown value, create
- BExpressionLookupValue[] nvalues = new BExpressionLookupValue[values.length+1];
- int[] nhisto = new int[values.length+1];
- System.arraycopy( values, 0, nvalues, 0, values.length );
- System.arraycopy( histo, 0, nhisto, 0, histo.length );
- values = nvalues;
- histo = nhisto;
- newValue = new BExpressionLookupValue( value );
- values[i] = newValue;
- lookupHistograms.set(inum, histo);
- lookupValues.set(inum, values);
- }
-
- histo[i]++;
-
- // finally remember the actual data
- if ( lookupData2 != null ) lookupData2[inum] = i;
- else lookupData[inum] = i;
- return newValue;
- }
-
- /**
- * add a value-index to to internal array
- * value-index means 0=unknown, 1=other, 2=value-x, ...
- */
- public void addLookupValue( String name, int valueIndex )
- {
- Integer num = lookupNumbers.get( name );
- if ( num == null )
- {
- return;
- }
-
- // look for that value
- int inum = num.intValue();
- int nvalues = lookupValues.get( inum ).length;
- if ( valueIndex < 0 || valueIndex >= nvalues ) throw new IllegalArgumentException( "value index out of range for name " + name + ": " + valueIndex );
- lookupData[inum] = valueIndex;
- }
-
-
- /**
- * special hack for yes/proposed relations:
- * add a lookup value if not yet a smaller, > 1 value was added
- * add a 2=yes if the provided value is out of range
- * value-index means here 0=unknown, 1=other, 2=yes, 3=proposed
- */
- public void addSmallestLookupValue( String name, int valueIndex )
- {
- Integer num = lookupNumbers.get( name );
- if ( num == null )
- {
- return;
- }
-
- // look for that value
- int inum = num.intValue();
- int nvalues = lookupValues.get( inum ).length;
- int oldValueIndex = lookupData[inum];
- if ( oldValueIndex > 1 && oldValueIndex < valueIndex )
- {
- return;
- }
- if ( valueIndex >= nvalues )
- {
- valueIndex = nvalues-1;
- }
- if ( valueIndex < 0 ) throw new IllegalArgumentException( "value index out of range for name " + name + ": " + valueIndex );
- lookupData[inum] = valueIndex;
- }
-
- public boolean getBooleanLookupValue( String name )
- {
- Integer num = lookupNumbers.get( name );
- return num != null && lookupData[num.intValue()] == 2;
- }
-
- public int getOutputVariableIndex( String name, boolean mustExist )
- {
- int idx = getVariableIdx( name, false );
- if ( idx < 0 )
- {
- if ( mustExist )
- {
- throw new IllegalArgumentException( "unknown variable: " + name );
- }
- }
- else if ( idx < minWriteIdx )
- {
- throw new IllegalArgumentException( "bad access to global variable: " + name );
- }
- for( int i=0; i _parseFile( File file ) throws Exception
- {
- _br = new BufferedReader( new FileReader( file ) );
- _readerDone = false;
- List result = new ArrayList();
- for(;;)
- {
- BExpression exp = BExpression.parse( this, 0 );
- if ( exp == null ) break;
- result.add( exp );
- }
- _br.close();
- _br = null;
- return result;
- }
-
- public void setVariableValue(String name, float value, boolean create ) {
- Integer num = variableNumbers.get( name );
- if ( num != null )
- {
- variableData[num.intValue()] = value;
- }
- }
-
- public float getVariableValue( String name, float defaultValue )
- {
- Integer num = variableNumbers.get( name );
- return num == null ? defaultValue : getVariableValue( num.intValue() );
- }
-
- float getVariableValue( int variableIdx )
- {
- return variableData[variableIdx];
- }
-
- int getVariableIdx( String name, boolean create )
- {
- Integer num = variableNumbers.get( name );
- if ( num == null )
- {
- if ( create )
- {
- num = new Integer( variableNumbers.size() );
- variableNumbers.put( name, num );
- }
- else
- {
- return -1;
- }
- }
- return num.intValue();
- }
-
- int getMinWriteIdx()
- {
- return minWriteIdx;
- }
-
- float getLookupMatch( int nameIdx, int[] valueIdxArray )
- {
- for( int i=0; i 0 ) return sb.toString();
- else continue;
- }
- if ( c == '#' && sb.length() == 0 ) inComment = true;
- else sb.append( c );
- }
- }
-
- float assign( int variableIdx, float value )
- {
- variableData[variableIdx] = value;
- return value;
- }
-
-}
+// context for simple expression
+// context means:
+// - the local variables
+// - the local variable names
+// - the lookup-input variables
+
+package btools.expressions;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.Locale;
+
+import btools.util.BitCoderContext;
+import btools.util.Crc32;
+import btools.util.IByteArrayUnifier;
+import btools.util.LruMap;
+
+
+public abstract class BExpressionContext implements IByteArrayUnifier {
+ private static final String CONTEXT_TAG = "---context:";
+ private static final String MODEL_TAG = "---model:";
+
+ private String context;
+ private boolean _inOurContext = false;
+ private BufferedReader _br = null;
+ private boolean _readerDone = false;
+
+ public String _modelClass;
+
+ private Map lookupNumbers = new HashMap();
+ private ArrayList lookupValues = new ArrayList();
+ private ArrayList lookupNames = new ArrayList();
+ private ArrayList lookupHistograms = new ArrayList();
+ private boolean[] lookupIdxUsed;
+
+ private boolean lookupDataFrozen = false;
+
+ private int[] lookupData = new int[0];
+
+ private byte[] abBuf = new byte[256];
+ private BitCoderContext ctxEndode = new BitCoderContext(abBuf);
+ private BitCoderContext ctxDecode = new BitCoderContext(new byte[0]);
+
+ private Map variableNumbers = new HashMap();
+
+ private float[] variableData;
+
+
+ // hash-cache for function results
+ private CacheNode probeCacheNode = new CacheNode();
+ private LruMap cache;
+
+ private VarWrapper probeVarSet = new VarWrapper();
+ private LruMap resultVarCache;
+
+ private List expressionList;
+
+ private int minWriteIdx;
+
+ // build-in variable indexes for fast access
+ private int[] buildInVariableIdx;
+ private int nBuildInVars;
+
+ private float[] currentVars;
+ private int currentVarOffset;
+
+ private BExpressionContext foreignContext;
+
+ protected void setInverseVars() {
+ currentVarOffset = nBuildInVars;
+ }
+
+ abstract String[] getBuildInVariableNames();
+
+ public final float getBuildInVariable(int idx) {
+ return currentVars[idx + currentVarOffset];
+ }
+
+ private int linenr;
+
+ public BExpressionMetaData meta;
+ private boolean lookupDataValid = false;
+
+ protected BExpressionContext(String context, BExpressionMetaData meta) {
+ this(context, 4096, meta);
+ }
+
+ /**
+ * Create an Expression-Context for the given node
+ *
+ * @param context global, way or node - context of that instance
+ * @param hashSize size of hashmap for result caching
+ */
+ protected BExpressionContext(String context, int hashSize, BExpressionMetaData meta) {
+ this.context = context;
+ this.meta = meta;
+
+ if (meta != null) meta.registerListener(context, this);
+
+ if (Boolean.getBoolean("disableExpressionCache")) hashSize = 1;
+
+ // create the expression cache
+ if (hashSize > 0) {
+ cache = new LruMap(4 * hashSize, hashSize);
+ resultVarCache = new LruMap(4096, 4096);
+ }
+ }
+
+ /**
+ * encode internal lookup data to a byte array
+ */
+ public byte[] encode() {
+ if (!lookupDataValid)
+ throw new IllegalArgumentException("internal error: encoding undefined data?");
+ return encode(lookupData);
+ }
+
+ public byte[] encode(int[] ld) {
+ BitCoderContext ctx = ctxEndode;
+ ctx.reset();
+
+ int skippedTags = 0;
+ int nonNullTags = 0;
+
+ // (skip first bit ("reversedirection") )
+
+ // all others are generic
+ for (int inum = 1; inum < lookupValues.size(); inum++) // loop over lookup names
+ {
+ int d = ld[inum];
+ if (d == 0) {
+ skippedTags++;
+ continue;
+ }
+ ctx.encodeVarBits(skippedTags + 1);
+ nonNullTags++;
+ skippedTags = 0;
+
+ // 0 excluded already, 1 (=unknown) we rotate up to 8
+ // to have the good code space for the popular values
+ int dd = d < 2 ? 7 : (d < 9 ? d - 2 : d - 1);
+ ctx.encodeVarBits(dd);
+ }
+ ctx.encodeVarBits(0);
+
+ if (nonNullTags == 0) return null;
+
+ int len = ctx.closeAndGetEncodedLength();
+ byte[] ab = new byte[len];
+ System.arraycopy(abBuf, 0, ab, 0, len);
+
+
+ // crosscheck: decode and compare
+ int[] ld2 = new int[lookupValues.size()];
+ decode(ld2, false, ab);
+ for (int inum = 1; inum < lookupValues.size(); inum++) // loop over lookup names (except reverse dir)
+ {
+ if (ld2[inum] != ld[inum])
+ throw new RuntimeException("assertion failed encoding inum=" + inum + " val=" + ld[inum] + " " + getKeyValueDescription(false, ab));
+ }
+
+ return ab;
+ }
+
+
+ /**
+ * decode byte array to internal lookup data
+ */
+ public void decode(byte[] ab) {
+ decode(lookupData, false, ab);
+ lookupDataValid = true;
+ }
+
+
+ /**
+ * decode a byte-array into a lookup data array
+ */
+ private void decode(int[] ld, boolean inverseDirection, byte[] ab) {
+ BitCoderContext ctx = ctxDecode;
+ ctx.reset(ab);
+
+ // start with first bit hardwired ("reversedirection")
+ ld[0] = inverseDirection ? 2 : 0;
+
+ // all others are generic
+ int inum = 1;
+ for (; ; ) {
+ int delta = ctx.decodeVarBits();
+ if (delta == 0) break;
+ if (inum + delta > ld.length) break; // higher minor version is o.k.
+
+ while (delta-- > 1) ld[inum++] = 0;
+
+ // see encoder for value rotation
+ int dd = ctx.decodeVarBits();
+ int d = dd == 7 ? 1 : (dd < 7 ? dd + 2 : dd + 1);
+ if (d >= lookupValues.get(inum).length && d < 1000) d = 1; // map out-of-range to unknown
+ ld[inum++] = d;
+ }
+ while (inum < ld.length) ld[inum++] = 0;
+ }
+
+ public String getKeyValueDescription(boolean inverseDirection, byte[] ab) {
+ StringBuilder sb = new StringBuilder(200);
+ decode(lookupData, inverseDirection, ab);
+ for (int inum = 0; inum < lookupValues.size(); inum++) // loop over lookup names
+ {
+ BExpressionLookupValue[] va = lookupValues.get(inum);
+ int val = lookupData[inum];
+ String value = (val >= 1000) ? Float.toString((val - 1000) / 100f) : va[val].toString();
+ if (value != null && value.length() > 0) {
+ if (sb.length() > 0) sb.append(' ');
+ sb.append(lookupNames.get(inum) + "=" + value);
+ }
+ }
+ return sb.toString();
+ }
+
+ public List getKeyValueList(boolean inverseDirection, byte[] ab) {
+ ArrayList res = new ArrayList();
+ decode(lookupData, inverseDirection, ab);
+ for (int inum = 0; inum < lookupValues.size(); inum++) // loop over lookup names
+ {
+ BExpressionLookupValue[] va = lookupValues.get(inum);
+ int val = lookupData[inum];
+ // no negative values
+ String value = (val >= 1000) ? Float.toString((val - 1000) / 100f) : va[val].toString();
+ if (value != null && value.length() > 0) {
+ res.add(lookupNames.get(inum));
+ res.add(value);
+ }
+ }
+ return res;
+ }
+
+ public int getLookupKey(String name) {
+ int res = -1;
+ try {
+ res = lookupNumbers.get(name).intValue();
+ } catch (Exception e) {
+ }
+ return res;
+ }
+
+ public float getLookupValue(int key) {
+ float res = 0f;
+ int val = lookupData[key];
+ if (val == 0) return Float.NaN;
+ res = (val - 1000) / 100f;
+ return res;
+ }
+
+ public float getLookupValue(boolean inverseDirection, byte[] ab, int key) {
+ float res = 0f;
+ decode(lookupData, inverseDirection, ab);
+ int val = lookupData[key];
+ if (val == 0) return Float.NaN;
+ res = (val - 1000) / 100f;
+ return res;
+ }
+
+ private int parsedLines = 0;
+ private boolean fixTagsWritten = false;
+
+ public void parseMetaLine(String line) {
+ parsedLines++;
+ StringTokenizer tk = new StringTokenizer(line, " ");
+ String name = tk.nextToken();
+ String value = tk.nextToken();
+ int idx = name.indexOf(';');
+ if (idx >= 0) name = name.substring(0, idx);
+
+ if (!fixTagsWritten) {
+ fixTagsWritten = true;
+ if ("way".equals(context)) addLookupValue("reversedirection", "yes", null);
+ else if ("node".equals(context)) addLookupValue("nodeaccessgranted", "yes", null);
+ }
+ if ("reversedirection".equals(name)) return; // this is hardcoded
+ if ("nodeaccessgranted".equals(name)) return; // this is hardcoded
+ BExpressionLookupValue newValue = addLookupValue(name, value, null);
+
+ // add aliases
+ while (newValue != null && tk.hasMoreTokens()) newValue.addAlias(tk.nextToken());
+ }
+
+ public void finishMetaParsing() {
+ if (parsedLines == 0 && !"global".equals(context)) {
+ throw new IllegalArgumentException("lookup table does not contain data for context " + context + " (old version?)");
+ }
+
+ // post-process metadata:
+ lookupDataFrozen = true;
+
+ lookupIdxUsed = new boolean[lookupValues.size()];
+ }
+
+ public final void evaluate(int[] lookupData2) {
+ lookupData = lookupData2;
+ evaluate();
+ }
+
+ private void evaluate() {
+ int n = expressionList.size();
+ for (int expidx = 0; expidx < n; expidx++) {
+ expressionList.get(expidx).evaluate(this);
+ }
+ }
+
+ private long requests;
+ private long requests2;
+ private long cachemisses;
+
+ public String cacheStats() {
+ return "requests=" + requests + " requests2=" + requests2 + " cachemisses=" + cachemisses;
+ }
+
+ private CacheNode lastCacheNode = new CacheNode();
+
+ // @Override
+ public final byte[] unify(byte[] ab, int offset, int len) {
+ probeCacheNode.ab = null; // crc based cache lookup only
+ probeCacheNode.hash = Crc32.crc(ab, offset, len);
+
+ CacheNode cn = (CacheNode) cache.get(probeCacheNode);
+ if (cn != null) {
+ byte[] cab = cn.ab;
+ if (cab.length == len) {
+ for (int i = 0; i < len; i++) {
+ if (cab[i] != ab[i + offset]) {
+ cn = null;
+ break;
+ }
+ }
+ if (cn != null) {
+ lastCacheNode = cn;
+ return cn.ab;
+ }
+ }
+ }
+ byte[] nab = new byte[len];
+ System.arraycopy(ab, offset, nab, 0, len);
+ return nab;
+ }
+
+
+ public final void evaluate(boolean inverseDirection, byte[] ab) {
+ requests++;
+ lookupDataValid = false; // this is an assertion for a nasty pifall
+
+ if (cache == null) {
+ decode(lookupData, inverseDirection, ab);
+ if (currentVars == null || currentVars.length != nBuildInVars) {
+ currentVars = new float[nBuildInVars];
+ }
+ evaluateInto(currentVars, 0);
+ currentVarOffset = 0;
+ return;
+ }
+
+ CacheNode cn;
+ if (lastCacheNode.ab == ab) {
+ cn = lastCacheNode;
+ } else {
+ probeCacheNode.ab = ab;
+ probeCacheNode.hash = Crc32.crc(ab, 0, ab.length);
+ cn = (CacheNode) cache.get(probeCacheNode);
+ }
+
+ if (cn == null) {
+ cachemisses++;
+
+ cn = (CacheNode) cache.removeLru();
+ if (cn == null) {
+ cn = new CacheNode();
+ }
+ cn.hash = probeCacheNode.hash;
+ cn.ab = ab;
+ cache.put(cn);
+
+ if (probeVarSet.vars == null) {
+ probeVarSet.vars = new float[2 * nBuildInVars];
+ }
+
+ // forward direction
+ decode(lookupData, false, ab);
+ evaluateInto(probeVarSet.vars, 0);
+
+ // inverse direction
+ lookupData[0] = 2; // inverse shortcut: reuse decoding
+ evaluateInto(probeVarSet.vars, nBuildInVars);
+
+ probeVarSet.hash = Arrays.hashCode(probeVarSet.vars);
+
+ // unify the result variable set
+ VarWrapper vw = (VarWrapper) resultVarCache.get(probeVarSet);
+ if (vw == null) {
+ vw = (VarWrapper) resultVarCache.removeLru();
+ if (vw == null) {
+ vw = new VarWrapper();
+ }
+ vw.hash = probeVarSet.hash;
+ vw.vars = probeVarSet.vars;
+ probeVarSet.vars = null;
+ resultVarCache.put(vw);
+ }
+ cn.vars = vw.vars;
+ } else {
+ if (ab == cn.ab) requests2++;
+
+ cache.touch(cn);
+ }
+
+ currentVars = cn.vars;
+ currentVarOffset = inverseDirection ? nBuildInVars : 0;
+ }
+
+ private void evaluateInto(float[] vars, int offset) {
+ evaluate();
+ for (int vi = 0; vi < nBuildInVars; vi++) {
+ int idx = buildInVariableIdx[vi];
+ vars[vi + offset] = idx == -1 ? 0.f : variableData[idx];
+ }
+ }
+
+
+ public void dumpStatistics() {
+ TreeMap counts = new TreeMap();
+ // first count
+ for (String name : lookupNumbers.keySet()) {
+ int cnt = 0;
+ int inum = lookupNumbers.get(name).intValue();
+ int[] histo = lookupHistograms.get(inum);
+// if ( histo.length == 500 ) continue;
+ for (int i = 2; i < histo.length; i++) {
+ cnt += histo[i];
+ }
+ counts.put("" + (1000000000 + cnt) + "_" + name, name);
+ }
+
+ while (counts.size() > 0) {
+ String key = counts.lastEntry().getKey();
+ String name = counts.get(key);
+ counts.remove(key);
+ int inum = lookupNumbers.get(name).intValue();
+ BExpressionLookupValue[] values = lookupValues.get(inum);
+ int[] histo = lookupHistograms.get(inum);
+ if (values.length == 1000) continue;
+ String[] svalues = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ String scnt = "0000000000" + histo[i];
+ scnt = scnt.substring(scnt.length() - 10);
+ svalues[i] = scnt + " " + values[i].toString();
+ }
+ Arrays.sort(svalues);
+ for (int i = svalues.length - 1; i >= 0; i--) {
+ System.out.println(name + ";" + svalues[i]);
+ }
+ }
+ }
+
+ /**
+ * @return a new lookupData array, or null if no metadata defined
+ */
+ public int[] createNewLookupData() {
+ if (lookupDataFrozen) {
+ return new int[lookupValues.size()];
+ }
+ return null;
+ }
+
+ /**
+ * generate random values for regression testing
+ */
+ public int[] generateRandomValues(Random rnd) {
+ int[] data = createNewLookupData();
+ data[0] = 2 * rnd.nextInt(2); // reverse-direction = 0 or 2
+ for (int inum = 1; inum < data.length; inum++) {
+ int nvalues = lookupValues.get(inum).length;
+ data[inum] = 0;
+ if (inum > 1 && rnd.nextInt(10) > 0) continue; // tags other than highway only 10%
+ data[inum] = rnd.nextInt(nvalues);
+ }
+ lookupDataValid = true;
+ return data;
+ }
+
+ public void assertAllVariablesEqual(BExpressionContext other) {
+ int nv = variableData.length;
+ int nv2 = other.variableData.length;
+ if (nv != nv2) throw new RuntimeException("mismatch in variable-count: " + nv + "<->" + nv2);
+ for (int i = 0; i < nv; i++) {
+ if (variableData[i] != other.variableData[i]) {
+ throw new RuntimeException("mismatch in variable " + variableName(i) + " " + variableData[i] + "<->" + other.variableData[i]
+ + "\ntags = " + getKeyValueDescription(false, encode()));
+ }
+ }
+ }
+
+ public String variableName(int idx) {
+ for (Map.Entry e : variableNumbers.entrySet()) {
+ if (e.getValue().intValue() == idx) {
+ return e.getKey();
+ }
+ }
+ throw new RuntimeException("no variable for index" + idx);
+ }
+
+ /**
+ * add a new lookup-value for the given name to the given lookupData array.
+ * If no array is given (null value passed), the value is added to
+ * the context-binded array. In that case, unknown names and values are
+ * created dynamically.
+ *
+ * @return a newly created value element, if any, to optionally add aliases
+ */
+ public BExpressionLookupValue addLookupValue(String name, String value, int[] lookupData2) {
+ BExpressionLookupValue newValue = null;
+ Integer num = lookupNumbers.get(name);
+ if (num == null) {
+ if (lookupData2 != null) {
+ // do not create unknown name for external data array
+ return newValue;
+ }
+
+ // unknown name, create
+ num = Integer.valueOf(lookupValues.size());
+ lookupNumbers.put(name, num);
+ lookupNames.add(name);
+ lookupValues.add(new BExpressionLookupValue[]{new BExpressionLookupValue("")
+ , new BExpressionLookupValue("unknown")});
+ lookupHistograms.add(new int[2]);
+ int[] ndata = new int[lookupData.length + 1];
+ System.arraycopy(lookupData, 0, ndata, 0, lookupData.length);
+ lookupData = ndata;
+ }
+
+ // look for that value
+ int inum = num.intValue();
+ BExpressionLookupValue[] values = lookupValues.get(inum);
+ int[] histo = lookupHistograms.get(inum);
+ int i = 0;
+ boolean bFoundAsterix = false;
+ for (; i < values.length; i++) {
+ BExpressionLookupValue v = values[i];
+ if (v.equals("*")) bFoundAsterix = true;
+ if (v.matches(value)) break;
+ }
+ if (i == values.length) {
+ if (lookupData2 != null) {
+ // do not create unknown value for external data array,
+ // record as 'unknown' instead
+ lookupData2[inum] = 1; // 1 == unknown
+ if (bFoundAsterix) {
+ // found value for lookup *
+ //System.out.println( "add unknown " + name + " " + value );
+ String org = value;
+ try {
+ // remove some unused characters
+ value = value.replace(",", ".");
+ value = value.replace(">", "");
+ value = value.replace("_", "");
+ if (value.indexOf("-") == 0) value = value.substring(1);
+ if (value.indexOf("~") == 0) value = value.substring(1);
+ if (value.contains("-")) { // replace eg. 1.4-1.6 m
+ String tmp = value.substring(value.indexOf("-") + 1).replaceAll("[0-9.,-]", "");
+ value = value.substring(0, value.indexOf("-")) + tmp;
+ }
+ // do some value conversion
+ if (value.toLowerCase().contains("ft")) {
+ float foot = 0f;
+ int inch = 0;
+ String[] sa = value.toLowerCase().trim().split("ft");
+ if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim());
+ if (sa.length == 2) {
+ value = sa[1];
+ if (value.indexOf("in") > 0) value = value.substring(0, value.indexOf("in"));
+ inch = Integer.parseInt(value.trim());
+ foot += inch / 12f;
+ }
+ value = String.format(Locale.US, "%3.1f", foot * 0.3048f);
+ }
+ if (value.toLowerCase().contains("'")) {
+ float foot = 0f;
+ int inch = 0;
+ String[] sa = value.toLowerCase().trim().split("'");
+ if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim());
+ if (sa.length == 2) {
+ value = sa[1];
+ if (value.indexOf("''") > 0) value = value.substring(0, value.indexOf("''"));
+ if (value.indexOf("\"") > 0) value = value.substring(0, value.indexOf("\""));
+ inch = Integer.parseInt(value.trim());
+ foot += inch / 12f;
+ }
+ value = String.format(Locale.US, "%3.1f", foot * 0.3048f);
+ } else if (value.contains("in") || value.contains("\"")) {
+ float inch = 0f;
+ if (value.indexOf("in") > 0) value = value.substring(0, value.indexOf("in"));
+ if (value.indexOf("\"") > 0) value = value.substring(0, value.indexOf("\""));
+ inch = Float.parseFloat(value.trim());
+ value = String.format(Locale.US, "%3.1f", inch * 0.0254f);
+ } else if (value.toLowerCase().contains("feet") || value.toLowerCase().contains("foot")) {
+ float feet = 0f;
+ String s = value.substring(0, value.toLowerCase().indexOf("f"));
+ feet = Float.parseFloat(s.trim());
+ value = String.format(Locale.US, "%3.1f", feet * 0.3048f);
+ } else if (value.toLowerCase().contains("fathom") || value.toLowerCase().contains("fm")) {
+ float fathom = 0f;
+ String s = value.substring(0, value.toLowerCase().indexOf("f"));
+ fathom = Float.parseFloat(s.trim());
+ value = String.format(Locale.US, "%3.1f", fathom * 1.8288f);
+ } else if (value.contains("cm")) {
+ String[] sa = value.trim().split("cm");
+ if (sa.length == 1) value = sa[0].trim();
+ float cm = Float.parseFloat(value.trim());
+ value = String.format(Locale.US, "%3.1f", cm * 100f);
+ } else if (value.toLowerCase().contains("meter")) {
+ String s = value.substring(0, value.toLowerCase().indexOf("m"));
+ value = s.trim();
+ } else if (value.toLowerCase().contains("mph")) {
+ value = value.replace("_", "");
+ String[] sa = value.trim().toLowerCase().split("mph");
+ if (sa.length >= 1) value = sa[0].trim();
+ float mph = Float.parseFloat(value.trim());
+ value = String.format(Locale.US, "%3.1f", mph * 1.609344f);
+ } else if (value.toLowerCase().contains("knot")) {
+ String[] sa = value.trim().toLowerCase().split("knot");
+ if (sa.length >= 1) value = sa[0].trim();
+ float nm = Float.parseFloat(value.trim());
+ value = String.format(Locale.US, "%3.1f", nm * 1.852f);
+ } else if (value.contains("kmh") || value.contains("km/h") || value.contains("kph")) {
+ String[] sa = value.trim().split("k");
+ if (sa.length == 1) value = sa[0].trim();
+ } else if (value.contains("m")) {
+ String s = value.substring(0, value.toLowerCase().indexOf("m"));
+ value = s.trim();
+ } else if (value.contains("(")) {
+ String s = value.substring(0, value.toLowerCase().indexOf("("));
+ value = s.trim();
+ }
+ // found negative maxdraft values
+ // no negative values
+ // values are float with 2 decimals
+ lookupData2[inum] = 1000 + (int) (Math.abs(Float.parseFloat(value)) * 100f);
+ } catch (Exception e) {
+ // ignore errors
+ System.err.println("error for " + name + " " + org + " trans " + value + " " + e.getMessage());
+ lookupData2[inum] = 0;
+ }
+ }
+ return newValue;
+ }
+
+ if (i == 499) {
+ // System.out.println( "value limit reached for: " + name );
+ }
+ if (i == 500) {
+ return newValue;
+ }
+ // unknown value, create
+ BExpressionLookupValue[] nvalues = new BExpressionLookupValue[values.length + 1];
+ int[] nhisto = new int[values.length + 1];
+ System.arraycopy(values, 0, nvalues, 0, values.length);
+ System.arraycopy(histo, 0, nhisto, 0, histo.length);
+ values = nvalues;
+ histo = nhisto;
+ newValue = new BExpressionLookupValue(value);
+ values[i] = newValue;
+ lookupHistograms.set(inum, histo);
+ lookupValues.set(inum, values);
+ }
+
+ histo[i]++;
+
+ // finally remember the actual data
+ if (lookupData2 != null) lookupData2[inum] = i;
+ else lookupData[inum] = i;
+ return newValue;
+ }
+
+ /**
+ * add a value-index to to internal array
+ * value-index means 0=unknown, 1=other, 2=value-x, ...
+ */
+ public void addLookupValue(String name, int valueIndex) {
+ Integer num = lookupNumbers.get(name);
+ if (num == null) {
+ return;
+ }
+
+ // look for that value
+ int inum = num.intValue();
+ int nvalues = lookupValues.get(inum).length;
+ if (valueIndex < 0 || valueIndex >= nvalues)
+ throw new IllegalArgumentException("value index out of range for name " + name + ": " + valueIndex);
+ lookupData[inum] = valueIndex;
+ }
+
+
+ /**
+ * special hack for yes/proposed relations:
+ * add a lookup value if not yet a smaller, > 1 value was added
+ * add a 2=yes if the provided value is out of range
+ * value-index means here 0=unknown, 1=other, 2=yes, 3=proposed
+ */
+ public void addSmallestLookupValue(String name, int valueIndex) {
+ Integer num = lookupNumbers.get(name);
+ if (num == null) {
+ return;
+ }
+
+ // look for that value
+ int inum = num.intValue();
+ int nvalues = lookupValues.get(inum).length;
+ int oldValueIndex = lookupData[inum];
+ if (oldValueIndex > 1 && oldValueIndex < valueIndex) {
+ return;
+ }
+ if (valueIndex >= nvalues) {
+ valueIndex = nvalues - 1;
+ }
+ if (valueIndex < 0)
+ throw new IllegalArgumentException("value index out of range for name " + name + ": " + valueIndex);
+ lookupData[inum] = valueIndex;
+ }
+
+ public boolean getBooleanLookupValue(String name) {
+ Integer num = lookupNumbers.get(name);
+ return num != null && lookupData[num.intValue()] == 2;
+ }
+
+ public int getOutputVariableIndex(String name, boolean mustExist) {
+ int idx = getVariableIdx(name, false);
+ if (idx < 0) {
+ if (mustExist) {
+ throw new IllegalArgumentException("unknown variable: " + name);
+ }
+ } else if (idx < minWriteIdx) {
+ throw new IllegalArgumentException("bad access to global variable: " + name);
+ }
+ for (int i = 0; i < nBuildInVars; i++) {
+ if (buildInVariableIdx[i] == idx) {
+ return i;
+ }
+ }
+ int[] extended = new int[nBuildInVars + 1];
+ System.arraycopy(buildInVariableIdx, 0, extended, 0, nBuildInVars);
+ extended[nBuildInVars] = idx;
+ buildInVariableIdx = extended;
+ return nBuildInVars++;
+ }
+
+ public void setForeignContext(BExpressionContext foreignContext) {
+ this.foreignContext = foreignContext;
+ }
+
+ public float getForeignVariableValue(int foreignIndex) {
+ return foreignContext.getBuildInVariable(foreignIndex);
+ }
+
+ public int getForeignVariableIdx(String context, String name) {
+ if (foreignContext == null || !context.equals(foreignContext.context)) {
+ throw new IllegalArgumentException("unknown foreign context: " + context);
+ }
+ return foreignContext.getOutputVariableIndex(name, true);
+ }
+
+ public void parseFile(File file, String readOnlyContext) {
+ if (!file.exists()) {
+ throw new IllegalArgumentException("profile " + file + " does not exist");
+ }
+ try {
+ if (readOnlyContext != null) {
+ linenr = 1;
+ String realContext = context;
+ context = readOnlyContext;
+ expressionList = _parseFile(file);
+ variableData = new float[variableNumbers.size()];
+ evaluate(lookupData); // lookupData is dummy here - evaluate just to create the variables
+ context = realContext;
+ }
+ linenr = 1;
+ minWriteIdx = variableData == null ? 0 : variableData.length;
+
+ expressionList = _parseFile(file);
+
+ // determine the build-in variable indices
+ String[] varNames = getBuildInVariableNames();
+ nBuildInVars = varNames.length;
+ buildInVariableIdx = new int[nBuildInVars];
+ for (int vi = 0; vi < varNames.length; vi++) {
+ buildInVariableIdx[vi] = getVariableIdx(varNames[vi], false);
+ }
+
+ float[] readOnlyData = variableData;
+ variableData = new float[variableNumbers.size()];
+ for (int i = 0; i < minWriteIdx; i++) {
+ variableData[i] = readOnlyData[i];
+ }
+ } catch (Exception e) {
+ if (e instanceof IllegalArgumentException) {
+ throw new IllegalArgumentException("ParseException " + file + " at line " + linenr + ": " + e.getMessage());
+ }
+ throw new RuntimeException(e);
+ }
+ if (expressionList.size() == 0) {
+ throw new IllegalArgumentException(file.getAbsolutePath()
+ + " does not contain expressions for context " + context + " (old version?)");
+ }
+ }
+
+ private List _parseFile(File file) throws Exception {
+ _br = new BufferedReader(new FileReader(file));
+ _readerDone = false;
+ List result = new ArrayList();
+ for (; ; ) {
+ BExpression exp = BExpression.parse(this, 0);
+ if (exp == null) break;
+ result.add(exp);
+ }
+ _br.close();
+ _br = null;
+ return result;
+ }
+
+ public void setVariableValue(String name, float value, boolean create) {
+ Integer num = variableNumbers.get(name);
+ if (num != null) {
+ variableData[num.intValue()] = value;
+ }
+ }
+
+ public float getVariableValue(String name, float defaultValue) {
+ Integer num = variableNumbers.get(name);
+ return num == null ? defaultValue : getVariableValue(num.intValue());
+ }
+
+ float getVariableValue(int variableIdx) {
+ return variableData[variableIdx];
+ }
+
+ int getVariableIdx(String name, boolean create) {
+ Integer num = variableNumbers.get(name);
+ if (num == null) {
+ if (create) {
+ num = new Integer(variableNumbers.size());
+ variableNumbers.put(name, num);
+ } else {
+ return -1;
+ }
+ }
+ return num.intValue();
+ }
+
+ int getMinWriteIdx() {
+ return minWriteIdx;
+ }
+
+ float getLookupMatch(int nameIdx, int[] valueIdxArray) {
+ for (int i = 0; i < valueIdxArray.length; i++) {
+ if (lookupData[nameIdx] == valueIdxArray[i]) {
+ return 1.0f;
+ }
+ }
+ return 0.0f;
+ }
+
+ public int getLookupNameIdx(String name) {
+ Integer num = lookupNumbers.get(name);
+ return num == null ? -1 : num.intValue();
+ }
+
+ public final void markLookupIdxUsed(int idx) {
+ lookupIdxUsed[idx] = true;
+ }
+
+ public final boolean isLookupIdxUsed(int idx) {
+ return idx < lookupIdxUsed.length ? lookupIdxUsed[idx] : false;
+ }
+
+ public final void setAllTagsUsed() {
+ for (int i = 0; i < lookupIdxUsed.length; i++) {
+ lookupIdxUsed[i] = true;
+ }
+ }
+
+ int getLookupValueIdx(int nameIdx, String value) {
+ BExpressionLookupValue[] values = lookupValues.get(nameIdx);
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(value)) return i;
+ }
+ return -1;
+ }
+
+
+ String parseToken() throws Exception {
+ for (; ; ) {
+ String token = _parseToken();
+ if (token == null) return null;
+ if (token.startsWith(CONTEXT_TAG)) {
+ _inOurContext = token.substring(CONTEXT_TAG.length()).equals(context);
+ } else if (token.startsWith(MODEL_TAG)) {
+ _modelClass = token.substring(MODEL_TAG.length()).trim();
+ } else if (_inOurContext) {
+ return token;
+ }
+ }
+ }
+
+
+ private String _parseToken() throws Exception {
+ StringBuilder sb = new StringBuilder(32);
+ boolean inComment = false;
+ for (; ; ) {
+ int ic = _readerDone ? -1 : _br.read();
+ if (ic < 0) {
+ if (sb.length() == 0) return null;
+ _readerDone = true;
+ return sb.toString();
+ }
+ char c = (char) ic;
+ if (c == '\n') linenr++;
+
+ if (inComment) {
+ if (c == '\r' || c == '\n') inComment = false;
+ continue;
+ }
+ if (Character.isWhitespace(c)) {
+ if (sb.length() > 0) return sb.toString();
+ else continue;
+ }
+ if (c == '#' && sb.length() == 0) inComment = true;
+ else sb.append(c);
+ }
+ }
+
+ float assign(int variableIdx, float value) {
+ variableData[variableIdx] = value;
+ return value;
+ }
+
+}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextNode.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextNode.java
index d6e90dc..9120610 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextNode.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextNode.java
@@ -7,32 +7,29 @@
package btools.expressions;
-
-public final class BExpressionContextNode extends BExpressionContext
-{
+public final class BExpressionContextNode extends BExpressionContext {
private static String[] buildInVariables =
- { "initialcost" };
-
- protected String[] getBuildInVariableNames()
- {
+ {"initialcost"};
+
+ protected String[] getBuildInVariableNames() {
return buildInVariables;
}
- public float getInitialcost() { return getBuildInVariable(0); }
+ public float getInitialcost() {
+ return getBuildInVariable(0);
+ }
- public BExpressionContextNode( BExpressionMetaData meta )
- {
- super( "node", meta );
+ public BExpressionContextNode(BExpressionMetaData meta) {
+ super("node", meta);
}
/**
* Create an Expression-Context for way context
*
- * @param hashSize size of hashmap for result caching
+ * @param hashSize size of hashmap for result caching
*/
- public BExpressionContextNode( int hashSize, BExpressionMetaData meta )
- {
- super( "node", hashSize, meta );
+ public BExpressionContextNode(int hashSize, BExpressionMetaData meta) {
+ super("node", hashSize, meta);
}
}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java
index a879cf4..9d286f5 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java
@@ -8,66 +8,93 @@ package btools.expressions;
import btools.codec.TagValueValidator;
-public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
-{
+public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator {
private boolean decodeForbidden = true;
private static String[] buildInVariables =
- { "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
-
- protected String[] getBuildInVariableNames()
- {
+ {"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed"};
+
+ protected String[] getBuildInVariableNames() {
return buildInVariables;
}
- public float getCostfactor() { return getBuildInVariable(0); }
- public float getTurncost() { return getBuildInVariable(1); }
- public float getUphillCostfactor() { return getBuildInVariable(2); }
- public float getDownhillCostfactor() { return getBuildInVariable(3); }
- public float getInitialcost() { return getBuildInVariable(4); }
- public float getNodeAccessGranted() { return getBuildInVariable(5); }
- public float getInitialClassifier() { return getBuildInVariable(6); }
- public float getTrafficSourceDensity() { return getBuildInVariable(7); }
- public float getIsTrafficBackbone() { return getBuildInVariable(8); }
- public float getPriorityClassifier() { return getBuildInVariable(9); }
- public float getClassifierMask() { return getBuildInVariable(10); }
- public float getMaxspeed() { return getBuildInVariable(11); }
+ public float getCostfactor() {
+ return getBuildInVariable(0);
+ }
- public BExpressionContextWay( BExpressionMetaData meta )
- {
- super( "way", meta );
+ public float getTurncost() {
+ return getBuildInVariable(1);
+ }
+
+ public float getUphillCostfactor() {
+ return getBuildInVariable(2);
+ }
+
+ public float getDownhillCostfactor() {
+ return getBuildInVariable(3);
+ }
+
+ public float getInitialcost() {
+ return getBuildInVariable(4);
+ }
+
+ public float getNodeAccessGranted() {
+ return getBuildInVariable(5);
+ }
+
+ public float getInitialClassifier() {
+ return getBuildInVariable(6);
+ }
+
+ public float getTrafficSourceDensity() {
+ return getBuildInVariable(7);
+ }
+
+ public float getIsTrafficBackbone() {
+ return getBuildInVariable(8);
+ }
+
+ public float getPriorityClassifier() {
+ return getBuildInVariable(9);
+ }
+
+ public float getClassifierMask() {
+ return getBuildInVariable(10);
+ }
+
+ public float getMaxspeed() {
+ return getBuildInVariable(11);
+ }
+
+ public BExpressionContextWay(BExpressionMetaData meta) {
+ super("way", meta);
}
/**
* Create an Expression-Context for way context
*
- * @param hashSize size of hashmap for result caching
+ * @param hashSize size of hashmap for result caching
*/
- public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
- {
- super( "way", hashSize, meta );
+ public BExpressionContextWay(int hashSize, BExpressionMetaData meta) {
+ super("way", hashSize, meta);
}
@Override
- public int accessType( byte[] description )
- {
- evaluate( false, description );
+ public int accessType(byte[] description) {
+ evaluate(false, description);
float minCostFactor = getCostfactor();
- if ( minCostFactor >= 9999.f )
- {
+ if (minCostFactor >= 9999.f) {
setInverseVars();
float reverseCostFactor = getCostfactor();
- if ( reverseCostFactor < minCostFactor )
- {
+ if (reverseCostFactor < minCostFactor) {
minCostFactor = reverseCostFactor;
}
}
return minCostFactor < 9999.f ? 2 : decodeForbidden ? (minCostFactor < 10000.f ? 1 : 0) : 0;
}
-
+
@Override
- public void setDecodeForbidden( boolean decodeForbidden )
- {
- this.decodeForbidden= decodeForbidden;
+ public void setDecodeForbidden(boolean decodeForbidden) {
+ this.decodeForbidden = decodeForbidden;
}
}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionLookupValue.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionLookupValue.java
index 8043d3c..9954c69 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionLookupValue.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionLookupValue.java
@@ -1,66 +1,56 @@
-/**
- * A lookup value with optional aliases
- *
- * toString just gives the primary value,
- * equals just compares against primary value
- * matches() also compares aliases
- *
- * @author ab
- */
-package btools.expressions;
-
-import java.util.ArrayList;
-
-final class BExpressionLookupValue
-{
- String value;
- ArrayList aliases;
-
- @Override
- public String toString()
- {
- return value;
- }
-
- public BExpressionLookupValue( String value )
- {
- this.value = value;
- }
-
- public void addAlias( String alias )
- {
- if ( aliases == null ) aliases = new ArrayList();
- aliases.add( alias );
- }
-
- @Override
- public boolean equals( Object o )
- {
- if ( o instanceof String )
- {
- String v = (String)o;
- return value.equals( v );
- }
- if ( o instanceof BExpressionLookupValue )
- {
- BExpressionLookupValue v = (BExpressionLookupValue)o;
-
- return value.equals( v.value );
- }
- return false;
- }
-
- public boolean matches( String s )
- {
- if ( value.equals( s ) ) return true;
- if ( aliases != null )
- {
- for( String alias : aliases )
- {
- if ( alias.equals( s ) ) return true;
- }
- }
- return false;
- }
-
-}
+/**
+ * A lookup value with optional aliases
+ *
+ * toString just gives the primary value,
+ * equals just compares against primary value
+ * matches() also compares aliases
+ *
+ * @author ab
+ */
+package btools.expressions;
+
+import java.util.ArrayList;
+
+final class BExpressionLookupValue {
+ String value;
+ ArrayList aliases;
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ public BExpressionLookupValue(String value) {
+ this.value = value;
+ }
+
+ public void addAlias(String alias) {
+ if (aliases == null) aliases = new ArrayList();
+ aliases.add(alias);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof String) {
+ String v = (String) o;
+ return value.equals(v);
+ }
+ if (o instanceof BExpressionLookupValue) {
+ BExpressionLookupValue v = (BExpressionLookupValue) o;
+
+ return value.equals(v.value);
+ }
+ return false;
+ }
+
+ public boolean matches(String s) {
+ if (value.equals(s)) return true;
+ if (aliases != null) {
+ for (String alias : aliases) {
+ if (alias.equals(s)) return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionMetaData.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionMetaData.java
index 6d5a901..40c8749 100644
--- a/brouter-expressions/src/main/java/btools/expressions/BExpressionMetaData.java
+++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionMetaData.java
@@ -21,9 +21,8 @@ import btools.util.BitCoderContext;
import btools.util.Crc32;
-public final class BExpressionMetaData
-{
- private static final String CONTEXT_TAG = "---context:";
+public final class BExpressionMetaData {
+ private static final String CONTEXT_TAG = "---context:";
private static final String VERSION_TAG = "---lookupversion:";
private static final String MINOR_VERSION_TAG = "---minorversion:";
private static final String VARLENGTH_TAG = "---readvarlength";
@@ -31,59 +30,49 @@ public final class BExpressionMetaData
public short lookupVersion = -1;
public short lookupMinorVersion = -1;
- private HashMap listeners = new HashMap();
-
- public void registerListener( String context, BExpressionContext ctx )
- {
- listeners.put( context, ctx );
+ private HashMap listeners = new HashMap();
+
+ public void registerListener(String context, BExpressionContext ctx) {
+ listeners.put(context, ctx);
}
-
- public void readMetaData( File lookupsFile )
- {
- try
- {
- BufferedReader br = new BufferedReader( new FileReader( lookupsFile ) );
-
- BExpressionContext ctx = null;
-
- for(;;)
- {
- String line = br.readLine();
- if ( line == null ) break;
- line = line.trim();
- if ( line.length() == 0 || line.startsWith( "#" ) ) continue;
- if ( line.startsWith( CONTEXT_TAG ) )
- {
- ctx = listeners.get( line.substring( CONTEXT_TAG.length() ) );
- continue;
+
+ public void readMetaData(File lookupsFile) {
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(lookupsFile));
+
+ BExpressionContext ctx = null;
+
+ for (; ; ) {
+ String line = br.readLine();
+ if (line == null) break;
+ line = line.trim();
+ if (line.length() == 0 || line.startsWith("#")) continue;
+ if (line.startsWith(CONTEXT_TAG)) {
+ ctx = listeners.get(line.substring(CONTEXT_TAG.length()));
+ continue;
+ }
+ if (line.startsWith(VERSION_TAG)) {
+ lookupVersion = Short.parseShort(line.substring(VERSION_TAG.length()));
+ continue;
+ }
+ if (line.startsWith(MINOR_VERSION_TAG)) {
+ lookupMinorVersion = Short.parseShort(line.substring(MINOR_VERSION_TAG.length()));
+ continue;
+ }
+ if (line.startsWith(VARLENGTH_TAG)) // tag removed...
+ {
+ continue;
+ }
+ if (ctx != null) ctx.parseMetaLine(line);
}
- if ( line.startsWith( VERSION_TAG ) )
- {
- lookupVersion = Short.parseShort( line.substring( VERSION_TAG.length() ) );
- continue;
+ br.close();
+
+ for (BExpressionContext c : listeners.values()) {
+ c.finishMetaParsing();
}
- if ( line.startsWith( MINOR_VERSION_TAG ) )
- {
- lookupMinorVersion = Short.parseShort( line.substring( MINOR_VERSION_TAG.length() ) );
- continue;
- }
- if ( line.startsWith( VARLENGTH_TAG ) ) // tag removed...
- {
- continue;
- }
- if ( ctx != null ) ctx.parseMetaLine( line );
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
- br.close();
-
- for( BExpressionContext c : listeners.values() )
- {
- c.finishMetaParsing();
- }
-
- }
- catch( Exception e )
- {
- throw new RuntimeException( e );
- }
}
}
diff --git a/brouter-expressions/src/main/java/btools/expressions/CacheNode.java b/brouter-expressions/src/main/java/btools/expressions/CacheNode.java
index dd95180..f25af31 100644
--- a/brouter-expressions/src/main/java/btools/expressions/CacheNode.java
+++ b/brouter-expressions/src/main/java/btools/expressions/CacheNode.java
@@ -4,29 +4,24 @@ import java.util.Arrays;
import btools.util.LruMapNode;
-public final class CacheNode extends LruMapNode
-{
+public final class CacheNode extends LruMapNode {
byte[] ab;
float[] vars;
@Override
- public int hashCode()
- {
+ public int hashCode() {
return hash;
}
@Override
- public boolean equals( Object o )
- {
+ public boolean equals(Object o) {
CacheNode n = (CacheNode) o;
- if ( hash != n.hash )
- {
+ if (hash != n.hash) {
return false;
}
- if ( ab == null )
- {
+ if (ab == null) {
return true; // hack: null = crc match only
}
- return Arrays.equals( ab, n.ab );
+ return Arrays.equals(ab, n.ab);
}
}
diff --git a/brouter-expressions/src/main/java/btools/expressions/ProfileComparator.java b/brouter-expressions/src/main/java/btools/expressions/ProfileComparator.java
index 3c5ee7c..b6e1dac 100644
--- a/brouter-expressions/src/main/java/btools/expressions/ProfileComparator.java
+++ b/brouter-expressions/src/main/java/btools/expressions/ProfileComparator.java
@@ -3,45 +3,40 @@ package btools.expressions;
import java.io.File;
import java.util.Random;
-public final class ProfileComparator
-{
- public static void main( String[] args )
- {
- if ( args.length != 4 )
- {
- System.out.println( "usage: java ProfileComparator " );
+public final class ProfileComparator {
+ public static void main(String[] args) {
+ if (args.length != 4) {
+ System.out.println("usage: java ProfileComparator ");
return;
}
- File lookupFile = new File( args[0] );
- File profile1File = new File( args[1] );
- File profile2File = new File( args[2] );
- int nsamples = Integer.parseInt( args[3] );
- testContext( lookupFile, profile1File, profile2File, nsamples, false );
- testContext( lookupFile, profile1File, profile2File, nsamples, true );
+ File lookupFile = new File(args[0]);
+ File profile1File = new File(args[1]);
+ File profile2File = new File(args[2]);
+ int nsamples = Integer.parseInt(args[3]);
+ testContext(lookupFile, profile1File, profile2File, nsamples, false);
+ testContext(lookupFile, profile1File, profile2File, nsamples, true);
}
-
- private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext )
- {
+
+ private static void testContext(File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext) {
// read lookup.dat + profiles
BExpressionMetaData meta1 = new BExpressionMetaData();
BExpressionMetaData meta2 = new BExpressionMetaData();
- BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
- BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 );
- meta1.readMetaData( lookupFile );
- meta2.readMetaData( lookupFile );
- expctx1.parseFile( profile1File, "global" );
- expctx2.parseFile( profile2File, "global" );
+ BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode(meta1) : new BExpressionContextWay(meta1);
+ BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode(meta2) : new BExpressionContextWay(meta2);
+ meta1.readMetaData(lookupFile);
+ meta2.readMetaData(lookupFile);
+ expctx1.parseFile(profile1File, "global");
+ expctx2.parseFile(profile2File, "global");
Random rnd = new Random();
- for( int i=0; i=): " + arg );
- String key = arg.substring( 0, idx );
- String value = arg.substring( idx+1 );
-
- expctxWay.addLookupValue( key, value, lookupData );
+ for (String arg : tags) {
+ int idx = arg.indexOf('=');
+ if (idx < 0)
+ throw new IllegalArgumentException("bad argument (should be =): " + arg);
+ String key = arg.substring(0, idx);
+ String value = arg.substring(idx + 1);
+
+ expctxWay.addLookupValue(key, value, lookupData);
}
byte[] description = expctxWay.encode(lookupData);
// calculate the cost factor from that description
- expctxWay.evaluate( true, description ); // true = "reversedirection=yes" (not encoded in description anymore)
+ expctxWay.evaluate(true, description); // true = "reversedirection=yes" (not encoded in description anymore)
- System.out.println( "description: " + expctxWay.getKeyValueDescription(true, description) );
+ System.out.println("description: " + expctxWay.getKeyValueDescription(true, description));
float costfactor = expctxWay.getCostfactor();
- Assert.assertTrue( "costfactor mismatch", Math.abs( costfactor - 5.15 ) < 0.00001 );
+ Assert.assertTrue("costfactor mismatch", Math.abs(costfactor - 5.15) < 0.00001);
}
}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java
index 0866c7d..65f18aa 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java
@@ -11,8 +11,7 @@ import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-public class ConvertLidarTile
-{
+public class ConvertLidarTile {
public static int NROWS;
public static int NCOLS;
@@ -21,78 +20,63 @@ public class ConvertLidarTile
static short[] imagePixels;
- private static void readHgtZip( String filename, int rowOffset, int colOffset ) throws Exception
- {
- ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( filename ) ) );
- try
- {
- for ( ;; )
- {
+ private static void readHgtZip(String filename, int rowOffset, int colOffset) throws Exception {
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename)));
+ try {
+ for (; ; ) {
ZipEntry ze = zis.getNextEntry();
- if ( ze.getName().endsWith( ".hgt" ) )
- {
- readHgtFromStream( zis, rowOffset, colOffset );
+ if (ze.getName().endsWith(".hgt")) {
+ readHgtFromStream(zis, rowOffset, colOffset);
return;
}
}
- }
- finally
- {
+ } finally {
zis.close();
}
}
- private static void readHgtFromStream( InputStream is, int rowOffset, int colOffset )
- throws Exception
- {
- DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) );
- for ( int ir = 0; ir < 1201; ir++ )
- {
+ private static void readHgtFromStream(InputStream is, int rowOffset, int colOffset)
+ throws Exception {
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
+ for (int ir = 0; ir < 1201; ir++) {
int row = rowOffset + ir;
- for ( int ic = 0; ic < 1201; ic++ )
- {
+ for (int ic = 0; ic < 1201; ic++) {
int col = colOffset + ic;
int i1 = dis.read(); // msb first!
int i0 = dis.read();
- if ( i0 == -1 || i1 == -1 )
- throw new RuntimeException( "unexcepted end of file reading hgt entry!" );
+ if (i0 == -1 || i1 == -1)
+ throw new RuntimeException("unexcepted end of file reading hgt entry!");
- short val = (short) ( ( i1 << 8 ) | i0 );
+ short val = (short) ((i1 << 8) | i0);
- if ( val == NODATA2 )
- {
+ if (val == NODATA2) {
val = NODATA;
}
- setPixel( row, col, val );
+ setPixel(row, col, val);
}
}
}
- private static void setPixel( int row, int col, short val )
- {
- if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
- {
+ private static void setPixel(int row, int col, short val) {
+ if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
imagePixels[row * NCOLS + col] = val;
}
}
- private static short getPixel( int row, int col )
- {
- if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
- {
+ private static short getPixel(int row, int col) {
+ if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
return imagePixels[row * NCOLS + col];
}
return NODATA;
}
- public static void doConvert( String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile ) throws Exception
- {
+ public static void doConvert(String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile) throws Exception {
int extraBorder = 0;
NROWS = 5 * 1200 + 1 + 2 * extraBorder;
@@ -101,34 +85,27 @@ public class ConvertLidarTile
imagePixels = new short[NROWS * NCOLS]; // 650 MB !
// prefill as NODATA
- for ( int row = 0; row < NROWS; row++ )
- {
- for ( int col = 0; col < NCOLS; col++ )
- {
+ for (int row = 0; row < NROWS; row++) {
+ for (int col = 0; col < NCOLS; col++) {
imagePixels[row * NCOLS + col] = NODATA;
}
}
- for ( int latIdx = -1; latIdx <= 5; latIdx++ )
- {
+ for (int latIdx = -1; latIdx <= 5; latIdx++) {
int latDegree = latDegreeStart + latIdx;
- int rowOffset = extraBorder + ( 4 - latIdx ) * 1200;
+ int rowOffset = extraBorder + (4 - latIdx) * 1200;
- for ( int lonIdx = -1; lonIdx <= 5; lonIdx++ )
- {
+ for (int lonIdx = -1; lonIdx <= 5; lonIdx++) {
int lonDegree = lonDegreeStart + lonIdx;
int colOffset = extraBorder + lonIdx * 1200;
- String filename = inputDir + "/" + formatLat( latDegree ) + formatLon( lonDegree ) + ".zip";
- File f = new File( filename );
- if ( f.exists() && f.length() > 0 )
- {
- System.out.println( "exist: " + filename );
- readHgtZip( filename, rowOffset, colOffset );
- }
- else
- {
- System.out.println( "none : " + filename );
+ String filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip";
+ File f = new File(filename);
+ if (f.exists() && f.length() > 0) {
+ System.out.println("exist: " + filename);
+ readHgtZip(filename, rowOffset, colOffset);
+ } else {
+ System.out.println("none : " + filename);
}
}
}
@@ -142,79 +119,71 @@ public class ConvertLidarTile
raster.halfcol = halfCol5;
raster.noDataValue = NODATA;
raster.cellsize = 1 / 1200.;
- raster.xllcorner = lonDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
- raster.yllcorner = latDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
+ raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize;
+ raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize;
raster.eval_array = imagePixels;
// encode the raster
- OutputStream os = new BufferedOutputStream( new FileOutputStream( outputFile ) );
- new RasterCoder().encodeRaster( raster, os );
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
+ new RasterCoder().encodeRaster(raster, os);
os.close();
// decode the raster
- InputStream is = new BufferedInputStream( new FileInputStream( outputFile ) );
- SrtmRaster raster2 = new RasterCoder().decodeRaster( is );
+ InputStream is = new BufferedInputStream(new FileInputStream(outputFile));
+ SrtmRaster raster2 = new RasterCoder().decodeRaster(is);
is.close();
short[] pix2 = raster2.eval_array;
- if ( pix2.length != imagePixels.length )
- throw new RuntimeException( "length mismatch!" );
+ if (pix2.length != imagePixels.length)
+ throw new RuntimeException("length mismatch!");
// compare decoding result
- for ( int row = 0; row < NROWS; row++ )
- {
+ for (int row = 0; row < NROWS; row++) {
int colstep = halfCol5 ? 2 : 1;
- for ( int col = 0; col < NCOLS; col += colstep )
- {
+ for (int col = 0; col < NCOLS; col += colstep) {
int idx = row * NCOLS + col;
short p2 = pix2[idx];
- if ( p2 != imagePixels[idx] )
- {
- throw new RuntimeException( "content mismatch: p2=" + p2 + " p1=" + imagePixels[idx] );
+ if (p2 != imagePixels[idx]) {
+ throw new RuntimeException("content mismatch: p2=" + p2 + " p1=" + imagePixels[idx]);
}
}
}
}
- private static String formatLon( int lon )
- {
- if ( lon >= 180 )
+ private static String formatLon(int lon) {
+ if (lon >= 180)
lon -= 180; // TODO: w180 oder E180 ?
String s = "E";
- if ( lon < 0 )
- {
+ if (lon < 0) {
lon = -lon;
s = "E";
}
String n = "000" + lon;
- return s + n.substring( n.length() - 3 );
+ return s + n.substring(n.length() - 3);
}
- private static String formatLat( int lat )
- {
+ private static String formatLat(int lat) {
String s = "N";
- if ( lat < 0 )
- {
+ if (lat < 0) {
lat = -lat;
s = "S";
}
String n = "00" + lat;
- return s + n.substring( n.length() - 2 );
+ return s + n.substring(n.length() - 2);
}
- public static void main( String[] args ) throws Exception
- {
- String filename90 = args[0];
- String filename30 = filename90.substring( 0, filename90.length() - 3 ) + "bef";
+ public static void main(String[] args) throws Exception {
+ String filename90 = args[0];
+ String filename30 = filename90.substring(0, filename90.length() - 3) + "bef";
- int srtmLonIdx = Integer.parseInt( filename90.substring( 5, 7 ).toLowerCase() );
- int srtmLatIdx = Integer.parseInt( filename90.substring( 8, 10 ).toLowerCase() );
+ int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase());
+ int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase());
- int ilon_base = ( srtmLonIdx - 1 ) * 5 - 180;
- int ilat_base = 150 - srtmLatIdx * 5 - 90;
-
- doConvert( args[1], ilon_base, ilat_base, filename30 );
+ int ilon_base = (srtmLonIdx - 1) * 5 - 180;
+ int ilat_base = 150 - srtmLatIdx * 5 - 90;
+
+ doConvert(args[1], ilon_base, ilat_base, filename30);
}
-
+
}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java
index f628152..e18c664 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java
@@ -3,8 +3,7 @@ package btools.mapcreator;
import java.io.*;
import java.util.zip.*;
-public class ConvertSrtmTile
-{
+public class ConvertSrtmTile {
public static int NROWS;
public static int NCOLS;
@@ -16,44 +15,33 @@ public class ConvertSrtmTile
public static int[] diffs = new int[100];
- private static void readBilZip( String filename, int rowOffset, int colOffset, boolean halfCols ) throws Exception
- {
- ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( filename ) ) );
- try
- {
- for ( ;; )
- {
+ private static void readBilZip(String filename, int rowOffset, int colOffset, boolean halfCols) throws Exception {
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename)));
+ try {
+ for (; ; ) {
ZipEntry ze = zis.getNextEntry();
- if ( ze.getName().endsWith( ".bil" ) )
- {
- readBilFromStream( zis, rowOffset, colOffset, halfCols );
+ if (ze.getName().endsWith(".bil")) {
+ readBilFromStream(zis, rowOffset, colOffset, halfCols);
return;
}
}
- }
- finally
- {
+ } finally {
zis.close();
}
}
- private static void readBilFromStream( InputStream is, int rowOffset, int colOffset, boolean halfCols )
- throws Exception
- {
- DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) );
- for ( int ir = 0; ir < 3601; ir++ )
- {
+ private static void readBilFromStream(InputStream is, int rowOffset, int colOffset, boolean halfCols)
+ throws Exception {
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
+ for (int ir = 0; ir < 3601; ir++) {
int row = rowOffset + ir;
- for ( int ic = 0; ic < 3601; ic++ )
- {
+ for (int ic = 0; ic < 3601; ic++) {
int col = colOffset + ic;
- if ( ( ic % 2 ) == 1 && halfCols )
- {
- if ( getPixel( row, col ) == NODATA )
- {
- setPixel( row, col, SKIPDATA );
+ if ((ic % 2) == 1 && halfCols) {
+ if (getPixel(row, col) == NODATA) {
+ setPixel(row, col, SKIPDATA);
}
continue;
}
@@ -61,42 +49,36 @@ public class ConvertSrtmTile
int i0 = dis.read();
int i1 = dis.read();
- if ( i0 == -1 || i1 == -1 )
- throw new RuntimeException( "unexcepted end of file reading bil entry!" );
+ if (i0 == -1 || i1 == -1)
+ throw new RuntimeException("unexcepted end of file reading bil entry!");
- short val = (short) ( ( i1 << 8 ) | i0 );
+ short val = (short) ((i1 << 8) | i0);
- if ( val == NODATA2 )
- {
+ if (val == NODATA2) {
val = NODATA;
}
- setPixel( row, col, val );
+ setPixel(row, col, val);
}
}
}
- private static void setPixel( int row, int col, short val )
- {
- if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
- {
+ private static void setPixel(int row, int col, short val) {
+ if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
imagePixels[row * NCOLS + col] = val;
}
}
- private static short getPixel( int row, int col )
- {
- if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
- {
+ private static short getPixel(int row, int col) {
+ if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
return imagePixels[row * NCOLS + col];
}
return NODATA;
}
- public static void doConvert( String inputDir, String v1Dir, int lonDegreeStart, int latDegreeStart, String outputFile, SrtmRaster raster90 ) throws Exception
- {
+ public static void doConvert(String inputDir, String v1Dir, int lonDegreeStart, int latDegreeStart, String outputFile, SrtmRaster raster90) throws Exception {
int extraBorder = 10;
int datacells = 0;
int mismatches = 0;
@@ -107,95 +89,74 @@ public class ConvertSrtmTile
imagePixels = new short[NROWS * NCOLS]; // 650 MB !
// prefill as NODATA
- for ( int row = 0; row < NROWS; row++ )
- {
- for ( int col = 0; col < NCOLS; col++ )
- {
+ for (int row = 0; row < NROWS; row++) {
+ for (int col = 0; col < NCOLS; col++) {
imagePixels[row * NCOLS + col] = NODATA;
}
}
- for ( int latIdx = -1; latIdx <= 5; latIdx++ )
- {
+ for (int latIdx = -1; latIdx <= 5; latIdx++) {
int latDegree = latDegreeStart + latIdx;
- int rowOffset = extraBorder + ( 4 - latIdx ) * 3600;
+ int rowOffset = extraBorder + (4 - latIdx) * 3600;
- for ( int lonIdx = -1; lonIdx <= 5; lonIdx++ )
- {
+ for (int lonIdx = -1; lonIdx <= 5; lonIdx++) {
int lonDegree = lonDegreeStart + lonIdx;
int colOffset = extraBorder + lonIdx * 3600;
- String filename = inputDir + "/" + formatLat( latDegree ) + "_" + formatLon( lonDegree ) + "_1arc_v3_bil.zip";
- File f = new File( filename );
- if ( f.exists() && f.length() > 0 )
- {
- System.out.println( "exist: " + filename );
+ String filename = inputDir + "/" + formatLat(latDegree) + "_" + formatLon(lonDegree) + "_1arc_v3_bil.zip";
+ File f = new File(filename);
+ if (f.exists() && f.length() > 0) {
+ System.out.println("exist: " + filename);
boolean halfCol = latDegree >= 50 || latDegree < -50;
- readBilZip( filename, rowOffset, colOffset, halfCol );
- }
- else
- {
- System.out.println( "none : " + filename );
+ readBilZip(filename, rowOffset, colOffset, halfCol);
+ } else {
+ System.out.println("none : " + filename);
}
}
}
boolean halfCol5 = latDegreeStart >= 50 || latDegreeStart < -50;
- for ( int row90 = 0; row90 < 6001; row90++ )
- {
+ for (int row90 = 0; row90 < 6001; row90++) {
int crow = 3 * row90 + extraBorder; // center row of 3x3
- for ( int col90 = 0; col90 < 6001; col90++ )
- {
+ for (int col90 = 0; col90 < 6001; col90++) {
int ccol = 3 * col90 + extraBorder; // center col of 3x3
// evaluate 3x3 area
- if ( raster90 != null && (!halfCol5 || (col90 % 2) == 0 ) )
- {
+ if (raster90 != null && (!halfCol5 || (col90 % 2) == 0)) {
short v90 = raster90.eval_array[row90 * 6001 + col90];
int sum = 0;
int nodatas = 0;
int datas = 0;
int colstep = halfCol5 ? 2 : 1;
- for ( int row = crow - 1; row <= crow + 1; row++ )
- {
- for ( int col = ccol - colstep; col <= ccol + colstep; col += colstep )
- {
+ for (int row = crow - 1; row <= crow + 1; row++) {
+ for (int col = ccol - colstep; col <= ccol + colstep; col += colstep) {
short v30 = imagePixels[row * NCOLS + col];
- if ( v30 == NODATA )
- {
+ if (v30 == NODATA) {
nodatas++;
- }
- else if ( v30 != SKIPDATA )
- {
+ } else if (v30 != SKIPDATA) {
sum += v30;
datas++;
}
}
}
boolean doReplace = nodatas > 0 || v90 == NODATA || datas < 7;
- if ( !doReplace )
- {
+ if (!doReplace) {
datacells++;
int diff = sum - datas * v90;
- if ( diff < -4 || diff > 4 )
- {
+ if (diff < -4 || diff > 4) {
doReplace = true;
mismatches++;
}
- if ( diff > -50 && diff < 50 && ( row90 % 1200 ) != 0 && ( col90 % 1200 ) != 0 )
- {
+ if (diff > -50 && diff < 50 && (row90 % 1200) != 0 && (col90 % 1200) != 0) {
diffs[diff + 50]++;
}
}
- if ( doReplace )
- {
- for ( int row = crow - 1; row <= crow + 1; row++ )
- {
- for ( int col = ccol - colstep; col <= ccol + colstep; col += colstep )
- {
+ if (doReplace) {
+ for (int row = crow - 1; row <= crow + 1; row++) {
+ for (int col = ccol - colstep; col <= ccol + colstep; col += colstep) {
imagePixels[row * NCOLS + col] = v90;
}
}
@@ -210,102 +171,90 @@ public class ConvertSrtmTile
raster.halfcol = halfCol5;
raster.noDataValue = NODATA;
raster.cellsize = 1 / 3600.;
- raster.xllcorner = lonDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
- raster.yllcorner = latDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
+ raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize;
+ raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize;
raster.eval_array = imagePixels;
// encode the raster
- OutputStream os = new BufferedOutputStream( new FileOutputStream( outputFile ) );
- new RasterCoder().encodeRaster( raster, os );
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
+ new RasterCoder().encodeRaster(raster, os);
os.close();
// decode the raster
- InputStream is = new BufferedInputStream( new FileInputStream( outputFile ) );
- SrtmRaster raster2 = new RasterCoder().decodeRaster( is );
+ InputStream is = new BufferedInputStream(new FileInputStream(outputFile));
+ SrtmRaster raster2 = new RasterCoder().decodeRaster(is);
is.close();
short[] pix2 = raster2.eval_array;
- if ( pix2.length != imagePixels.length )
- throw new RuntimeException( "length mismatch!" );
+ if (pix2.length != imagePixels.length)
+ throw new RuntimeException("length mismatch!");
// compare decoding result
- for ( int row = 0; row < NROWS; row++ )
- {
+ for (int row = 0; row < NROWS; row++) {
int colstep = halfCol5 ? 2 : 1;
- for ( int col = 0; col < NCOLS; col += colstep )
- {
+ for (int col = 0; col < NCOLS; col += colstep) {
int idx = row * NCOLS + col;
- if ( imagePixels[idx] == SKIPDATA )
- {
+ if (imagePixels[idx] == SKIPDATA) {
continue;
}
short p2 = pix2[idx];
- if ( p2 > SKIPDATA )
- {
+ if (p2 > SKIPDATA) {
p2 /= 2;
}
- if ( p2 != imagePixels[idx] )
- {
- throw new RuntimeException( "content mismatch!" );
+ if (p2 != imagePixels[idx]) {
+ throw new RuntimeException("content mismatch!");
}
}
}
-
- for(int i=1; i<100;i++) System.out.println( "diff[" + (i-50) + "] = " + diffs[i] );
- System.out.println( "datacells=" + datacells + " mismatch%=" + (100.*mismatches)/datacells );
-btools.util.MixCoderDataOutputStream.stats();
+
+ for (int i = 1; i < 100; i++) System.out.println("diff[" + (i - 50) + "] = " + diffs[i]);
+ System.out.println("datacells=" + datacells + " mismatch%=" + (100. * mismatches) / datacells);
+ btools.util.MixCoderDataOutputStream.stats();
// test( raster );
// raster.calcWeights( 50. );
// test( raster );
// 39828330 &lon=3115280&layer=OpenStreetMap
}
- private static void test( SrtmRaster raster )
- {
+ private static void test(SrtmRaster raster) {
int lat0 = 39828330;
int lon0 = 3115280;
- for ( int iy = -9; iy <= 9; iy++ )
- {
+ for (int iy = -9; iy <= 9; iy++) {
StringBuilder sb = new StringBuilder();
- for ( int ix = -9; ix <= 9; ix++ )
- {
+ for (int ix = -9; ix <= 9; ix++) {
int lat = lat0 + 90000000 - 100 * iy;
int lon = lon0 + 180000000 + 100 * ix;
- int ival = (int) ( raster.getElevation( lon, lat ) / 4. );
+ int ival = (int) (raster.getElevation(lon, lat) / 4.);
String sval = " " + ival;
- sb.append( sval.substring( sval.length() - 4 ) );
+ sb.append(sval.substring(sval.length() - 4));
}
- System.out.println( sb );
+ System.out.println(sb);
System.out.println();
}
}
- private static String formatLon( int lon )
- {
- if ( lon >= 180 )
+ private static String formatLon(int lon) {
+ if (lon >= 180)
lon -= 180; // TODO: w180 oder E180 ?
String s = "e";
- if ( lon < 0 )
- {
+ if (lon < 0) {
lon = -lon;
s = "w";
}
String n = "000" + lon;
- return s + n.substring( n.length() - 3 );
+ return s + n.substring(n.length() - 3);
}
- private static String formatLat( int lat )
- {
+ private static String formatLat(int lat) {
String s = "n";
- if ( lat < 0 )
- {
+ if (lat < 0) {
lat = -lat;
s = "s";
}
String n = "00" + lat;
- return s + n.substring( n.length() - 2 );
+ return s + n.substring(n.length() - 2);
}
}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java
index b41e31a..ab5b8db 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java
@@ -4,57 +4,50 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
-public class ConvertUrlList
-{
+public class ConvertUrlList {
public static final short NODATA = -32767;
- public static void main( String[] args ) throws Exception
- {
- BufferedReader br = new BufferedReader( new FileReader( args[0] ) );
+ public static void main(String[] args) throws Exception {
+ BufferedReader br = new BufferedReader(new FileReader(args[0]));
- for ( ;; )
- {
+ for (; ; ) {
String line = br.readLine();
- if ( line == null )
- {
+ if (line == null) {
break;
}
- int idx1 = line.indexOf( "srtm_" );
- if ( idx1 < 0 )
- {
+ int idx1 = line.indexOf("srtm_");
+ if (idx1 < 0) {
continue;
}
- String filename90 = line.substring( idx1 );
- String filename30 = filename90.substring( 0, filename90.length() - 3 ) + "bef";
+ String filename90 = line.substring(idx1);
+ String filename30 = filename90.substring(0, filename90.length() - 3) + "bef";
- if ( new File( filename30 ).exists() )
- {
+ if (new File(filename30).exists()) {
continue;
}
// int srtmLonIdx = (ilon+5000000)/5000000; -> ilon = (srtmLonIdx-1)*5
// int srtmLatIdx = (154999999-ilat)/5000000; -> ilat = 155 - srtmLatIdx*5
- int srtmLonIdx = Integer.parseInt( filename90.substring( 5, 7 ).toLowerCase() );
- int srtmLatIdx = Integer.parseInt( filename90.substring( 8, 10 ).toLowerCase() );
+ int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase());
+ int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase());
- int ilon_base = ( srtmLonIdx - 1 ) * 5 - 180;
+ int ilon_base = (srtmLonIdx - 1) * 5 - 180;
int ilat_base = 150 - srtmLatIdx * 5 - 90;
SrtmRaster raster90 = null;
- File file90 = new File( new File( args[1] ), filename90 );
- if ( file90.exists() )
- {
- System.out.println( "reading " + file90 );
- raster90 = new SrtmData( file90 ).getRaster();
+ File file90 = new File(new File(args[1]), filename90);
+ if (file90.exists()) {
+ System.out.println("reading " + file90);
+ raster90 = new SrtmData(file90).getRaster();
}
-
- ConvertSrtmTile.doConvert( args[2], args[3], ilon_base, ilat_base, filename30, raster90 );
+
+ ConvertSrtmTile.doConvert(args[2], args[3], ilon_base, ilat_base, filename30, raster90);
}
br.close();
}
-
+
}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/DPFilter.java b/brouter-map-creator/src/main/java/btools/mapcreator/DPFilter.java
index 2310fd1..1ac64f6 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/DPFilter.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/DPFilter.java
@@ -9,76 +9,63 @@ import java.util.ArrayList;
import btools.util.CheapRuler;
-public class DPFilter
-{
+public class DPFilter {
private static double dp_sql_threshold = 0.4 * 0.4;
/*
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
*/
- public static void doDPFilter( ArrayList nodes )
- {
+ public static void doDPFilter(ArrayList nodes) {
int first = 0;
- int last = nodes.size()-1;
- while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
- {
+ int last = nodes.size() - 1;
+ while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
first++;
}
- while( first < last && (nodes.get(last-1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
- {
+ while (first < last && (nodes.get(last - 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
last--;
}
- if ( last - first > 1 )
- {
- doDPFilter( nodes, first, last );
+ if (last - first > 1) {
+ doDPFilter(nodes, first, last);
}
}
-
- public static void doDPFilter( ArrayList nodes, int first, int last )
- {
+
+ public static void doDPFilter(ArrayList nodes, int first, int last) {
double maxSqDist = -1.;
int index = -1;
- OsmNodeP p1 = nodes.get( first );
- OsmNodeP p2 = nodes.get( last );
+ OsmNodeP p1 = nodes.get(first);
+ OsmNodeP p2 = nodes.get(last);
- double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (p1.ilat+p2.ilat) >> 1 );
+ double[] lonlat2m = CheapRuler.getLonLatToMeterScales((p1.ilat + p2.ilat) >> 1);
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
double dx = (p2.ilon - p1.ilon) * dlon2m;
double dy = (p2.ilat - p1.ilat) * dlat2m;
double d2 = dx * dx + dy * dy;
- for ( int i = first + 1; i < last; i++ )
- {
- OsmNodeP p = nodes.get( i );
+ for (int i = first + 1; i < last; i++) {
+ OsmNodeP p = nodes.get(i);
double t = 0.;
- if ( d2 != 0f )
- {
- t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
- t = t > 1. ? 1. : ( t < 0. ? 0. : t );
+ if (d2 != 0f) {
+ t = ((p.ilon - p1.ilon) * dlon2m * dx + (p.ilat - p1.ilat) * dlat2m * dy) / d2;
+ t = t > 1. ? 1. : (t < 0. ? 0. : t);
}
- double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m;
- double dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
+ double dx2 = (p.ilon - (p1.ilon + t * (p2.ilon - p1.ilon))) * dlon2m;
+ double dy2 = (p.ilat - (p1.ilat + t * (p2.ilat - p1.ilat))) * dlat2m;
double sqDist = dx2 * dx2 + dy2 * dy2;
- if ( sqDist > maxSqDist )
- {
+ if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
- if ( index >= 0 )
- {
- if ( index - first > 1 )
- {
- doDPFilter( nodes, first, index );
+ if (index >= 0) {
+ if (index - first > 1) {
+ doDPFilter(nodes, first, index);
}
- if ( maxSqDist >= dp_sql_threshold )
- {
- nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
+ if (maxSqDist >= dp_sql_threshold) {
+ nodes.get(index).bits |= OsmNodeP.DP_SURVIVOR_BIT;
}
- if ( last - index > 1 )
- {
- doDPFilter( nodes, index, last );
+ if (last - index > 1) {
+ doDPFilter(nodes, index, last);
}
}
}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/MapCreatorBase.java b/brouter-map-creator/src/main/java/btools/mapcreator/MapCreatorBase.java
index f7c7b0a..c207b2a 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/MapCreatorBase.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/MapCreatorBase.java
@@ -1,171 +1,158 @@
-/**
- * common base class for the map-filters
- *
- * @author ab
- */
-package btools.mapcreator;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.HashMap;
-
-import btools.util.DiffCoderDataOutputStream;
-
-public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
-{
- private DiffCoderDataOutputStream[] tileOutStreams;
- protected File outTileDir;
-
- protected HashMap tags;
-
- public void putTag( String key, String value )
- {
- if ( tags == null ) tags = new HashMap();
- tags.put( key, value );
- }
-
- public String getTag( String key )
- {
- return tags == null ? null : tags.get( key );
- }
-
- public HashMap getTagsOrNull()
- {
- return tags;
- }
-
- public void setTags( HashMap tags )
- {
- this.tags = tags;
- }
-
- protected static long readId( DataInputStream is) throws IOException
- {
- int offset = is.readByte();
- if ( offset == 32 ) return -1;
- long i = is.readInt();
- i = i << 5;
- return i | offset;
- }
-
- protected static void writeId( DataOutputStream o, long id ) throws IOException
- {
- if ( id == -1 )
- {
- o.writeByte( 32 );
- return;
- }
- int offset = (int)( id & 0x1f );
- int i = (int)( id >> 5 );
- o.writeByte( offset );
- o.writeInt( i );
- }
-
-
- protected static File[] sortBySizeAsc( File[] files )
- {
- int n = files.length;
- long[] sizes = new long[n];
- File[] sorted = new File[n];
- for( int i=0; i tags;
+
+ public void putTag(String key, String value) {
+ if (tags == null) tags = new HashMap();
+ tags.put(key, value);
+ }
+
+ public String getTag(String key) {
+ return tags == null ? null : tags.get(key);
+ }
+
+ public HashMap getTagsOrNull() {
+ return tags;
+ }
+
+ public void setTags(HashMap tags) {
+ this.tags = tags;
+ }
+
+ protected static long readId(DataInputStream is) throws IOException {
+ int offset = is.readByte();
+ if (offset == 32) return -1;
+ long i = is.readInt();
+ i = i << 5;
+ return i | offset;
+ }
+
+ protected static void writeId(DataOutputStream o, long id) throws IOException {
+ if (id == -1) {
+ o.writeByte(32);
+ return;
+ }
+ int offset = (int) (id & 0x1f);
+ int i = (int) (id >> 5);
+ o.writeByte(offset);
+ o.writeInt(i);
+ }
+
+
+ protected static File[] sortBySizeAsc(File[] files) {
+ int n = files.length;
+ long[] sizes = new long[n];
+ File[] sorted = new File[n];
+ for (int i = 0; i < n; i++) sizes[i] = files[i].length();
+ for (int nf = 0; nf < n; nf++) {
+ int idx = -1;
+ long min = -1;
+ for (int i = 0; i < n; i++) {
+ if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) {
+ min = sizes[i];
+ idx = i;
+ }
+ }
+ sizes[idx] = -1;
+ sorted[nf] = files[idx];
+ }
+ return sorted;
+ }
+
+ protected File fileFromTemplate(File template, File dir, String suffix) {
+ String filename = template.getName();
+ filename = filename.substring(0, filename.length() - 3) + suffix;
+ return new File(dir, filename);
+ }
+
+ protected DataInputStream createInStream(File inFile) throws IOException {
+ return new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
+ }
+
+ protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException {
+ return new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
+ }
+
+ protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception {
+ if (tileOutStreams == null) {
+ tileOutStreams = new DiffCoderDataOutputStream[64];
+ }
+
+ if (tileOutStreams[tileIndex] == null) {
+ tileOutStreams[tileIndex] = createOutStream(new File(outTileDir, getNameForTile(tileIndex)));
+ }
+ return tileOutStreams[tileIndex];
+ }
+
+ protected String getNameForTile(int tileIndex) {
+ throw new IllegalArgumentException("getNameForTile not implemented");
+ }
+
+ protected void closeTileOutStreams() throws Exception {
+ if (tileOutStreams == null) {
+ return;
+ }
+ for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) {
+ if (tileOutStreams[tileIndex] != null) tileOutStreams[tileIndex].close();
+ tileOutStreams[tileIndex] = null;
+ }
+ }
+
+
+ // interface dummys
+
+ @Override
+ public void nodeFileStart(File nodefile) throws Exception {
+ }
+
+ @Override
+ public void nextNode(NodeData n) throws Exception {
+ }
+
+ @Override
+ public void nodeFileEnd(File nodefile) throws Exception {
+ }
+
+ @Override
+ public boolean wayFileStart(File wayfile) throws Exception {
+ return true;
+ }
+
+ @Override
+ public void nextWay(WayData data) throws Exception {
+ }
+
+ @Override
+ public void wayFileEnd(File wayfile) throws Exception {
+ }
+
+ @Override
+ public void nextRelation(RelationData data) throws Exception {
+ }
+
+ @Override
+ public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception {
+ }
+
+}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/NodeCutter.java b/brouter-map-creator/src/main/java/btools/mapcreator/NodeCutter.java
index 8dc20ed..0464f30 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/NodeCutter.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/NodeCutter.java
@@ -1,84 +1,75 @@
-package btools.mapcreator;
-
-import java.io.File;
-
-/**
- * NodeCutter does 1 step in map-processing:
- *
- * - cuts the 45*30 node tiles into 5*5 pieces
- *
- * @author ab
- */
-public class NodeCutter extends MapCreatorBase
-{
- private int lonoffset;
- private int latoffset;
-
- public static void main(String[] args) throws Exception
- {
- System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
- if (args.length != 2)
- {
- System.out.println("usage: java NodeCutter " );
- return;
- }
- new NodeCutter().process( new File( args[0] ), new File( args[1] ) );
- }
-
- public void init( File nodeTilesOut )
- {
- this.outTileDir = nodeTilesOut;
- }
-
- public void process( File nodeTilesIn, File nodeTilesOut ) throws Exception
- {
- init( nodeTilesOut );
-
- new NodeIterator( this, true ).processDir( nodeTilesIn, ".tlf" );
- }
-
- @Override
- public void nodeFileStart( File nodefile ) throws Exception
- {
- lonoffset = -1;
- latoffset = -1;
- }
-
- @Override
- public void nextNode( NodeData n ) throws Exception
- {
- n.writeTo( getOutStreamForTile( getTileIndex( n.ilon, n.ilat ) ) );
- }
-
- @Override
- public void nodeFileEnd( File nodeFile ) throws Exception
- {
- closeTileOutStreams();
- }
-
- private int getTileIndex( int ilon, int ilat )
- {
- int lonoff = (ilon / 45000000 ) * 45;
- int latoff = (ilat / 30000000 ) * 30;
- if ( lonoffset == -1 ) lonoffset = lonoff;
- if ( latoffset == -1 ) latoffset = latoff;
- if ( lonoff != lonoffset || latoff != latoffset )
- throw new IllegalArgumentException( "inconsistent node: " + ilon + " " + ilat );
-
- int lon = (ilon / 5000000) % 9;
- int lat = (ilat / 5000000) % 6;
- if ( lon < 0 || lon > 8 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat );
- return lon*6 + lat;
- }
-
-
- protected String getNameForTile( int tileIndex )
- {
- int lon = (tileIndex / 6 ) * 5 + lonoffset - 180;
- int lat = (tileIndex % 6 ) * 5 + latoffset - 90;
- String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
- String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
- return slon + "_" + slat + ".n5d";
- }
-
-}
+package btools.mapcreator;
+
+import java.io.File;
+
+/**
+ * NodeCutter does 1 step in map-processing:
+ *
+ * - cuts the 45*30 node tiles into 5*5 pieces
+ *
+ * @author ab
+ */
+public class NodeCutter extends MapCreatorBase {
+ private int lonoffset;
+ private int latoffset;
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
+ if (args.length != 2) {
+ System.out.println("usage: java NodeCutter ");
+ return;
+ }
+ new NodeCutter().process(new File(args[0]), new File(args[1]));
+ }
+
+ public void init(File nodeTilesOut) {
+ this.outTileDir = nodeTilesOut;
+ }
+
+ public void process(File nodeTilesIn, File nodeTilesOut) throws Exception {
+ init(nodeTilesOut);
+
+ new NodeIterator(this, true).processDir(nodeTilesIn, ".tlf");
+ }
+
+ @Override
+ public void nodeFileStart(File nodefile) throws Exception {
+ lonoffset = -1;
+ latoffset = -1;
+ }
+
+ @Override
+ public void nextNode(NodeData n) throws Exception {
+ n.writeTo(getOutStreamForTile(getTileIndex(n.ilon, n.ilat)));
+ }
+
+ @Override
+ public void nodeFileEnd(File nodeFile) throws Exception {
+ closeTileOutStreams();
+ }
+
+ private int getTileIndex(int ilon, int ilat) {
+ int lonoff = (ilon / 45000000) * 45;
+ int latoff = (ilat / 30000000) * 30;
+ if (lonoffset == -1) lonoffset = lonoff;
+ if (latoffset == -1) latoffset = latoff;
+ if (lonoff != lonoffset || latoff != latoffset)
+ throw new IllegalArgumentException("inconsistent node: " + ilon + " " + ilat);
+
+ int lon = (ilon / 5000000) % 9;
+ int lat = (ilat / 5000000) % 6;
+ if (lon < 0 || lon > 8 || lat < 0 || lat > 5)
+ throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat);
+ return lon * 6 + lat;
+ }
+
+
+ protected String getNameForTile(int tileIndex) {
+ int lon = (tileIndex / 6) * 5 + lonoffset - 180;
+ int lat = (tileIndex % 6) * 5 + latoffset - 90;
+ String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
+ String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
+ return slon + "_" + slat + ".n5d";
+ }
+
+}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/NodeData.java b/brouter-map-creator/src/main/java/btools/mapcreator/NodeData.java
index ef80ac6..f1fe04c 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/NodeData.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/NodeData.java
@@ -1,46 +1,49 @@
-package btools.mapcreator;
-
-import btools.util.DiffCoderDataInputStream;
-import btools.util.DiffCoderDataOutputStream;
-
-/**
- * Container for node data on the preprocessor level
- *
- * @author ab
- */
-public class NodeData extends MapCreatorBase
-{
- public long nid;
- public int ilon;
- public int ilat;
- public byte[] description;
- public short selev = Short.MIN_VALUE;
-
- public NodeData( long id, double lon, double lat )
- {
- nid = id;
- ilat = (int)( ( lat + 90. )*1000000. + 0.5);
- ilon = (int)( ( lon + 180. )*1000000. + 0.5);
- }
-
- public NodeData( DiffCoderDataInputStream dis ) throws Exception
- {
- nid = dis.readDiffed( 0 );
- ilon = (int)dis.readDiffed( 1 );
- ilat = (int)dis.readDiffed( 2 );
- int mode = dis.readByte();
- if ( ( mode & 1 ) != 0 ) { int dlen = dis.readShort(); description = new byte[dlen]; dis.readFully( description ); }
- if ( ( mode & 2 ) != 0 ) selev = dis.readShort();
- }
-
- public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
- {
- dos.writeDiffed( nid, 0 );
- dos.writeDiffed( ilon, 1 );
- dos.writeDiffed( ilat, 2 );
- int mode = (description == null ? 0 : 1 ) | ( selev == Short.MIN_VALUE ? 0 : 2 );
- dos.writeByte( (byte) mode);
- if ( ( mode & 1 ) != 0 ) { dos.writeShort( description.length ); dos.write( description ); }
- if ( ( mode & 2 ) != 0 ) dos.writeShort( selev );
- }
-}
+package btools.mapcreator;
+
+import btools.util.DiffCoderDataInputStream;
+import btools.util.DiffCoderDataOutputStream;
+
+/**
+ * Container for node data on the preprocessor level
+ *
+ * @author ab
+ */
+public class NodeData extends MapCreatorBase {
+ public long nid;
+ public int ilon;
+ public int ilat;
+ public byte[] description;
+ public short selev = Short.MIN_VALUE;
+
+ public NodeData(long id, double lon, double lat) {
+ nid = id;
+ ilat = (int) ((lat + 90.) * 1000000. + 0.5);
+ ilon = (int) ((lon + 180.) * 1000000. + 0.5);
+ }
+
+ public NodeData(DiffCoderDataInputStream dis) throws Exception {
+ nid = dis.readDiffed(0);
+ ilon = (int) dis.readDiffed(1);
+ ilat = (int) dis.readDiffed(2);
+ int mode = dis.readByte();
+ if ((mode & 1) != 0) {
+ int dlen = dis.readShort();
+ description = new byte[dlen];
+ dis.readFully(description);
+ }
+ if ((mode & 2) != 0) selev = dis.readShort();
+ }
+
+ public void writeTo(DiffCoderDataOutputStream dos) throws Exception {
+ dos.writeDiffed(nid, 0);
+ dos.writeDiffed(ilon, 1);
+ dos.writeDiffed(ilat, 2);
+ int mode = (description == null ? 0 : 1) | (selev == Short.MIN_VALUE ? 0 : 2);
+ dos.writeByte((byte) mode);
+ if ((mode & 1) != 0) {
+ dos.writeShort(description.length);
+ dos.write(description);
+ }
+ if ((mode & 2) != 0) dos.writeShort(selev);
+ }
+}
diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/NodeFilter.java b/brouter-map-creator/src/main/java/btools/mapcreator/NodeFilter.java
index f40b2fe..1f52f73 100644
--- a/brouter-map-creator/src/main/java/btools/mapcreator/NodeFilter.java
+++ b/brouter-map-creator/src/main/java/btools/mapcreator/NodeFilter.java
@@ -1,92 +1,80 @@
-package btools.mapcreator;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-
-import btools.util.DenseLongMap;
-import btools.util.DiffCoderDataOutputStream;
-import btools.util.TinyDenseLongMap;
-
-/**
- * NodeFilter does 1 step in map-processing:
- *
- * - filters out unused nodes according to the way file
- *
- * @author ab
- */
-public class NodeFilter extends MapCreatorBase
-{
- private DiffCoderDataOutputStream nodesOutStream;
- private File nodeTilesOut;
- protected DenseLongMap nodebitmap;
-
- public static void main(String[] args) throws Exception
- {
- System.out.println("*** NodeFilter: Filter way related nodes");
- if (args.length != 3)
- {
- System.out.println("usage: java NodeFilter