diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 16fec68..664db89 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -7,3 +7,6 @@ # Reformat brouter-routing-app using Android Studio 54d5c5e9439be2c3df4c95b6fc12d33fdcc9b389 + +# Reformat whole codebase using Android Studio +c15913c1ab9befd8d583d4a7716d5043d2966f64 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); } 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 " ); - return; - } - - new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) ); - } - - public void init() throws Exception - { - nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap(); - } - - public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception - { - init(); - - this.nodeTilesOut = nodeTilesOut; - - // read the wayfile into a bitmap of used nodes - new WayIterator( this, false ).processFile( wayFileIn ); - - // finally filter all node files - new NodeIterator( this, true ).processDir( nodeTilesIn, ".tls" ); - } - - @Override - public void nextWay( WayData data ) throws Exception - { - int nnodes = data.nodes.size(); - for (int i=0; i bit set, -1 -> unset - } - - @Override - public void nodeFileEnd( File nodeFile ) throws Exception - { - nodesOutStream.close(); - } -} +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 "); + return; + } + + new NodeFilter().process(new File(args[0]), new File(args[1]), new File(args[2])); + } + + public void init() throws Exception { + nodebitmap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap(512) : new TinyDenseLongMap(); + } + + public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception { + init(); + + this.nodeTilesOut = nodeTilesOut; + + // read the wayfile into a bitmap of used nodes + new WayIterator(this, false).processFile(wayFileIn); + + // finally filter all node files + new NodeIterator(this, true).processDir(nodeTilesIn, ".tls"); + } + + @Override + public void nextWay(WayData data) throws Exception { + int nnodes = data.nodes.size(); + for (int i = 0; i < nnodes; i++) { + nodebitmap.put(data.nodes.get(i), 0); + } + } + + @Override + public void nodeFileStart(File nodefile) throws Exception { + String filename = nodefile.getName(); + File outfile = new File(nodeTilesOut, filename); + nodesOutStream = new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outfile))); + } + + @Override + public void nextNode(NodeData n) throws Exception { + if (isRelevant(n)) { + n.writeTo(nodesOutStream); + } + } + + public boolean isRelevant(NodeData n) { + // check if node passes bitmap + return nodebitmap.getInt(n.nid) == 0; // 0 -> bit set, -1 -> unset + } + + @Override + public void nodeFileEnd(File nodeFile) throws Exception { + nodesOutStream.close(); + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/NodeIterator.java b/brouter-map-creator/src/main/java/btools/mapcreator/NodeIterator.java index c523f92..dbe24a2 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/NodeIterator.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/NodeIterator.java @@ -1,71 +1,59 @@ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; - -import btools.util.DiffCoderDataInputStream; - -/** - * Iterate over a singe nodefile or a directory - * of nodetiles and feed the nodes to the callback listener - * - * @author ab - */ -public class NodeIterator extends MapCreatorBase -{ - private NodeListener listener; - private boolean delete; - - public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading ) - { - listener = nodeListener; - delete = deleteAfterReading; - } - - public void processDir( File indir, String inSuffix ) throws Exception - { - if ( !indir.isDirectory() ) - { - throw new IllegalArgumentException( "not a directory: " + indir ); - } - - File[] af = sortBySizeAsc( indir.listFiles() ); - for( int i=0; i | java OsmCutter "); - System.out.println("or : java OsmCutter "); - return; - } - - new OsmCutter().process( - new File( args[0] ) - , new File( args[1] ) - , new File( args[2] ) - , new File( args[3] ) - , new File( args[4] ) - , new File( args[5] ) - , args.length > 6 ? new File( args[6] ) : null - ); - } - - private BExpressionContextWay _expctxWay; - private BExpressionContextNode _expctxNode; - - // private BExpressionContextWay _expctxWayStat; - // private BExpressionContextNode _expctxNodeStat; - - public void process (File lookupFile, File outTileDir, File wayFile, File relFile, File resFile, File profileFile, File mapFile ) throws Exception - { - if ( !lookupFile.exists() ) - { - throw new IllegalArgumentException( "lookup-file: " + lookupFile + " does not exist" ); - } - - BExpressionMetaData meta = new BExpressionMetaData(); - - _expctxWay = new BExpressionContextWay( meta ); - _expctxNode = new BExpressionContextNode( meta ); - meta.readMetaData( lookupFile ); - _expctxWay.parseFile( profileFile, "global" ); - - - // _expctxWayStat = new BExpressionContextWay( null ); - // _expctxNodeStat = new BExpressionContextNode( null ); - - this.outTileDir = outTileDir; - if ( !outTileDir.isDirectory() ) throw new RuntimeException( "out tile directory " + outTileDir + " does not exist" ); - - wayDos = wayFile == null ? null : new DataOutputStream( new BufferedOutputStream( new FileOutputStream( wayFile ) ) ); - cyclewayDos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( relFile ) ) ); - if ( resFile != null ) - { - restrictionsDos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( resFile ) ) ); - } - - // read the osm map into memory - long t0 = System.currentTimeMillis(); - new OsmParser().readMap( mapFile, this, this, this ); - long t1 = System.currentTimeMillis(); - - System.out.println( "parsing time (ms) =" + (t1-t0) ); - - // close all files - closeTileOutStreams(); - if ( wayDos != null ) - { - wayDos.close(); - } - cyclewayDos.close(); - if ( restrictionsDos != null ) - { - restrictionsDos.close(); - } - -// System.out.println( "-------- way-statistics -------- " ); -// _expctxWayStat.dumpStatistics(); -// System.out.println( "-------- node-statistics -------- " ); -// _expctxNodeStat.dumpStatistics(); - - System.out.println( statsLine() ); - } - - private void checkStats() - { - if ( (++recordCnt % 100000) == 0 ) System.out.println( statsLine() ); - } - - private String statsLine() - { - return "records read: " + recordCnt + " nodes=" + nodesParsed + " ways=" + waysParsed + " rels=" + relsParsed + " changesets=" + changesetsParsed; - } - - - @Override - public void nextNode( NodeData n ) throws Exception - { - nodesParsed++; - checkStats(); - - if ( n.getTagsOrNull() != null ) - { - int[] lookupData = _expctxNode.createNewLookupData(); - for( Map.Entry e : n.getTagsOrNull().entrySet() ) - { - _expctxNode.addLookupValue( e.getKey(), e.getValue(), lookupData ); - // _expctxNodeStat.addLookupValue( key, value, null ); - } - n.description = _expctxNode.encode(lookupData); - } - // write node to file - int tileIndex = getTileIndex( n.ilon, n.ilat ); - if ( tileIndex >= 0 ) - { - n.writeTo( getOutStreamForTile( tileIndex ) ); - if ( wayCutter != null ) - { - wayCutter.nextNode( n ); - } - } - } - - - private void generatePseudoTags( HashMap map ) - { - // add pseudo.tags for concrete:lanes and concrete:plates - - String concrete = null; - for( Map.Entry e : map.entrySet() ) - { - String key = e.getKey(); - - if ( "concrete".equals( key ) ) - { - return; - } - if ( "surface".equals( key ) ) - { - String value = e.getValue(); - if ( value.startsWith( "concrete:" ) ) - { - concrete = value.substring( "concrete:".length() ); - } - } - } - if ( concrete != null ) - { - map.put( "concrete", concrete ); - } - } - - - @Override - public void nextWay( WayData w ) throws Exception - { - waysParsed++; - checkStats(); - - // encode tags - if ( w.getTagsOrNull() == null ) return; - - generatePseudoTags( w.getTagsOrNull() ); - - int[] lookupData = _expctxWay.createNewLookupData(); - for( String key : w.getTagsOrNull().keySet() ) - { - String value = w.getTag( key ); - _expctxWay.addLookupValue( key, value.replace( ' ', '_' ), lookupData ); - // _expctxWayStat.addLookupValue( key, value, null ); - } - w.description = _expctxWay.encode(lookupData); - - if ( w.description == null ) return; - - // filter according to profile - _expctxWay.evaluate( false, w.description ); - boolean ok = _expctxWay.getCostfactor() < 10000.; - _expctxWay.evaluate( true, w.description ); - ok |= _expctxWay.getCostfactor() < 10000.; - if ( !ok ) return; - - if ( wayDos != null ) - { - w.writeTo( wayDos ); - } - if ( wayCutter != null ) - { - wayCutter.nextWay( w ); - } - if ( nodeFilter != null ) - { - nodeFilter.nextWay( w ); - } - } - - @Override - public void nextRelation( RelationData r ) throws Exception - { - relsParsed++; - checkStats(); - - String route = r.getTag( "route" ); - // filter out non-cycle relations - if ( route == null ) - { - return; - } - - String network = r.getTag( "network" ); - if ( network == null ) network = ""; - String state = r.getTag( "state" ); - if ( state == null ) state = ""; - writeId( cyclewayDos, r.rid ); - cyclewayDos.writeUTF( route ); - cyclewayDos.writeUTF( network ); - cyclewayDos.writeUTF( state ); - for ( int i=0; i 7 || lat < 0 || lat > 5 ) - { - System.out.println( "warning: ignoring illegal pos: " + ilon + "," + ilat ); - return -1; - } - return lon*6 + lat; - } - - protected String getNameForTile( int tileIndex ) - { - int lon = (tileIndex / 6 ) * 45 - 180; - int lat = (tileIndex % 6 ) * 30 - 90; - String slon = lon < 0 ? "W" + (-lon) : "E" + lon; - String slat = lat < 0 ? "S" + (-lat) : "N" + lat; - return slon + "_" + slat + ".ntl"; - } -} +/** + * This program + * - reads an *.osm from stdin + * - writes 45*30 degree node tiles + a way file + a rel file + * + * @author ab + */ +package btools.mapcreator; + +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Map; + +import btools.expressions.BExpressionContextNode; +import btools.expressions.BExpressionContextWay; +import btools.expressions.BExpressionMetaData; + +public class OsmCutter extends MapCreatorBase { + private long recordCnt; + private long nodesParsed; + private long waysParsed; + private long relsParsed; + private long changesetsParsed; + + private DataOutputStream wayDos; + private DataOutputStream cyclewayDos; + private DataOutputStream restrictionsDos; + + public WayCutter wayCutter; + public RestrictionCutter restrictionCutter; + public NodeFilter nodeFilter; + + public static void main(String[] args) throws Exception { + System.out.println("*** OsmCutter: cut an osm map in node-tiles + a way file"); + if (args.length != 6 && args.length != 7) { + System.out.println("usage: bzip2 -dc | java OsmCutter "); + System.out.println("or : java OsmCutter "); + return; + } + + new OsmCutter().process( + new File(args[0]) + , new File(args[1]) + , new File(args[2]) + , new File(args[3]) + , new File(args[4]) + , new File(args[5]) + , args.length > 6 ? new File(args[6]) : null + ); + } + + private BExpressionContextWay _expctxWay; + private BExpressionContextNode _expctxNode; + + // private BExpressionContextWay _expctxWayStat; + // private BExpressionContextNode _expctxNodeStat; + + public void process(File lookupFile, File outTileDir, File wayFile, File relFile, File resFile, File profileFile, File mapFile) throws Exception { + if (!lookupFile.exists()) { + throw new IllegalArgumentException("lookup-file: " + lookupFile + " does not exist"); + } + + BExpressionMetaData meta = new BExpressionMetaData(); + + _expctxWay = new BExpressionContextWay(meta); + _expctxNode = new BExpressionContextNode(meta); + meta.readMetaData(lookupFile); + _expctxWay.parseFile(profileFile, "global"); + + + // _expctxWayStat = new BExpressionContextWay( null ); + // _expctxNodeStat = new BExpressionContextNode( null ); + + this.outTileDir = outTileDir; + if (!outTileDir.isDirectory()) + throw new RuntimeException("out tile directory " + outTileDir + " does not exist"); + + wayDos = wayFile == null ? null : new DataOutputStream(new BufferedOutputStream(new FileOutputStream(wayFile))); + cyclewayDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(relFile))); + if (resFile != null) { + restrictionsDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(resFile))); + } + + // read the osm map into memory + long t0 = System.currentTimeMillis(); + new OsmParser().readMap(mapFile, this, this, this); + long t1 = System.currentTimeMillis(); + + System.out.println("parsing time (ms) =" + (t1 - t0)); + + // close all files + closeTileOutStreams(); + if (wayDos != null) { + wayDos.close(); + } + cyclewayDos.close(); + if (restrictionsDos != null) { + restrictionsDos.close(); + } + +// System.out.println( "-------- way-statistics -------- " ); +// _expctxWayStat.dumpStatistics(); +// System.out.println( "-------- node-statistics -------- " ); +// _expctxNodeStat.dumpStatistics(); + + System.out.println(statsLine()); + } + + private void checkStats() { + if ((++recordCnt % 100000) == 0) System.out.println(statsLine()); + } + + private String statsLine() { + return "records read: " + recordCnt + " nodes=" + nodesParsed + " ways=" + waysParsed + " rels=" + relsParsed + " changesets=" + changesetsParsed; + } + + + @Override + public void nextNode(NodeData n) throws Exception { + nodesParsed++; + checkStats(); + + if (n.getTagsOrNull() != null) { + int[] lookupData = _expctxNode.createNewLookupData(); + for (Map.Entry e : n.getTagsOrNull().entrySet()) { + _expctxNode.addLookupValue(e.getKey(), e.getValue(), lookupData); + // _expctxNodeStat.addLookupValue( key, value, null ); + } + n.description = _expctxNode.encode(lookupData); + } + // write node to file + int tileIndex = getTileIndex(n.ilon, n.ilat); + if (tileIndex >= 0) { + n.writeTo(getOutStreamForTile(tileIndex)); + if (wayCutter != null) { + wayCutter.nextNode(n); + } + } + } + + + private void generatePseudoTags(HashMap map) { + // add pseudo.tags for concrete:lanes and concrete:plates + + String concrete = null; + for (Map.Entry e : map.entrySet()) { + String key = e.getKey(); + + if ("concrete".equals(key)) { + return; + } + if ("surface".equals(key)) { + String value = e.getValue(); + if (value.startsWith("concrete:")) { + concrete = value.substring("concrete:".length()); + } + } + } + if (concrete != null) { + map.put("concrete", concrete); + } + } + + + @Override + public void nextWay(WayData w) throws Exception { + waysParsed++; + checkStats(); + + // encode tags + if (w.getTagsOrNull() == null) return; + + generatePseudoTags(w.getTagsOrNull()); + + int[] lookupData = _expctxWay.createNewLookupData(); + for (String key : w.getTagsOrNull().keySet()) { + String value = w.getTag(key); + _expctxWay.addLookupValue(key, value.replace(' ', '_'), lookupData); + // _expctxWayStat.addLookupValue( key, value, null ); + } + w.description = _expctxWay.encode(lookupData); + + if (w.description == null) return; + + // filter according to profile + _expctxWay.evaluate(false, w.description); + boolean ok = _expctxWay.getCostfactor() < 10000.; + _expctxWay.evaluate(true, w.description); + ok |= _expctxWay.getCostfactor() < 10000.; + if (!ok) return; + + if (wayDos != null) { + w.writeTo(wayDos); + } + if (wayCutter != null) { + wayCutter.nextWay(w); + } + if (nodeFilter != null) { + nodeFilter.nextWay(w); + } + } + + @Override + public void nextRelation(RelationData r) throws Exception { + relsParsed++; + checkStats(); + + String route = r.getTag("route"); + // filter out non-cycle relations + if (route == null) { + return; + } + + String network = r.getTag("network"); + if (network == null) network = ""; + String state = r.getTag("state"); + if (state == null) state = ""; + writeId(cyclewayDos, r.rid); + cyclewayDos.writeUTF(route); + cyclewayDos.writeUTF(network); + cyclewayDos.writeUTF(state); + for (int i = 0; i < r.ways.size(); i++) { + long wid = r.ways.get(i); + writeId(cyclewayDos, wid); + } + writeId(cyclewayDos, -1); + } + + @Override + public void nextRestriction(RelationData r, long fromWid, long toWid, long viaNid) throws Exception { + String type = r.getTag("type"); + if (type == null || !"restriction".equals(type)) { + return; + } + short exceptions = 0; + String except = r.getTag("except"); + if (except != null) { + exceptions |= toBit("bicycle", 0, except); + exceptions |= toBit("motorcar", 1, except); + exceptions |= toBit("agricultural", 2, except); + exceptions |= toBit("forestry", 2, except); + exceptions |= toBit("psv", 3, except); + exceptions |= toBit("hgv", 4, except); + } + + for (String restrictionKey : r.getTagsOrNull().keySet()) { + if (!(restrictionKey.equals("restriction") || restrictionKey.startsWith("restriction:"))) { + continue; + } + String restriction = r.getTag(restrictionKey); + + RestrictionData res = new RestrictionData(); + res.restrictionKey = restrictionKey; + res.restriction = restriction; + res.exceptions = exceptions; + res.fromWid = fromWid; + res.toWid = toWid; + res.viaNid = viaNid; + + if (restrictionsDos != null) { + res.writeTo(restrictionsDos); + } + if (restrictionCutter != null) { + restrictionCutter.nextRestriction(res); + } + } + } + + private static short toBit(String tag, int bitpos, String s) { + return (short) (s.indexOf(tag) < 0 ? 0 : 1 << bitpos); + } + + private int getTileIndex(int ilon, int ilat) { + int lon = ilon / 45000000; + int lat = ilat / 30000000; + if (lon < 0 || lon > 7 || lat < 0 || lat > 5) { + System.out.println("warning: ignoring illegal pos: " + ilon + "," + ilat); + return -1; + } + return lon * 6 + lat; + } + + protected String getNameForTile(int tileIndex) { + int lon = (tileIndex / 6) * 45 - 180; + int lat = (tileIndex % 6) * 30 - 90; + String slon = lon < 0 ? "W" + (-lon) : "E" + lon; + String slat = lat < 0 ? "S" + (-lat) : "N" + lat; + return slon + "_" + slat + ".ntl"; + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmFastCutter.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmFastCutter.java index fcb05c1..6c4a725 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmFastCutter.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmFastCutter.java @@ -9,55 +9,51 @@ package btools.mapcreator; import java.io.File; -public class OsmFastCutter extends MapCreatorBase -{ - public static void main(String[] args) throws Exception - { +public class OsmFastCutter extends MapCreatorBase { + public static void main(String[] args) throws Exception { System.out.println("*** OsmFastCutter: cut an osm map in node-tiles + way-tiles"); - if (args.length != 11 && args.length != 12) - { + if (args.length != 11 && args.length != 12) { String common = "java OsmFastCutter "; - - System.out.println("usage: bzip2 -dc | " + common ); - System.out.println("or : " + common + " " ); + + System.out.println("usage: bzip2 -dc | " + common); + System.out.println("or : " + common + " "); return; } doCut( - new File( args[0] ) - , new File( args[1] ) - , new File( args[2] ) - , new File( args[3] ) - , new File( args[4] ) - , new File( args[5] ) - , new File( args[6] ) - , new File( args[7] ) - , new File( args[8] ) - , new File( args[9] ) - , new File( args[10] ) - , args.length > 11 ? new File( args[11] ) : null - ); + new File(args[0]) + , new File(args[1]) + , new File(args[2]) + , new File(args[3]) + , new File(args[4]) + , new File(args[5]) + , new File(args[6]) + , new File(args[7]) + , new File(args[8]) + , new File(args[9]) + , new File(args[10]) + , args.length > 11 ? new File(args[11]) : null + ); } - public static void doCut (File lookupFile, File nodeDir, File wayDir, File node55Dir, File way55Dir, File borderFile, File relFile, File resFile, File profileAll, File profileReport, File profileCheck, File mapFile ) throws Exception - { + public static void doCut(File lookupFile, File nodeDir, File wayDir, File node55Dir, File way55Dir, File borderFile, File relFile, File resFile, File profileAll, File profileReport, File profileCheck, File mapFile) throws Exception { // **** run OsmCutter **** OsmCutter cutter = new OsmCutter(); // ... inject WayCutter cutter.wayCutter = new WayCutter(); - cutter.wayCutter.init( wayDir ); + cutter.wayCutter.init(wayDir); // ... inject RestrictionCutter cutter.restrictionCutter = new RestrictionCutter(); - cutter.restrictionCutter.init( new File( nodeDir.getParentFile(), "restrictions" ), cutter.wayCutter ); + cutter.restrictionCutter.init(new File(nodeDir.getParentFile(), "restrictions"), cutter.wayCutter); // ... inject NodeFilter NodeFilter nodeFilter = new NodeFilter(); nodeFilter.init(); cutter.nodeFilter = nodeFilter; - cutter.process( lookupFile, nodeDir, null, relFile, null, profileAll, mapFile ); + cutter.process(lookupFile, nodeDir, null, relFile, null, profileAll, mapFile); cutter.wayCutter.finish(); cutter.restrictionCutter.finish(); cutter = null; @@ -67,20 +63,20 @@ public class OsmFastCutter extends MapCreatorBase //... inject RelationMerger wayCut5.relMerger = new RelationMerger(); - wayCut5.relMerger.init( relFile, lookupFile, profileReport, profileCheck ); + wayCut5.relMerger.init(relFile, lookupFile, profileReport, profileCheck); // ... inject RestrictionCutter5 wayCut5.restrictionCutter5 = new RestrictionCutter5(); - wayCut5.restrictionCutter5.init( new File( nodeDir.getParentFile(), "restrictions55" ), wayCut5 ); + wayCut5.restrictionCutter5.init(new File(nodeDir.getParentFile(), "restrictions55"), wayCut5); //... inject NodeFilter wayCut5.nodeFilter = nodeFilter; // ... inject NodeCutter wayCut5.nodeCutter = new NodeCutter(); - wayCut5.nodeCutter.init( node55Dir ); + wayCut5.nodeCutter.init(node55Dir); - wayCut5.process( nodeDir, wayDir, way55Dir, borderFile ); + wayCut5.process(nodeDir, wayDir, way55Dir, borderFile); } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java index bce6451..a9d4c9b 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java @@ -1,118 +1,90 @@ -/** - * Container for link between two Osm nodes (pre-pocessor version) - * - * @author ab - */ -package btools.mapcreator; - - -public class OsmLinkP -{ - /** - * The description bitmap is mainly the way description - * used to calculate the costfactor - */ - public byte[] descriptionBitmap; - - /** - * The target is either the next link or the target node - */ - protected OsmNodeP sourceNode; - protected OsmNodeP targetNode; - - protected OsmLinkP previous; - protected OsmLinkP next; - - - public OsmLinkP( OsmNodeP source, OsmNodeP target ) - { - sourceNode = source; - targetNode = target; - } - - protected OsmLinkP() - { - } - - public final boolean counterLinkWritten( ) - { - return descriptionBitmap == null; - } - - /** - * Set the relevant next-pointer for the given source - */ - public void setNext( OsmLinkP link, OsmNodeP source ) - { - if ( sourceNode == source ) - { - next = link; - } - else if ( targetNode == source ) - { - previous = link; - } - else - { - throw new IllegalArgumentException( "internal error: setNext: unknown source" ); - } - } - - /** - * Get the relevant next-pointer for the given source - */ - public OsmLinkP getNext( OsmNodeP source ) - { - if ( sourceNode == source ) - { - return next; - } - else if ( targetNode == source ) - { - return previous; - } - else - { - throw new IllegalArgumentException( "internal error: gextNext: unknown source" ); - } - } - - /** - * Get the relevant target-node for the given source - */ - public OsmNodeP getTarget( OsmNodeP source ) - { - if ( sourceNode == source ) - { - return targetNode; - } - else if ( targetNode == source ) - { - return sourceNode; - } - else - { - throw new IllegalArgumentException( "internal error: getTarget: unknown source" ); - } - } - - /** - * Check if reverse link for the given source - */ - public boolean isReverse( OsmNodeP source ) - { - if ( sourceNode == source ) - { - return false; - } - else if ( targetNode == source ) - { - return true; - } - else - { - throw new IllegalArgumentException( "internal error: isReverse: unknown source" ); - } - } - -} +/** + * Container for link between two Osm nodes (pre-pocessor version) + * + * @author ab + */ +package btools.mapcreator; + + +public class OsmLinkP { + /** + * The description bitmap is mainly the way description + * used to calculate the costfactor + */ + public byte[] descriptionBitmap; + + /** + * The target is either the next link or the target node + */ + protected OsmNodeP sourceNode; + protected OsmNodeP targetNode; + + protected OsmLinkP previous; + protected OsmLinkP next; + + + public OsmLinkP(OsmNodeP source, OsmNodeP target) { + sourceNode = source; + targetNode = target; + } + + protected OsmLinkP() { + } + + public final boolean counterLinkWritten() { + return descriptionBitmap == null; + } + + /** + * Set the relevant next-pointer for the given source + */ + public void setNext(OsmLinkP link, OsmNodeP source) { + if (sourceNode == source) { + next = link; + } else if (targetNode == source) { + previous = link; + } else { + throw new IllegalArgumentException("internal error: setNext: unknown source"); + } + } + + /** + * Get the relevant next-pointer for the given source + */ + public OsmLinkP getNext(OsmNodeP source) { + if (sourceNode == source) { + return next; + } else if (targetNode == source) { + return previous; + } else { + throw new IllegalArgumentException("internal error: gextNext: unknown source"); + } + } + + /** + * Get the relevant target-node for the given source + */ + public OsmNodeP getTarget(OsmNodeP source) { + if (sourceNode == source) { + return targetNode; + } else if (targetNode == source) { + return sourceNode; + } else { + throw new IllegalArgumentException("internal error: getTarget: unknown source"); + } + } + + /** + * Check if reverse link for the given source + */ + public boolean isReverse(OsmNodeP source) { + if (sourceNode == source) { + return false; + } else if (targetNode == source) { + return true; + } else { + throw new IllegalArgumentException("internal error: isReverse: unknown source"); + } + } + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java index 92b1b54..44247e1 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java @@ -12,8 +12,7 @@ import java.util.HashMap; import btools.codec.MicroCache; import btools.codec.MicroCache2; -public class OsmNodeP extends OsmLinkP -{ +public class OsmNodeP extends OsmLinkP { /** * The latitude */ @@ -40,311 +39,261 @@ public class OsmNodeP extends OsmLinkP public byte bits = 0; // interface OsmPos - public int getILat() - { + public int getILat() { return ilat; } - public int getILon() - { + public int getILon() { return ilon; } - public short getSElev() - { + public short getSElev() { // if all bridge or all tunnel, elevation=no-data - return ( bits & NO_BRIDGE_BIT ) == 0 || ( bits & NO_TUNNEL_BIT ) == 0 ? Short.MIN_VALUE : selev; + return (bits & NO_BRIDGE_BIT) == 0 || (bits & NO_TUNNEL_BIT) == 0 ? Short.MIN_VALUE : selev; } - public double getElev() - { + public double getElev() { return selev / 4.; } // populate and return the inherited link, if available, // else create a new one - public OsmLinkP createLink( OsmNodeP source ) - { - if ( sourceNode == null && targetNode == null ) - { + public OsmLinkP createLink(OsmNodeP source) { + if (sourceNode == null && targetNode == null) { // inherited instance is available, use this sourceNode = source; targetNode = this; - source.addLink( this ); + source.addLink(this); return this; } - OsmLinkP link = new OsmLinkP( source, this ); - addLink( link ); - source.addLink( link ); + OsmLinkP link = new OsmLinkP(source, this); + addLink(link); + source.addLink(link); return link; } // memory-squeezing-hack: OsmLinkP's "previous" also used as firstlink.. - public void addLink( OsmLinkP link ) - { - link.setNext( previous, this ); + public void addLink(OsmLinkP link) { + link.setNext(previous, this); previous = link; } - public OsmLinkP getFirstLink() - { + public OsmLinkP getFirstLink() { return sourceNode == null && targetNode == null ? previous : this; } - public byte[] getNodeDecsription() - { + public byte[] getNodeDecsription() { return null; } - public RestrictionData getFirstRestriction() - { + public RestrictionData getFirstRestriction() { return null; } - public void writeNodeData( MicroCache mc, OsmTrafficMap trafficMap ) throws IOException - { + public void writeNodeData(MicroCache mc, OsmTrafficMap trafficMap) throws IOException { boolean valid = true; - if ( mc instanceof MicroCache2 ) - { - valid = writeNodeData2( (MicroCache2) mc, trafficMap ); - } - else - throw new IllegalArgumentException( "unknown cache version: " + mc.getClass() ); - if ( valid ) - { - mc.finishNode( getIdFromPos() ); - } - else - { + if (mc instanceof MicroCache2) { + valid = writeNodeData2((MicroCache2) mc, trafficMap); + } else + throw new IllegalArgumentException("unknown cache version: " + mc.getClass()); + if (valid) { + mc.finishNode(getIdFromPos()); + } else { mc.discardNode(); } } - public void checkDuplicateTargets() - { - HashMap targets = new HashMap(); + public void checkDuplicateTargets() { + HashMap targets = new HashMap(); - for ( OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext( this ) ) - { + for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) { OsmLinkP link = link0; OsmNodeP origin = this; OsmNodeP target = null; // first pass just to see if that link is consistent - while (link != null) - { - target = link.getTarget( origin ); - if ( !target.isTransferNode() ) - { + while (link != null) { + target = link.getTarget(origin); + if (!target.isTransferNode()) { break; } // next link is the one (of two), does does'nt point back - for ( link = target.getFirstLink(); link != null; link = link.getNext( target ) ) - { - if ( link.getTarget( target ) != origin ) + for (link = target.getFirstLink(); link != null; link = link.getNext(target)) { + if (link.getTarget(target) != origin) break; } origin = target; } - if ( link == null ) continue; - OsmLinkP oldLink = targets.put( target, link0 ); - if ( oldLink != null ) - { - unifyLink( oldLink ); - unifyLink( link0 ); + if (link == null) continue; + OsmLinkP oldLink = targets.put(target, link0); + if (oldLink != null) { + unifyLink(oldLink); + unifyLink(link0); } } } - private void unifyLink( OsmLinkP link ) - { - if ( link.isReverse( this ) ) return; - OsmNodeP target = link.getTarget( this ); - if ( target.isTransferNode() ) - { + private void unifyLink(OsmLinkP link) { + if (link.isReverse(this)) return; + OsmNodeP target = link.getTarget(this); + if (target.isTransferNode()) { target.incWayCount(); } } - public boolean writeNodeData2( MicroCache2 mc, OsmTrafficMap trafficMap ) throws IOException - { + public boolean writeNodeData2(MicroCache2 mc, OsmTrafficMap trafficMap) throws IOException { boolean hasLinks = false; - + // write turn restrictions RestrictionData r = getFirstRestriction(); - while( r != null ) - { - if ( r.isValid() && r.fromLon != 0 && r.toLon != 0 ) - { - mc.writeBoolean( true ); // restriction follows - mc.writeShort( r.exceptions ); - mc.writeBoolean( r.isPositive() ); - mc.writeInt( r.fromLon ); - mc.writeInt( r.fromLat ); - mc.writeInt( r.toLon ); - mc.writeInt( r.toLat ); + while (r != null) { + if (r.isValid() && r.fromLon != 0 && r.toLon != 0) { + mc.writeBoolean(true); // restriction follows + mc.writeShort(r.exceptions); + mc.writeBoolean(r.isPositive()); + mc.writeInt(r.fromLon); + mc.writeInt(r.fromLat); + mc.writeInt(r.toLon); + mc.writeInt(r.toLat); } r = r.next; } - mc.writeBoolean( false ); // end restritions + mc.writeBoolean(false); // end restritions - mc.writeShort( getSElev() ); - mc.writeVarBytes( getNodeDecsription() ); + mc.writeShort(getSElev()); + mc.writeVarBytes(getNodeDecsription()); // buffer internal reverse links ArrayList internalReverse = new ArrayList(); - for ( OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext( this ) ) - { + for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) { OsmLinkP link = link0; OsmNodeP origin = this; OsmNodeP target = null; ArrayList linkNodes = new ArrayList(); - linkNodes.add( this ); + linkNodes.add(this); // first pass just to see if that link is consistent - while (link != null) - { - target = link.getTarget( origin ); - linkNodes.add( target ); + while (link != null) { + target = link.getTarget(origin); + linkNodes.add(target); - if ( !target.isTransferNode() ) - { + if (!target.isTransferNode()) { break; } // next link is the one (of two), does does'nt point back - for ( link = target.getFirstLink(); link != null; link = link.getNext( target ) ) - { - if ( link.getTarget( target ) != origin ) + for (link = target.getFirstLink(); link != null; link = link.getNext(target)) { + if (link.getTarget(target) != origin) break; } - if ( link != null && link.descriptionBitmap != link0.descriptionBitmap ) - { - throw new IllegalArgumentException( "assertion failed: description change along transfer nodes" ); + if (link != null && link.descriptionBitmap != link0.descriptionBitmap) { + throw new IllegalArgumentException("assertion failed: description change along transfer nodes"); } origin = target; } - if ( link == null ) + if (link == null) continue; // dead end - if ( target == this ) + if (target == this) continue; // self-ref hasLinks = true; // internal reverse links later - boolean isReverse = link0.isReverse( this ); - if ( isReverse ) - { - if ( mc.isInternal( target.ilon, target.ilat ) ) - { - internalReverse.add( target ); + boolean isReverse = link0.isReverse(this); + if (isReverse) { + if (mc.isInternal(target.ilon, target.ilat)) { + internalReverse.add(target); continue; } } // add traffic simulation, if present byte[] description = link0.descriptionBitmap; - if ( trafficMap != null ) - { - description = trafficMap.addTrafficClass( linkNodes, description ); + if (trafficMap != null) { + description = trafficMap.addTrafficClass(linkNodes, description); } // write link data int sizeoffset = mc.writeSizePlaceHolder(); - mc.writeVarLengthSigned( target.ilon - ilon ); - mc.writeVarLengthSigned( target.ilat - ilat ); - mc.writeModeAndDesc( isReverse, description ); - if ( !isReverse && linkNodes.size() > 2 ) // write geometry for forward links only + mc.writeVarLengthSigned(target.ilon - ilon); + mc.writeVarLengthSigned(target.ilat - ilat); + mc.writeModeAndDesc(isReverse, description); + if (!isReverse && linkNodes.size() > 2) // write geometry for forward links only { - DPFilter.doDPFilter( linkNodes ); + DPFilter.doDPFilter(linkNodes); origin = this; - for( int i=1; i 0) - { + while (internalReverse.size() > 0) { int nextIdx = 0; - if ( internalReverse.size() > 1 ) - { + if (internalReverse.size() > 1) { int max32 = Integer.MIN_VALUE; - for ( int i = 0; i < internalReverse.size(); i++ ) - { - int id32 = mc.shrinkId( internalReverse.get( i ).getIdFromPos() ); - if ( id32 > max32 ) - { + for (int i = 0; i < internalReverse.size(); i++) { + int id32 = mc.shrinkId(internalReverse.get(i).getIdFromPos()); + if (id32 > max32) { max32 = id32; nextIdx = i; } } } - OsmNodeP target = internalReverse.remove( nextIdx ); + OsmNodeP target = internalReverse.remove(nextIdx); int sizeoffset = mc.writeSizePlaceHolder(); - mc.writeVarLengthSigned( target.ilon - ilon ); - mc.writeVarLengthSigned( target.ilat - ilat ); - mc.writeModeAndDesc( true, null ); - mc.injectSize( sizeoffset ); + mc.writeVarLengthSigned(target.ilon - ilon); + mc.writeVarLengthSigned(target.ilat - ilat); + mc.writeModeAndDesc(true, null); + mc.injectSize(sizeoffset); } return hasLinks; } - public String toString2() - { - return ( ilon - 180000000 ) + "_" + ( ilat - 90000000 ) + "_" + ( selev / 4 ); + public String toString2() { + return (ilon - 180000000) + "_" + (ilat - 90000000) + "_" + (selev / 4); } - public long getIdFromPos() - { - return ( (long) ilon ) << 32 | ilat; + public long getIdFromPos() { + return ((long) ilon) << 32 | ilat; } - public boolean isBorderNode() - { - return ( bits & BORDER_BIT ) != 0; + public boolean isBorderNode() { + return (bits & BORDER_BIT) != 0; } - public boolean hasTraffic() - { - return ( bits & TRAFFIC_BIT ) != 0; + public boolean hasTraffic() { + return (bits & TRAFFIC_BIT) != 0; } /** * Not really count the ways, just detect if more than one */ - public void incWayCount() - { - if ( ( bits & ANY_WAY_BIT ) != 0 ) - { + public void incWayCount() { + if ((bits & ANY_WAY_BIT) != 0) { bits |= MULTI_WAY_BIT; } bits |= ANY_WAY_BIT; } - public boolean isTransferNode() - { - return ( bits & BORDER_BIT ) == 0 && ( bits & MULTI_WAY_BIT ) == 0 && _linkCnt() == 2; + public boolean isTransferNode() { + return (bits & BORDER_BIT) == 0 && (bits & MULTI_WAY_BIT) == 0 && _linkCnt() == 2; } - private int _linkCnt() - { + private int _linkCnt() { int cnt = 0; - for ( OsmLinkP link = getFirstLink(); link != null; link = link.getNext( this ) ) - { + for (OsmLinkP link = getFirstLink(); link != null; link = link.getNext(this)) { cnt++; } return cnt; diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodePT.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodePT.java index 4286671..3387bb5 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodePT.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodePT.java @@ -1,50 +1,43 @@ -/** - * Container for an osm node with tags or restrictions (pre-pocessor version) - * - * @author ab - */ -package btools.mapcreator; - - -public class OsmNodePT extends OsmNodeP -{ - public byte[] descriptionBits; - - public RestrictionData firstRestriction; - - public OsmNodePT() - { - } - - public OsmNodePT( OsmNodeP n ) - { - ilat = n.ilat; - ilon = n.ilon; - selev = n.selev; - bits = n.bits; - } - - public OsmNodePT( byte[] descriptionBits ) - { - this.descriptionBits = descriptionBits; - } - - @Override - public final byte[] getNodeDecsription() - { - return descriptionBits; - } - - @Override - public final RestrictionData getFirstRestriction() - { - return firstRestriction; - } - - @Override - public boolean isTransferNode() - { - return false; // always have descriptionBits so never transfernode - } - -} +/** + * Container for an osm node with tags or restrictions (pre-pocessor version) + * + * @author ab + */ +package btools.mapcreator; + + +public class OsmNodePT extends OsmNodeP { + public byte[] descriptionBits; + + public RestrictionData firstRestriction; + + public OsmNodePT() { + } + + public OsmNodePT(OsmNodeP n) { + ilat = n.ilat; + ilon = n.ilon; + selev = n.selev; + bits = n.bits; + } + + public OsmNodePT(byte[] descriptionBits) { + this.descriptionBits = descriptionBits; + } + + @Override + public final byte[] getNodeDecsription() { + return descriptionBits; + } + + @Override + public final RestrictionData getFirstRestriction() { + return firstRestriction; + } + + @Override + public boolean isTransferNode() { + return false; // always have descriptionBits so never transfernode + } + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmParser.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmParser.java index d5904dd..9539a51 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmParser.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmParser.java @@ -1,236 +1,200 @@ -package btools.mapcreator; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.util.zip.GZIPInputStream; - -/** - * Parser for OSM data - * - * @author ab - */ -public class OsmParser extends MapCreatorBase -{ - private BufferedReader _br; - - private NodeListener nListener; - private WayListener wListener; - private RelationListener rListener; - - public void readMap( File mapFile, - NodeListener nListener, - WayListener wListener, - RelationListener rListener ) throws Exception - { - - this.nListener = nListener; - this.wListener = wListener; - this.rListener = rListener; - - if ( mapFile == null ) - { - _br = new BufferedReader(new InputStreamReader(System.in)); - } - else - { - if ( mapFile.getName().endsWith( ".gz" ) ) - { - _br = new BufferedReader(new InputStreamReader( new GZIPInputStream( new FileInputStream( mapFile ) ) ) ); - } - else - { - _br = new BufferedReader(new InputStreamReader( new FileInputStream( mapFile ) ) ); - } - } - - for(;;) - { - String line = _br.readLine(); - if ( line == null ) break; - - if ( checkNode( line ) ) continue; - if ( checkWay( line ) ) continue; - if ( checkRelation( line ) ) continue; - if ( checkChangeset( line ) ) continue; - } - - if ( mapFile != null ) - { - _br.close(); - } - } - - - private boolean checkNode( String line ) throws Exception - { - int idx0 = line.indexOf( "" ) ) - { - // read additional tags - for(;;) - { - String l2 = _br.readLine(); - if ( l2 == null ) return false; - - int i2; - if ( (i2 = l2.indexOf( "= 0 ) - { // property-tag - i2 += 8; - int ri2 = l2.indexOf( '"', i2 ); - String key = l2.substring( i2, ri2 ); - i2 = l2.indexOf( " v=\"", ri2 ); - if ( i2 >= 0 ) - { - i2 += 4; - int ri3 = l2.indexOf( '"', i2 ); - String value = l2.substring( i2, ri3 ); - - n.putTag( key, value ); - } - } - else if ( l2.indexOf( "" ) >= 0 ) - { // end-tag - break; - } - } - } - nListener.nextNode( n ); - return true; - } - - - private boolean checkWay( String line ) throws Exception - { - int idx0 = line.indexOf( "= 0 ) - { // node reference - i2 += 9; - int ri2 = l2.indexOf( '"', i2 ); - long nid = Long.parseLong( l2.substring( i2, ri2 ) ); - w.nodes.add( nid ); - } - else if ( (i2 = l2.indexOf( "= 0 ) - { // property-tag - i2 += 8; - int ri2 = l2.indexOf( '"', i2 ); - String key = l2.substring( i2, ri2 ); - i2 = l2.indexOf( " v=\"", ri2 ); - if ( i2 >= 0 ) - { - i2 += 4; - int ri3 = l2.indexOf( '"', i2 ); - String value = l2.substring( i2, ri3 ); - w.putTag( key, value ); - } - } - else if ( l2.indexOf( "" ) >= 0 ) - { // end-tag - break; - } - } - wListener.nextWay( w ); - return true; - } - - private boolean checkChangeset( String line ) throws Exception - { - int idx0 = line.indexOf( "" ) ) - { - int loopcheck = 0; - for(;;) - { - String l2 = _br.readLine(); - if ( l2.indexOf("") >= 0 || ++loopcheck > 10000 ) break; - } - } - return true; - } - - private boolean checkRelation( String line ) throws Exception - { - int idx0 = line.indexOf( "= 0 ) - { // node reference - i2 += 24; - int ri2 = l2.indexOf( '"', i2 ); - long wid = Long.parseLong( l2.substring( i2, ri2 ) ); - r.ways.add( wid ); - } - else if ( (i2 = l2.indexOf( "= 0 ) - { // property-tag - i2 += 8; - int ri2 = l2.indexOf( '"', i2 ); - String key = l2.substring( i2, ri2 ); - i2 = l2.indexOf( " v=\"", ri2 ); - if ( i2 >= 0 ) - { - i2 += 4; - int ri3 = l2.indexOf( '"', i2 ); - String value = l2.substring( i2, ri3 ); - r.putTag( key, value ); - } - } - else if ( l2.indexOf( "" ) >= 0 ) - { // end-tag - break; - } - } - rListener.nextRelation( r ); - return true; - } - -} +package btools.mapcreator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.zip.GZIPInputStream; + +/** + * Parser for OSM data + * + * @author ab + */ +public class OsmParser extends MapCreatorBase { + private BufferedReader _br; + + private NodeListener nListener; + private WayListener wListener; + private RelationListener rListener; + + public void readMap(File mapFile, + NodeListener nListener, + WayListener wListener, + RelationListener rListener) throws Exception { + + this.nListener = nListener; + this.wListener = wListener; + this.rListener = rListener; + + if (mapFile == null) { + _br = new BufferedReader(new InputStreamReader(System.in)); + } else { + if (mapFile.getName().endsWith(".gz")) { + _br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(mapFile)))); + } else { + _br = new BufferedReader(new InputStreamReader(new FileInputStream(mapFile))); + } + } + + for (; ; ) { + String line = _br.readLine(); + if (line == null) break; + + if (checkNode(line)) continue; + if (checkWay(line)) continue; + if (checkRelation(line)) continue; + if (checkChangeset(line)) continue; + } + + if (mapFile != null) { + _br.close(); + } + } + + + private boolean checkNode(String line) throws Exception { + int idx0 = line.indexOf("")) { + // read additional tags + for (; ; ) { + String l2 = _br.readLine(); + if (l2 == null) return false; + + int i2; + if ((i2 = l2.indexOf("= 0) { // property-tag + i2 += 8; + int ri2 = l2.indexOf('"', i2); + String key = l2.substring(i2, ri2); + i2 = l2.indexOf(" v=\"", ri2); + if (i2 >= 0) { + i2 += 4; + int ri3 = l2.indexOf('"', i2); + String value = l2.substring(i2, ri3); + + n.putTag(key, value); + } + } else if (l2.indexOf("") >= 0) { // end-tag + break; + } + } + } + nListener.nextNode(n); + return true; + } + + + private boolean checkWay(String line) throws Exception { + int idx0 = line.indexOf("= 0) { // node reference + i2 += 9; + int ri2 = l2.indexOf('"', i2); + long nid = Long.parseLong(l2.substring(i2, ri2)); + w.nodes.add(nid); + } else if ((i2 = l2.indexOf("= 0) { // property-tag + i2 += 8; + int ri2 = l2.indexOf('"', i2); + String key = l2.substring(i2, ri2); + i2 = l2.indexOf(" v=\"", ri2); + if (i2 >= 0) { + i2 += 4; + int ri3 = l2.indexOf('"', i2); + String value = l2.substring(i2, ri3); + w.putTag(key, value); + } + } else if (l2.indexOf("") >= 0) { // end-tag + break; + } + } + wListener.nextWay(w); + return true; + } + + private boolean checkChangeset(String line) throws Exception { + int idx0 = line.indexOf("")) { + int loopcheck = 0; + for (; ; ) { + String l2 = _br.readLine(); + if (l2.indexOf("") >= 0 || ++loopcheck > 10000) break; + } + } + return true; + } + + private boolean checkRelation(String line) throws Exception { + int idx0 = line.indexOf("= 0) { // node reference + i2 += 24; + int ri2 = l2.indexOf('"', i2); + long wid = Long.parseLong(l2.substring(i2, ri2)); + r.ways.add(wid); + } else if ((i2 = l2.indexOf("= 0) { // property-tag + i2 += 8; + int ri2 = l2.indexOf('"', i2); + String key = l2.substring(i2, ri2); + i2 = l2.indexOf(" v=\"", ri2); + if (i2 >= 0) { + i2 += 4; + int ri3 = l2.indexOf('"', i2); + String value = l2.substring(i2, ri3); + r.putTag(key, value); + } + } else if (l2.indexOf("") >= 0) { // end-tag + break; + } + } + rListener.nextRelation(r); + return true; + } + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java index a4ab68e..f387851 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java @@ -22,8 +22,7 @@ import btools.util.CompactLongMap; import btools.util.FrozenLongMap; -public class OsmTrafficMap -{ +public class OsmTrafficMap { int minLon; int minLat; int maxLon; @@ -38,243 +37,206 @@ public class OsmTrafficMap private int totalChanges = 0; private int supressedChanges = 0; - + private boolean doNotAdd = false; private boolean debug = false; - public OsmTrafficMap( BExpressionContextWay expctxWay ) - { + public OsmTrafficMap(BExpressionContextWay expctxWay) { this.expctxWay = expctxWay; - debug = Boolean.getBoolean( "debugTrafficMap" ); + debug = Boolean.getBoolean("debugTrafficMap"); } - public static class OsmTrafficElement - { + public static class OsmTrafficElement { public long node2; public int traffic; public OsmTrafficElement next; } - + private CompactLongMap map = new CompactLongMap(); - public void loadAll( File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways ) throws Exception - { - load( file, minLon, minLat, maxLon, maxLat, includeMotorways ); - + public void loadAll(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { + load(file, minLon, minLat, maxLon, maxLat, includeMotorways); + // check for old traffic data - oldTrafficFile = new File( file.getParentFile(), file.getName() + "_old" ); - if ( oldTrafficFile.exists() ) - { - oldTrafficClasses = new OsmTrafficMap( null ); + oldTrafficFile = new File(file.getParentFile(), file.getName() + "_old"); + if (oldTrafficFile.exists()) { + oldTrafficClasses = new OsmTrafficMap(null); oldTrafficClasses.doNotAdd = true; - oldTrafficClasses.load( oldTrafficFile, minLon, minLat, maxLon, maxLat, false ); + oldTrafficClasses.load(oldTrafficFile, minLon, minLat, maxLon, maxLat, false); } - + // check for old traffic data - newTrafficFile = new File( file.getParentFile(), file.getName() + "_new" ); - newTrafficDos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( newTrafficFile ) ) ); + newTrafficFile = new File(file.getParentFile(), file.getName() + "_new"); + newTrafficDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(newTrafficFile))); } - public void finish() throws Exception - { - if ( newTrafficDos != null ) - { + public void finish() throws Exception { + if (newTrafficDos != null) { newTrafficDos.close(); newTrafficDos = null; oldTrafficFile.delete(); - newTrafficFile.renameTo( oldTrafficFile ); - System.out.println( "TrafficMap: changes total=" + totalChanges + " supressed=" + supressedChanges ); + newTrafficFile.renameTo(oldTrafficFile); + System.out.println("TrafficMap: changes total=" + totalChanges + " supressed=" + supressedChanges); } } - public void load( File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways ) throws Exception - { + public void load(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { this.minLon = minLon; this.minLat = minLat; this.maxLon = maxLon; this.maxLat = maxLat; - int trafficElements = 0; - DataInputStream is = new DataInputStream( new BufferedInputStream( new FileInputStream( file ) ) ); - try - { - for(;;) - { - long n1 = is.readLong(); - long n2 = is.readLong(); - int traffic = is.readInt(); - if ( traffic == -1 && !includeMotorways ) - { - continue; - } - if ( isInsideBounds( n1 ) || isInsideBounds( n2 ) ) - { - if ( addElement( n1, n2, traffic ) ) - { - trafficElements++; - } + int trafficElements = 0; + DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); + try { + for (; ; ) { + long n1 = is.readLong(); + long n2 = is.readLong(); + int traffic = is.readInt(); + if (traffic == -1 && !includeMotorways) { + continue; + } + if (isInsideBounds(n1) || isInsideBounds(n2)) { + if (addElement(n1, n2, traffic)) { + trafficElements++; } } } - catch( EOFException eof ) {} - finally{ is.close(); } - - map = new FrozenLongMap( map ); - System.out.println( "read traffic-elements: " + trafficElements ); + } catch (EOFException eof) { + } finally { + is.close(); + } + + map = new FrozenLongMap(map); + System.out.println("read traffic-elements: " + trafficElements); } - public boolean addElement( long n1, long n2, int traffic ) - { - OsmTrafficElement e = getElement( n1, n2 ); - if ( e == null ) - { + public boolean addElement(long n1, long n2, int traffic) { + OsmTrafficElement e = getElement(n1, n2); + if (e == null) { e = new OsmTrafficElement(); e.node2 = n2; e.traffic = traffic; - OsmTrafficElement e0 = map.get( n1 ); - if ( e0 != null ) - { - while( e0.next != null ) - { + OsmTrafficElement e0 = map.get(n1); + if (e0 != null) { + while (e0.next != null) { e0 = e0.next; } e0.next = e; - } - else - { - map.fastPut( n1, e ); + } else { + map.fastPut(n1, e); } return true; } - if ( doNotAdd ) - { - e.traffic = Math.max( e.traffic, traffic ); - } - else - { + if (doNotAdd) { + e.traffic = Math.max(e.traffic, traffic); + } else { e.traffic = e.traffic == -1 || traffic == -1 ? -1 : e.traffic + traffic; } return false; } - - private boolean isInsideBounds( long id ) - { - int ilon = (int)(id >> 32); - int ilat = (int)(id & 0xffffffff); - + + private boolean isInsideBounds(long id) { + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + return ilon >= minLon && ilon < maxLon && ilat >= minLat && ilat < maxLat; } - public int getTrafficClass( long n1, long n2 ) - { + public int getTrafficClass(long n1, long n2) { // used for the old data, where we stpre traffic-classes, not volumes - OsmTrafficElement e = getElement( n1, n2 ); + OsmTrafficElement e = getElement(n1, n2); return e == null ? 0 : e.traffic; } - public int getTrafficClassForTraffic( int traffic ) - { - if ( traffic < 0 ) return -1; - if ( traffic < 40000 ) return 0; - if ( traffic < 80000 ) return 2; - if ( traffic < 160000 ) return 3; - if ( traffic < 320000 ) return 4; - if ( traffic < 640000 ) return 5; - if ( traffic <1280000 ) return 6; + public int getTrafficClassForTraffic(int traffic) { + if (traffic < 0) return -1; + if (traffic < 40000) return 0; + if (traffic < 80000) return 2; + if (traffic < 160000) return 3; + if (traffic < 320000) return 4; + if (traffic < 640000) return 5; + if (traffic < 1280000) return 6; return 7; } - private int getTraffic( long n1, long n2 ) - { - OsmTrafficElement e1 = getElement( n1, n2 ); + private int getTraffic(long n1, long n2) { + OsmTrafficElement e1 = getElement(n1, n2); int traffic1 = e1 == null ? 0 : e1.traffic; - OsmTrafficElement e2 = getElement( n2, n1 ); + OsmTrafficElement e2 = getElement(n2, n1); int traffic2 = e2 == null ? 0 : e2.traffic; return traffic1 == -1 || traffic2 == -1 ? -1 : traffic1 > traffic2 ? traffic1 : traffic2; } - public void freeze() - { + public void freeze() { } - - private OsmTrafficElement getElement( long n1, long n2 ) - { - OsmTrafficElement e = map.get( n1 ); - while( e != null ) - { - if ( e.node2 == n2 ) - { + + private OsmTrafficElement getElement(long n1, long n2) { + OsmTrafficElement e = map.get(n1); + while (e != null) { + if (e.node2 == n2) { return e; - } - e = e.next; + } + e = e.next; } return null; } - - public OsmTrafficElement getElement( long n ) - { - return map.get( n ); + + public OsmTrafficElement getElement(long n) { + return map.get(n); } - public byte[] addTrafficClass( ArrayList linkNodes, byte[] description ) throws IOException - { + public byte[] addTrafficClass(ArrayList linkNodes, byte[] description) throws IOException { double distance = 0.; double sum = 0.; - - for( int i=0; i" + trafficClass + " supress=" + supressChange ); + if (debug) { + System.out.println("traffic class change " + oldTrafficClass + "->" + trafficClass + " supress=" + supressChange); } - if ( supressChange ) - { + if (supressChange) { trafficClass = oldTrafficClass; supressedChanges++; } } } - - if ( trafficClass > 0 ) - { - newTrafficDos.writeLong( id0 ); - newTrafficDos.writeLong( id1 ); - newTrafficDos.writeInt( trafficClass ); - - expctxWay.decode( description ); - expctxWay.addLookupValue( "estimated_traffic_class", trafficClass + 1 ); + + if (trafficClass > 0) { + newTrafficDos.writeLong(id0); + newTrafficDos.writeLong(id1); + newTrafficDos.writeInt(trafficClass); + + expctxWay.decode(description); + expctxWay.addLookupValue("estimated_traffic_class", trafficClass + 1); return expctxWay.encode(); } return description; diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java b/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java index 5e9da4a..ccfcacd 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java @@ -1,229 +1,194 @@ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.HashMap; - -import btools.util.CompactLongSet; -import btools.util.DiffCoderDataOutputStream; -import btools.util.FrozenLongSet; - -/** - * PosUnifier does 3 steps in map-processing: - * - * - unify positions - add srtm elevation data - make a bordernodes file - * containing net data from the bordernids-file just containing ids - * - * @author ab - */ -public class PosUnifier extends MapCreatorBase -{ - private DiffCoderDataOutputStream nodesOutStream; - private DiffCoderDataOutputStream borderNodesOut; - private File nodeTilesOut; - private CompactLongSet[] positionSets; - - private HashMap srtmmap; - private int lastSrtmLonIdx; - private int lastSrtmLatIdx; - private SrtmRaster lastSrtmRaster; - private String srtmdir; - - private CompactLongSet borderNids; - - public static void main( String[] args ) throws Exception - { - System.out.println( "*** PosUnifier: Unify position values and enhance elevation" ); - if ( args.length != 5 ) - { - System.out.println( "usage: java PosUnifier " ); - return; - } - new PosUnifier().process( new File( args[0] ), new File( args[1] ), new File( args[2] ), new File( args[3] ), args[4] ); - } - - public void process( File nodeTilesIn, File nodeTilesOut, File bordernidsinfile, File bordernodesoutfile, String srtmdir ) throws Exception - { - this.nodeTilesOut = nodeTilesOut; - this.srtmdir = srtmdir; - - // read border nids set - DataInputStream dis = createInStream( bordernidsinfile ); - borderNids = new CompactLongSet(); - try - { - for ( ;; ) - { - long nid = readId( dis ); - if ( !borderNids.contains( nid ) ) - borderNids.fastAdd( nid ); - } - } - catch (EOFException eof) - { - dis.close(); - } - borderNids = new FrozenLongSet( borderNids ); - - // process all files - borderNodesOut = createOutStream( bordernodesoutfile ); - new NodeIterator( this, true ).processDir( nodeTilesIn, ".n5d" ); - borderNodesOut.close(); - } - - @Override - public void nodeFileStart( File nodefile ) throws Exception - { - resetSrtm(); - - nodesOutStream = createOutStream( fileFromTemplate( nodefile, nodeTilesOut, "u5d" ) ); - - positionSets = new CompactLongSet[2500]; - } - - @Override - public void nextNode( NodeData n ) throws Exception - { - SrtmRaster srtm = srtmForNode( n.ilon, n.ilat ); - n.selev = srtm == null ? Short.MIN_VALUE : srtm.getElevation( n.ilon, n.ilat ); - - findUniquePos( n ); - - n.writeTo( nodesOutStream ); - if ( borderNids.contains( n.nid ) ) - { - n.writeTo( borderNodesOut ); - } - } - - @Override - public void nodeFileEnd( File nodeFile ) throws Exception - { - nodesOutStream.close(); - } - - private boolean checkAdd( int lon, int lat ) - { - int slot = ((lon%5000000)/100000)*50 + ((lat%5000000)/100000); - long id = ( (long) lon ) << 32 | lat; - CompactLongSet set = positionSets[slot]; - if ( set == null ) - { - positionSets[slot] = set = new CompactLongSet(); - } - if ( !set.contains( id ) ) - { - set.fastAdd( id ); - return true; - } - return false; - } - - - - - private void findUniquePos( NodeData n ) - { - if ( !checkAdd( n.ilon, n.ilat ) ) - { - _findUniquePos( n ); - } - } - - private void _findUniquePos( NodeData n ) - { - // fix the position for uniqueness - int lonmod = n.ilon % 1000000; - int londelta = lonmod < 500000 ? 1 : -1; - int latmod = n.ilat % 1000000; - int latdelta = latmod < 500000 ? 1 : -1; - for ( int latsteps = 0; latsteps < 100; latsteps++ ) - { - for ( int lonsteps = 0; lonsteps <= latsteps; lonsteps++ ) - { - int lon = n.ilon + lonsteps * londelta; - int lat = n.ilat + latsteps * latdelta; - if ( checkAdd( lon, lat ) ) - { - n.ilon = lon; - n.ilat = lat; - return; - } - } - } - System.out.println( "*** WARNING: cannot unify position for: " + n.ilon + " " + n.ilat ); - } - - /** - * get the srtm data set for a position srtm coords are - * srtm__ where srtmLon = 180 + lon, srtmLat = 60 - lat - */ - private SrtmRaster srtmForNode( int ilon, int ilat ) throws Exception - { - int srtmLonIdx = ( ilon + 5000000 ) / 5000000; - int srtmLatIdx = ( 654999999 - ilat ) / 5000000 - 100; // ugly negative rounding... - - if ( srtmLonIdx == lastSrtmLonIdx && srtmLatIdx == lastSrtmLatIdx ) - { - return lastSrtmRaster; - } - lastSrtmLonIdx = srtmLonIdx; - lastSrtmLatIdx = srtmLatIdx; - - String slonidx = "0" + srtmLonIdx; - String slatidx = "0" + srtmLatIdx; - String filename = "srtm_" + slonidx.substring( slonidx.length()-2 ) + "_" + slatidx.substring( slatidx.length()-2 ); - - lastSrtmRaster = srtmmap.get( filename ); - if ( lastSrtmRaster == null && !srtmmap.containsKey( filename ) ) - { - File f = new File( new File( srtmdir ), filename + ".bef" ); - System.out.println( "checking: " + f + " ilon=" + ilon + " ilat=" + ilat ); - if ( f.exists() ) - { - System.out.println( "*** reading: " + f ); - try - { - InputStream isc = new BufferedInputStream( new FileInputStream( f ) ); - lastSrtmRaster = new RasterCoder().decodeRaster( isc ); - isc.close(); - } - catch (Exception e) - { - System.out.println( "**** ERROR reading " + f + " ****" ); - } - srtmmap.put( filename, lastSrtmRaster ); - return lastSrtmRaster; - } - - f = new File( new File( srtmdir ), filename + ".zip" ); - System.out.println( "reading: " + f + " ilon=" + ilon + " ilat=" + ilat ); - if ( f.exists() ) - { - try - { - lastSrtmRaster = new SrtmData( f ).getRaster(); - } - catch (Exception e) - { - System.out.println( "**** ERROR reading " + f + " ****" ); - } - } - srtmmap.put( filename, lastSrtmRaster ); - } - return lastSrtmRaster; - } - - private void resetSrtm() - { - srtmmap = new HashMap(); - lastSrtmLonIdx = -1; - lastSrtmLatIdx = -1; - lastSrtmRaster = null; - } - -} +package btools.mapcreator; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.HashMap; + +import btools.util.CompactLongSet; +import btools.util.DiffCoderDataOutputStream; +import btools.util.FrozenLongSet; + +/** + * PosUnifier does 3 steps in map-processing: + *

+ * - unify positions - add srtm elevation data - make a bordernodes file + * containing net data from the bordernids-file just containing ids + * + * @author ab + */ +public class PosUnifier extends MapCreatorBase { + private DiffCoderDataOutputStream nodesOutStream; + private DiffCoderDataOutputStream borderNodesOut; + private File nodeTilesOut; + private CompactLongSet[] positionSets; + + private HashMap srtmmap; + private int lastSrtmLonIdx; + private int lastSrtmLatIdx; + private SrtmRaster lastSrtmRaster; + private String srtmdir; + + private CompactLongSet borderNids; + + public static void main(String[] args) throws Exception { + System.out.println("*** PosUnifier: Unify position values and enhance elevation"); + if (args.length != 5) { + System.out.println("usage: java PosUnifier "); + return; + } + new PosUnifier().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3]), args[4]); + } + + public void process(File nodeTilesIn, File nodeTilesOut, File bordernidsinfile, File bordernodesoutfile, String srtmdir) throws Exception { + this.nodeTilesOut = nodeTilesOut; + this.srtmdir = srtmdir; + + // read border nids set + DataInputStream dis = createInStream(bordernidsinfile); + borderNids = new CompactLongSet(); + try { + for (; ; ) { + long nid = readId(dis); + if (!borderNids.contains(nid)) + borderNids.fastAdd(nid); + } + } catch (EOFException eof) { + dis.close(); + } + borderNids = new FrozenLongSet(borderNids); + + // process all files + borderNodesOut = createOutStream(bordernodesoutfile); + new NodeIterator(this, true).processDir(nodeTilesIn, ".n5d"); + borderNodesOut.close(); + } + + @Override + public void nodeFileStart(File nodefile) throws Exception { + resetSrtm(); + + nodesOutStream = createOutStream(fileFromTemplate(nodefile, nodeTilesOut, "u5d")); + + positionSets = new CompactLongSet[2500]; + } + + @Override + public void nextNode(NodeData n) throws Exception { + SrtmRaster srtm = srtmForNode(n.ilon, n.ilat); + n.selev = srtm == null ? Short.MIN_VALUE : srtm.getElevation(n.ilon, n.ilat); + + findUniquePos(n); + + n.writeTo(nodesOutStream); + if (borderNids.contains(n.nid)) { + n.writeTo(borderNodesOut); + } + } + + @Override + public void nodeFileEnd(File nodeFile) throws Exception { + nodesOutStream.close(); + } + + private boolean checkAdd(int lon, int lat) { + int slot = ((lon % 5000000) / 100000) * 50 + ((lat % 5000000) / 100000); + long id = ((long) lon) << 32 | lat; + CompactLongSet set = positionSets[slot]; + if (set == null) { + positionSets[slot] = set = new CompactLongSet(); + } + if (!set.contains(id)) { + set.fastAdd(id); + return true; + } + return false; + } + + + private void findUniquePos(NodeData n) { + if (!checkAdd(n.ilon, n.ilat)) { + _findUniquePos(n); + } + } + + private void _findUniquePos(NodeData n) { + // fix the position for uniqueness + int lonmod = n.ilon % 1000000; + int londelta = lonmod < 500000 ? 1 : -1; + int latmod = n.ilat % 1000000; + int latdelta = latmod < 500000 ? 1 : -1; + for (int latsteps = 0; latsteps < 100; latsteps++) { + for (int lonsteps = 0; lonsteps <= latsteps; lonsteps++) { + int lon = n.ilon + lonsteps * londelta; + int lat = n.ilat + latsteps * latdelta; + if (checkAdd(lon, lat)) { + n.ilon = lon; + n.ilat = lat; + return; + } + } + } + System.out.println("*** WARNING: cannot unify position for: " + n.ilon + " " + n.ilat); + } + + /** + * get the srtm data set for a position srtm coords are + * srtm__ where srtmLon = 180 + lon, srtmLat = 60 - lat + */ + private SrtmRaster srtmForNode(int ilon, int ilat) throws Exception { + int srtmLonIdx = (ilon + 5000000) / 5000000; + int srtmLatIdx = (654999999 - ilat) / 5000000 - 100; // ugly negative rounding... + + if (srtmLonIdx == lastSrtmLonIdx && srtmLatIdx == lastSrtmLatIdx) { + return lastSrtmRaster; + } + lastSrtmLonIdx = srtmLonIdx; + lastSrtmLatIdx = srtmLatIdx; + + String slonidx = "0" + srtmLonIdx; + String slatidx = "0" + srtmLatIdx; + String filename = "srtm_" + slonidx.substring(slonidx.length() - 2) + "_" + slatidx.substring(slatidx.length() - 2); + + lastSrtmRaster = srtmmap.get(filename); + if (lastSrtmRaster == null && !srtmmap.containsKey(filename)) { + File f = new File(new File(srtmdir), filename + ".bef"); + System.out.println("checking: " + f + " ilon=" + ilon + " ilat=" + ilat); + if (f.exists()) { + System.out.println("*** reading: " + f); + try { + InputStream isc = new BufferedInputStream(new FileInputStream(f)); + lastSrtmRaster = new RasterCoder().decodeRaster(isc); + isc.close(); + } catch (Exception e) { + System.out.println("**** ERROR reading " + f + " ****"); + } + srtmmap.put(filename, lastSrtmRaster); + return lastSrtmRaster; + } + + f = new File(new File(srtmdir), filename + ".zip"); + System.out.println("reading: " + f + " ilon=" + ilon + " ilat=" + ilat); + if (f.exists()) { + try { + lastSrtmRaster = new SrtmData(f).getRaster(); + } catch (Exception e) { + System.out.println("**** ERROR reading " + f + " ****"); + } + } + srtmmap.put(filename, lastSrtmRaster); + } + return lastSrtmRaster; + } + + private void resetSrtm() { + srtmmap = new HashMap(); + lastSrtmLonIdx = -1; + lastSrtmLatIdx = -1; + lastSrtmRaster = null; + } + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java b/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java index 7a8473a..233e3ea 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java @@ -1,16 +1,15 @@ package btools.mapcreator; import java.io.*; + import btools.util.*; // // Encode/decode a raster // -public class RasterCoder -{ - public void encodeRaster(SrtmRaster raster, OutputStream os) throws IOException - { +public class RasterCoder { + public void encodeRaster(SrtmRaster raster, OutputStream os) throws IOException { DataOutputStream dos = new DataOutputStream(os); long t0 = System.currentTimeMillis(); @@ -29,8 +28,7 @@ public class RasterCoder System.out.println("finished encoding in " + (t1 - t0) + " ms"); } - public SrtmRaster decodeRaster(InputStream is) throws IOException - { + public SrtmRaster decodeRaster(InputStream is) throws IOException { DataInputStream dis = new DataInputStream(is); long t0 = System.currentTimeMillis(); @@ -46,78 +44,65 @@ public class RasterCoder raster.eval_array = new short[raster.ncols * raster.nrows]; _decodeRaster(raster, is); - + raster.usingWeights = raster.ncols > 6001; long t1 = System.currentTimeMillis(); - System.out.println("finished decoding in " + (t1 - t0) + " ms ncols=" + raster.ncols + " nrows=" + raster.nrows ); + System.out.println("finished decoding in " + (t1 - t0) + " ms ncols=" + raster.ncols + " nrows=" + raster.nrows); return raster; } - private void _encodeRaster(SrtmRaster raster, OutputStream os) throws IOException - { + private void _encodeRaster(SrtmRaster raster, OutputStream os) throws IOException { MixCoderDataOutputStream mco = new MixCoderDataOutputStream(os); int nrows = raster.nrows; int ncols = raster.ncols; short[] pixels = raster.eval_array; int colstep = raster.halfcol ? 2 : 1; - for (int row = 0; row < nrows; row++) - { + for (int row = 0; row < nrows; row++) { short lastval = Short.MIN_VALUE; // nodata - for (int col = 0; col < ncols; col += colstep ) - { + for (int col = 0; col < ncols; col += colstep) { short val = pixels[row * ncols + col]; - if ( val == -32766 ) - { + if (val == -32766) { val = lastval; // replace remaining (border) skips - } - else - { + } else { lastval = val; } - + // remap nodata - int code = val == Short.MIN_VALUE ? -1 : ( val < 0 ? val-1 : val ); - mco.writeMixed( code ); + int code = val == Short.MIN_VALUE ? -1 : (val < 0 ? val - 1 : val); + mco.writeMixed(code); } } mco.flush(); } - private void _decodeRaster(SrtmRaster raster, InputStream is) throws IOException - { + private void _decodeRaster(SrtmRaster raster, InputStream is) throws IOException { MixCoderDataInputStream mci = new MixCoderDataInputStream(is); int nrows = raster.nrows; int ncols = raster.ncols; short[] pixels = raster.eval_array; int colstep = raster.halfcol ? 2 : 1; - for (int row = 0; row < nrows; row++) - { - for (int col = 0; col < ncols; col += colstep ) - { + for (int row = 0; row < nrows; row++) { + for (int col = 0; col < ncols; col += colstep) { int code = mci.readMixed(); - + // remap nodata - int v30 = code == -1 ? Short.MIN_VALUE : ( code < 0 ? code + 1 : code ); - if ( raster.usingWeights && v30 > -32766 ) - { + int v30 = code == -1 ? Short.MIN_VALUE : (code < 0 ? code + 1 : code); + if (raster.usingWeights && v30 > -32766) { v30 *= 2; - } - pixels[row * ncols + col] = (short) ( v30 ); + } + pixels[row * ncols + col] = (short) (v30); } - if ( raster.halfcol ) - { - for (int col = 1; col < ncols-1; col += colstep ) - { - int l = (int)pixels[row * ncols + col - 1]; - int r = (int)pixels[row * ncols + col + 1]; + if (raster.halfcol) { + for (int col = 1; col < ncols - 1; col += colstep) { + int l = (int) pixels[row * ncols + col - 1]; + int r = (int) pixels[row * ncols + col + 1]; short v30 = Short.MIN_VALUE; // nodata - if ( l > -32766 && r > -32766 ) - { - v30 = (short)((l+r)/2); + if (l > -32766 && r > -32766) { + v30 = (short) ((l + r) / 2); } pixels[row * ncols + col] = v30; } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RelationData.java b/brouter-map-creator/src/main/java/btools/mapcreator/RelationData.java index 5d83629..ebdfda2 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RelationData.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RelationData.java @@ -1,27 +1,24 @@ -package btools.mapcreator; - -import btools.util.LongList; - -/** - * Container for relation data on the preprocessor level - * - * @author ab - */ -public class RelationData extends MapCreatorBase -{ - public long rid; - public long description; - public LongList ways; - - public RelationData( long id ) - { - rid = id; - ways = new LongList( 16 ); - } - - public RelationData( long id, LongList ways ) - { - rid = id; - this.ways = ways; - } -} +package btools.mapcreator; + +import btools.util.LongList; + +/** + * Container for relation data on the preprocessor level + * + * @author ab + */ +public class RelationData extends MapCreatorBase { + public long rid; + public long description; + public LongList ways; + + public RelationData(long id) { + rid = id; + ways = new LongList(16); + } + + public RelationData(long id, LongList ways) { + rid = id; + this.ways = ways; + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RelationListener.java b/brouter-map-creator/src/main/java/btools/mapcreator/RelationListener.java index 34bae75..5709a7c 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RelationListener.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RelationListener.java @@ -1,14 +1,13 @@ -package btools.mapcreator; - - -/** - * Callbacklistener for Relations - * - * @author ab - */ -public interface RelationListener -{ - void nextRelation( RelationData data ) throws Exception; - - void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception; -} +package btools.mapcreator; + + +/** + * Callbacklistener for Relations + * + * @author ab + */ +public interface RelationListener { + void nextRelation(RelationData data) throws Exception; + + void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception; +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RelationMerger.java b/brouter-map-creator/src/main/java/btools/mapcreator/RelationMerger.java index b21f757..2cacdda 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RelationMerger.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RelationMerger.java @@ -13,152 +13,131 @@ import btools.util.FrozenLongSet; /** * RelationMerger does 1 step in map processing: - * + *

* - enrich ways with relation information * * @author ab */ -public class RelationMerger extends MapCreatorBase -{ - private HashMap routesets; +public class RelationMerger extends MapCreatorBase { + private HashMap routesets; private CompactLongSet routesetall; private BExpressionContextWay expctxReport; private BExpressionContextWay expctxCheck; - // private BExpressionContext expctxStat; + // private BExpressionContext expctxStat; private DataOutputStream wayOutStream; - - public static void main(String[] args) throws Exception - { - System.out.println("*** RelationMerger: merge relations into ways" ); - if (args.length != 6) - { - System.out.println("usage: java RelationMerger " ); + + public static void main(String[] args) throws Exception { + System.out.println("*** RelationMerger: merge relations into ways"); + if (args.length != 6) { + System.out.println("usage: java RelationMerger "); return; } - new RelationMerger().process( new File( args[0] ), new File( args[1] ), new File( args[2] ), new File( args[3] ), new File( args[4] ), new File( args[5] ) ); + new RelationMerger().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3]), new File(args[4]), new File(args[5])); } - public void init( File relationFileIn, File lookupFile, File reportProfile, File checkProfile ) throws Exception - { + public void init(File relationFileIn, File lookupFile, File reportProfile, File checkProfile) throws Exception { // read lookup + profile for relation access-check - BExpressionMetaData metaReport = new BExpressionMetaData(); - expctxReport = new BExpressionContextWay( metaReport ); - metaReport.readMetaData( lookupFile ); + BExpressionMetaData metaReport = new BExpressionMetaData(); + expctxReport = new BExpressionContextWay(metaReport); + metaReport.readMetaData(lookupFile); - BExpressionMetaData metaCheck = new BExpressionMetaData(); - expctxCheck = new BExpressionContextWay( metaCheck ); - metaCheck.readMetaData( lookupFile ); + BExpressionMetaData metaCheck = new BExpressionMetaData(); + expctxCheck = new BExpressionContextWay(metaCheck); + metaCheck.readMetaData(lookupFile); - expctxReport.parseFile( reportProfile, "global" ); - expctxCheck.parseFile( checkProfile, "global" ); + expctxReport.parseFile(reportProfile, "global"); + expctxCheck.parseFile(checkProfile, "global"); // expctxStat = new BExpressionContext("way"); - + // *** read the relation file into sets for each processed tag - routesets = new HashMap(); + routesets = new HashMap(); routesetall = new CompactLongSet(); - DataInputStream dis = createInStream( relationFileIn ); - try - { - for(;;) - { - long rid = readId( dis ); + DataInputStream dis = createInStream(relationFileIn); + try { + for (; ; ) { + long rid = readId(dis); String route = dis.readUTF(); String network = dis.readUTF(); String state = dis.readUTF(); - int value = "proposed".equals( state ) ? 3 : 2; // 2=yes, 3=proposed - + int value = "proposed".equals(state) ? 3 : 2; // 2=yes, 3=proposed + String tagname = "route_" + route + "_" + network; - + CompactLongSet routeset = null; - if ( expctxCheck.getLookupNameIdx(tagname) >= 0 ) - { + if (expctxCheck.getLookupNameIdx(tagname) >= 0) { String key = tagname + "_" + value; - routeset = routesets.get( key ); - if ( routeset == null ) - { + routeset = routesets.get(key); + if (routeset == null) { routeset = new CompactLongSet(); - routesets.put( key, routeset ); + routesets.put(key, routeset); } } - for(;;) - { - long wid = readId( dis ); - if ( wid == -1 ) break; + for (; ; ) { + long wid = readId(dis); + if (wid == -1) break; // expctxStat.addLookupValue( tagname, "yes", null ); - if ( routeset != null && !routeset.contains( wid ) ) - { - routeset.add( wid ); - routesetall.add( wid ); + if (routeset != null && !routeset.contains(wid)) { + routeset.add(wid); + routesetall.add(wid); } } } - } - catch( EOFException eof ) - { + } catch (EOFException eof) { dis.close(); } - for( String key : routesets.keySet() ) - { - CompactLongSet routeset = new FrozenLongSet( routesets.get( key ) ); - routesets.put( key, routeset ); - System.out.println( "marked " + routeset.size() + " routes for key: " + key ); + for (String key : routesets.keySet()) { + CompactLongSet routeset = new FrozenLongSet(routesets.get(key)); + routesets.put(key, routeset); + System.out.println("marked " + routeset.size() + " routes for key: " + key); } } - public void process( File wayFileIn, File wayFileOut, File relationFileIn, File lookupFile, File reportProfile, File checkProfile ) throws Exception - { - init( relationFileIn, lookupFile, reportProfile, checkProfile ); + public void process(File wayFileIn, File wayFileOut, File relationFileIn, File lookupFile, File reportProfile, File checkProfile) throws Exception { + init(relationFileIn, lookupFile, reportProfile, checkProfile); // *** finally process the way-file - wayOutStream = createOutStream( wayFileOut ); - new WayIterator( this, true ).processFile( wayFileIn ); + wayOutStream = createOutStream(wayFileOut); + new WayIterator(this, true).processFile(wayFileIn); wayOutStream.close(); // System.out.println( "-------- route-statistics -------- " ); // expctxStat.dumpStatistics(); -} + } @Override - public void nextWay( WayData data ) throws Exception - { + public void nextWay(WayData data) throws Exception { // propagate the route-bits - if ( routesetall.contains( data.wid ) ) - { + if (routesetall.contains(data.wid)) { boolean ok = true; // check access and log a warning for conflicts - expctxReport.evaluate( false, data.description ); + expctxReport.evaluate(false, data.description); boolean warn = expctxReport.getCostfactor() >= 10000.; - if ( warn ) - { - expctxCheck.evaluate( false, data.description ); + if (warn) { + expctxCheck.evaluate(false, data.description); ok = expctxCheck.getCostfactor() < 10000.; - System.out.println( "** relation access conflict for wid = " + data.wid + " tags:" + expctxReport.getKeyValueDescription( false, data.description ) + " (ok=" + ok + ")" ); + System.out.println("** relation access conflict for wid = " + data.wid + " tags:" + expctxReport.getKeyValueDescription(false, data.description) + " (ok=" + ok + ")"); } - - if ( ok ) - { - expctxReport.decode( data.description ); - for( String key : routesets.keySet() ) - { - CompactLongSet routeset = routesets.get( key ); - if ( routeset.contains( data.wid ) ) - { - int sepIdx = key.lastIndexOf( '_' ); - String tagname = key.substring( 0, sepIdx ); - int val = Integer.valueOf( key.substring( sepIdx+1 ) ); - expctxReport.addSmallestLookupValue( tagname, val ); + + if (ok) { + expctxReport.decode(data.description); + for (String key : routesets.keySet()) { + CompactLongSet routeset = routesets.get(key); + if (routeset.contains(data.wid)) { + int sepIdx = key.lastIndexOf('_'); + String tagname = key.substring(0, sepIdx); + int val = Integer.valueOf(key.substring(sepIdx + 1)); + expctxReport.addSmallestLookupValue(tagname, val); } } - data.description = expctxReport.encode(); + data.description = expctxReport.encode(); } } - if ( wayOutStream != null ) - { - data.writeTo( wayOutStream ); + if (wayOutStream != null) { + data.writeTo(wayOutStream); } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java b/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java index 4a19d7d..d62f3a0 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java @@ -10,63 +10,52 @@ import btools.util.TinyDenseLongMap; /** * WayCutter does 2 step in map-processing: - * + *

* - cut the way file into 45*30 - pieces * - enrich ways with relation information * * @author ab */ -public class RelationStatistics extends MapCreatorBase -{ - public static void main(String[] args) throws Exception - { +public class RelationStatistics extends MapCreatorBase { + public static void main(String[] args) throws Exception { System.out.println("*** RelationStatistics: count relation networks"); - if (args.length != 1) - { - System.out.println("usage: java WayCutter " ); + if (args.length != 1) { + System.out.println("usage: java WayCutter "); return; } - new RelationStatistics().process( new File( args[0] ) ); + new RelationStatistics().process(new File(args[0])); } - public void process( File relationFileIn ) throws Exception - { - HashMap relstats = new HashMap(); - - DataInputStream dis = createInStream( relationFileIn ); - try - { - for(;;) - { - long rid = readId( dis ); + public void process(File relationFileIn) throws Exception { + HashMap relstats = new HashMap(); + + DataInputStream dis = createInStream(relationFileIn); + try { + for (; ; ) { + long rid = readId(dis); String network = dis.readUTF(); int waycount = 0; - for(;;) - { - long wid = readId( dis ); - if ( wid == -1 ) break; + for (; ; ) { + long wid = readId(dis); + if (wid == -1) break; waycount++; } - - long[] stat = relstats.get( network ); - if ( stat == null ) - { + + long[] stat = relstats.get(network); + if (stat == null) { stat = new long[2]; - relstats.put( network, stat ); + relstats.put(network, stat); } stat[0]++; stat[1] += waycount; } - } - catch( EOFException eof ) - { + } catch (EOFException eof) { dis.close(); } - for( String network : relstats.keySet() ) - { - long[] stat = relstats.get( network ); - System.out.println( "network: " + network + " has " + stat[0] + " relations with " + stat[1] + " ways" ); + for (String network : relstats.keySet()) { + long[] stat = relstats.get(network); + System.out.println("network: " + network + " has " + stat[0] + " relations with " + stat[1] + " ways"); } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter.java b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter.java index 6b4e096..0dec4c1 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter.java @@ -4,41 +4,35 @@ import java.io.File; /** * RestrictionCutter writes Restrictions to tiles - * + *

* - cut the way file into 45*30 - pieces * - enrich ways with relation information * * @author ab */ -public class RestrictionCutter extends MapCreatorBase -{ +public class RestrictionCutter extends MapCreatorBase { private WayCutter wayCutter; - public void init( File outTileDir, WayCutter wayCutter ) throws Exception - { + public void init(File outTileDir, WayCutter wayCutter) throws Exception { outTileDir.mkdir(); this.outTileDir = outTileDir; this.wayCutter = wayCutter; } - public void finish() throws Exception - { + public void finish() throws Exception { closeTileOutStreams(); } - public void nextRestriction( RestrictionData data ) throws Exception - { - int tileIndex = wayCutter.getTileIndexForNid( data.viaNid ); - if ( tileIndex != -1 ) - { - data.writeTo( getOutStreamForTile( tileIndex ) ); + public void nextRestriction(RestrictionData data) throws Exception { + int tileIndex = wayCutter.getTileIndexForNid(data.viaNid); + if (tileIndex != -1) { + data.writeTo(getOutStreamForTile(tileIndex)); } } - protected String getNameForTile( int tileIndex ) - { - String name = wayCutter.getNameForTile( tileIndex ); - return name.substring( 0, name.length()-3 ) + "rtl"; + protected String getNameForTile(int tileIndex) { + String name = wayCutter.getNameForTile(tileIndex); + return name.substring(0, name.length() - 3) + "rtl"; } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter5.java b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter5.java index 83b74d8..1065f9a 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter5.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionCutter5.java @@ -4,40 +4,34 @@ import java.io.File; /** * RestrictionCutter5 does 1 step in map-processing: - * + *

* - cut the 45*30 restriction files into 5*5 pieces * * @author ab */ -public class RestrictionCutter5 extends MapCreatorBase -{ +public class RestrictionCutter5 extends MapCreatorBase { private WayCutter5 wayCutter5; - public void init( File outTileDir, WayCutter5 wayCutter5 ) throws Exception - { + public void init(File outTileDir, WayCutter5 wayCutter5) throws Exception { outTileDir.mkdir(); this.outTileDir = outTileDir; this.wayCutter5 = wayCutter5; } - public void finish() throws Exception - { + public void finish() throws Exception { closeTileOutStreams(); } - public void nextRestriction( RestrictionData data ) throws Exception - { - int tileIndex = wayCutter5.getTileIndexForNid( data.viaNid ); - if ( tileIndex != -1 ) - { - data.writeTo( getOutStreamForTile( tileIndex ) ); + public void nextRestriction(RestrictionData data) throws Exception { + int tileIndex = wayCutter5.getTileIndexForNid(data.viaNid); + if (tileIndex != -1) { + data.writeTo(getOutStreamForTile(tileIndex)); } } - protected String getNameForTile( int tileIndex ) - { - String name = wayCutter5.getNameForTile( tileIndex ); - return name.substring( 0, name.length()-3 ) + "rt5"; + protected String getNameForTile(int tileIndex) { + String name = wayCutter5.getNameForTile(tileIndex); + return name.substring(0, name.length() - 3) + "rt5"; } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java index 50f3130..5fafd0e 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RestrictionData.java @@ -15,8 +15,7 @@ import btools.util.CheapAngleMeter; * * @author ab */ -public class RestrictionData extends MapCreatorBase -{ +public class RestrictionData extends MapCreatorBase { public String restrictionKey; public String restriction; public short exceptions; @@ -30,125 +29,99 @@ public class RestrictionData extends MapCreatorBase public int fromLon; public int fromLat; - + public int toLon; public int toLat; - + public boolean badWayMatch; - - private static HashMap names = new HashMap<>(); + + private static HashMap names = new HashMap<>(); private static TreeSet badTRs = new TreeSet<>(); - public RestrictionData() - { - } - - public boolean isPositive() - { - return restriction.startsWith( "only_" ); + public RestrictionData() { } - public boolean isValid() - { - boolean valid = fromLon != 0 && toLon != 0 && ( restriction.startsWith( "only_" ) || restriction.startsWith( "no_" ) ); - if ( (!valid) || badWayMatch || !(checkGeometry()) ) - { - synchronized( badTRs ) - { - badTRs.add( ( (long) viaLon ) << 32 | viaLat ); + public boolean isPositive() { + return restriction.startsWith("only_"); + } + + public boolean isValid() { + boolean valid = fromLon != 0 && toLon != 0 && (restriction.startsWith("only_") || restriction.startsWith("no_")); + if ((!valid) || badWayMatch || !(checkGeometry())) { + synchronized (badTRs) { + badTRs.add(((long) viaLon) << 32 | viaLat); } } - return valid && "restriction".equals( restrictionKey ); + return valid && "restriction".equals(restrictionKey); } - - private boolean checkGeometry() - { - double a = (new CheapAngleMeter()).calcAngle( fromLon, fromLat, viaLon, viaLat, toLon, toLat ); + + private boolean checkGeometry() { + double a = (new CheapAngleMeter()).calcAngle(fromLon, fromLat, viaLon, viaLat, toLon, toLat); String t; - if ( restriction.startsWith( "only_" ) ) - { - t = restriction.substring( "only_".length() ); - } - else if ( restriction.startsWith( "no_" ) ) - { - t = restriction.substring( "no_".length() ); - } - else throw new RuntimeException( "ups" ); + if (restriction.startsWith("only_")) { + t = restriction.substring("only_".length()); + } else if (restriction.startsWith("no_")) { + t = restriction.substring("no_".length()); + } else throw new RuntimeException("ups"); - if ( restrictionKey.endsWith( ":conditional" ) ) - { - int idx = t.indexOf( '@' ); - if ( idx >= 0 ) - { - t = t.substring(0, idx ).trim(); + if (restrictionKey.endsWith(":conditional")) { + int idx = t.indexOf('@'); + if (idx >= 0) { + t = t.substring(0, idx).trim(); } } - if ( "left_turn".equals( t ) ) - { + if ("left_turn".equals(t)) { return a < -5. && a > -175.; } - if ( "right_turn".equals( t ) ) - { + if ("right_turn".equals(t)) { return a > 5. && a < 175.; } - if ( "straight_on".equals( t ) ) - { + if ("straight_on".equals(t)) { return a > -85. && a < 85.; } - if ( "u_turn".equals( t ) ) - { - return a < - 95. || a > 95.; + if ("u_turn".equals(t)) { + return a < -95. || a > 95.; } - return "entry".equals( t ) || "exit".equals( t ); + return "entry".equals(t) || "exit".equals(t); } - private static String unifyName( String name ) - { - synchronized( names ) - { + private static String unifyName(String name) { + synchronized (names) { String n = names.get(name); - if ( n == null ) - { - names.put( name, name ); + if (n == null) { + names.put(name, name); n = name; } return n; } } - - public static void dumpBadTRs() - { - try( BufferedWriter bw = new BufferedWriter( new FileWriter( "badtrs.txt" ) ) ) - { - for( Long id : badTRs ) - { - bw.write( "" + id + " 26\n" ); + + public static void dumpBadTRs() { + try (BufferedWriter bw = new BufferedWriter(new FileWriter("badtrs.txt"))) { + for (Long id : badTRs) { + bw.write("" + id + " 26\n"); } - } - catch( IOException ioe ) - { - throw new RuntimeException( ioe ); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } - public RestrictionData( DataInputStream di ) throws Exception - { - restrictionKey = unifyName( di.readUTF() ); - restriction = unifyName( di.readUTF() ); + public RestrictionData(DataInputStream di) throws Exception { + restrictionKey = unifyName(di.readUTF()); + restriction = unifyName(di.readUTF()); exceptions = di.readShort(); - fromWid = readId( di ); - toWid = readId( di ); - viaNid = readId( di ); + fromWid = readId(di); + toWid = readId(di); + viaNid = readId(di); } - public void writeTo( DataOutputStream dos ) throws Exception - { - dos.writeUTF( restrictionKey ); - dos.writeUTF( restriction ); - dos.writeShort( exceptions ); - writeId( dos, fromWid ); - writeId( dos, toWid ); - writeId( dos, viaNid ); + public void writeTo(DataOutputStream dos) throws Exception { + dos.writeUTF(restrictionKey); + dos.writeUTF(restriction); + dos.writeShort(exceptions); + writeId(dos, fromWid); + writeId(dos, toWid); + writeId(dos, viaNid); } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java b/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java index 039fadb..b101cd8 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java @@ -1,195 +1,166 @@ -package btools.mapcreator; - -/** - * This is a wrapper for a 5*5 degree srtm file in ascii/zip-format - * - * - filter out unused nodes according to the way file - * - enhance with SRTM elevation data - * - split further in smaller (5*5 degree) tiles - * - * @author ab - */ - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.StringTokenizer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class SrtmData -{ - private SrtmRaster raster; - - public SrtmData( File file ) throws Exception - { - raster = new SrtmRaster(); - - ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( file ) ) ); - try - { - for ( ;; ) - { - ZipEntry ze = zis.getNextEntry(); - if ( ze.getName().endsWith( ".asc" ) ) - { - readFromStream( zis ); - return; - } - } - } - finally - { - zis.close(); - } - } - - public SrtmRaster getRaster() - { - return raster; - } - - private String secondToken( String s ) - { - StringTokenizer tk = new StringTokenizer( s, " " ); - tk.nextToken(); - return tk.nextToken(); - } - - public void readFromStream( InputStream is ) throws Exception - { - BufferedReader br = new BufferedReader( new InputStreamReader( is ) ); - int linenr = 0; - for ( ;; ) - { - linenr++; - if ( linenr <= 6 ) - { - String line = br.readLine(); - if ( linenr == 1 ) - raster.ncols = Integer.parseInt( secondToken( line ) ); - else if ( linenr == 2 ) - raster.nrows = Integer.parseInt( secondToken( line ) ); - else if ( linenr == 3 ) - raster.xllcorner = Double.parseDouble( secondToken( line ) ); - else if ( linenr == 4 ) - raster.yllcorner = Double.parseDouble( secondToken( line ) ); - else if ( linenr == 5 ) - raster.cellsize = Double.parseDouble( secondToken( line ) ); - else if ( linenr == 6 ) - { - // nodata ignored here ( < -250 assumed nodata... ) - // raster.noDataValue = Short.parseShort( secondToken( line ) ); - raster.eval_array = new short[raster.ncols * raster.nrows]; - } - } - else - { - int row = 0; - int col = 0; - int n = 0; - boolean negative = false; - for ( ;; ) - { - int c = br.read(); - if ( c < 0 ) - break; - if ( c == ' ' ) - { - if ( negative ) - n = -n; - short val = n < -250 ? Short.MIN_VALUE : (short) (n); - - raster.eval_array[row * raster.ncols + col] = val; - if ( ++col == raster.ncols ) - { - col = 0; - ++row; - } - n = 0; - negative = false; - } - else if ( c >= '0' && c <= '9' ) - { - n = 10 * n + ( c - '0' ); - } - else if ( c == '-' ) - { - negative = true; - } - } - break; - } - } - br.close(); - } - - public static void main( String[] args ) throws Exception - { - String fromDir = args[0]; - String toDir = args[1]; - - File[] files = new File( fromDir ).listFiles(); - for( File f : files ) - { - if ( !f.getName().endsWith( ".zip" ) ) - { - continue; - } - System.out.println( "*** reading: " + f ); - long t0 = System.currentTimeMillis(); - SrtmRaster raster = new SrtmData( f ).getRaster(); - long t1 = System.currentTimeMillis(); - String name = f.getName(); - - long zipTime = t1-t0; - - File fbef = new File( new File( toDir ), name.substring( 0, name.length()-3 ) + "bef" ); - System.out.println( "recoding: " + f + " to " + fbef ); - OutputStream osbef = new BufferedOutputStream( new FileOutputStream( fbef ) ); - new RasterCoder().encodeRaster( raster, osbef ); - osbef.close(); - - System.out.println( "*** re-reading: " + fbef ); - - long t2 = System.currentTimeMillis(); - InputStream isc = new BufferedInputStream( new FileInputStream( fbef ) ); - SrtmRaster raster2 = new RasterCoder().decodeRaster( isc ); - isc.close(); - long t3 = System.currentTimeMillis(); - - long befTime = t3-t2; - - System.out.println( "*** zip-time: " + zipTime + "*** bef-time: " + befTime ); - - String s1 = raster.toString(); - String s2 = raster2.toString(); - - if ( !s1.equals( s2 ) ) - { - throw new IllegalArgumentException( "missmatch: " + s1 + "<--->" + s2 ); - } - - int cols = raster.ncols; - int rows = raster.nrows; - for( int c = 0; c < cols; c++ ) - { - for( int r = 0; r < rows; r++ ) - { - int idx = r * cols + c; - - if ( raster.eval_array[idx] != raster2.eval_array[idx] ) - { - throw new IllegalArgumentException( "missmatch: at " + c + "," + r + ": " + raster.eval_array[idx] + "<--->" + raster2.eval_array[idx] ); - } - } - } - } - } -} +package btools.mapcreator; + +/** + * This is a wrapper for a 5*5 degree srtm file in ascii/zip-format + *

+ * - filter out unused nodes according to the way file + * - enhance with SRTM elevation data + * - split further in smaller (5*5 degree) tiles + * + * @author ab + */ + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.StringTokenizer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class SrtmData { + private SrtmRaster raster; + + public SrtmData(File file) throws Exception { + raster = new SrtmRaster(); + + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file))); + try { + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze.getName().endsWith(".asc")) { + readFromStream(zis); + return; + } + } + } finally { + zis.close(); + } + } + + public SrtmRaster getRaster() { + return raster; + } + + private String secondToken(String s) { + StringTokenizer tk = new StringTokenizer(s, " "); + tk.nextToken(); + return tk.nextToken(); + } + + public void readFromStream(InputStream is) throws Exception { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + int linenr = 0; + for (; ; ) { + linenr++; + if (linenr <= 6) { + String line = br.readLine(); + if (linenr == 1) + raster.ncols = Integer.parseInt(secondToken(line)); + else if (linenr == 2) + raster.nrows = Integer.parseInt(secondToken(line)); + else if (linenr == 3) + raster.xllcorner = Double.parseDouble(secondToken(line)); + else if (linenr == 4) + raster.yllcorner = Double.parseDouble(secondToken(line)); + else if (linenr == 5) + raster.cellsize = Double.parseDouble(secondToken(line)); + else if (linenr == 6) { + // nodata ignored here ( < -250 assumed nodata... ) + // raster.noDataValue = Short.parseShort( secondToken( line ) ); + raster.eval_array = new short[raster.ncols * raster.nrows]; + } + } else { + int row = 0; + int col = 0; + int n = 0; + boolean negative = false; + for (; ; ) { + int c = br.read(); + if (c < 0) + break; + if (c == ' ') { + if (negative) + n = -n; + short val = n < -250 ? Short.MIN_VALUE : (short) (n); + + raster.eval_array[row * raster.ncols + col] = val; + if (++col == raster.ncols) { + col = 0; + ++row; + } + n = 0; + negative = false; + } else if (c >= '0' && c <= '9') { + n = 10 * n + (c - '0'); + } else if (c == '-') { + negative = true; + } + } + break; + } + } + br.close(); + } + + public static void main(String[] args) throws Exception { + String fromDir = args[0]; + String toDir = args[1]; + + File[] files = new File(fromDir).listFiles(); + for (File f : files) { + if (!f.getName().endsWith(".zip")) { + continue; + } + System.out.println("*** reading: " + f); + long t0 = System.currentTimeMillis(); + SrtmRaster raster = new SrtmData(f).getRaster(); + long t1 = System.currentTimeMillis(); + String name = f.getName(); + + long zipTime = t1 - t0; + + File fbef = new File(new File(toDir), name.substring(0, name.length() - 3) + "bef"); + System.out.println("recoding: " + f + " to " + fbef); + OutputStream osbef = new BufferedOutputStream(new FileOutputStream(fbef)); + new RasterCoder().encodeRaster(raster, osbef); + osbef.close(); + + System.out.println("*** re-reading: " + fbef); + + long t2 = System.currentTimeMillis(); + InputStream isc = new BufferedInputStream(new FileInputStream(fbef)); + SrtmRaster raster2 = new RasterCoder().decodeRaster(isc); + isc.close(); + long t3 = System.currentTimeMillis(); + + long befTime = t3 - t2; + + System.out.println("*** zip-time: " + zipTime + "*** bef-time: " + befTime); + + String s1 = raster.toString(); + String s2 = raster2.toString(); + + if (!s1.equals(s2)) { + throw new IllegalArgumentException("missmatch: " + s1 + "<--->" + s2); + } + + int cols = raster.ncols; + int rows = raster.nrows; + for (int c = 0; c < cols; c++) { + for (int r = 0; r < rows; r++) { + int idx = r * cols + c; + + if (raster.eval_array[idx] != raster2.eval_array[idx]) { + throw new IllegalArgumentException("missmatch: at " + c + "," + r + ": " + raster.eval_array[idx] + "<--->" + raster2.eval_array[idx]); + } + } + } + } + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java b/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java index 60ef81f..3a307bf 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java @@ -7,8 +7,7 @@ import btools.util.ReducedMedianFilter; * * @author ab */ -public class SrtmRaster -{ +public class SrtmRaster { public int ncols; public int nrows; public boolean halfcol; @@ -22,99 +21,93 @@ public class SrtmRaster private boolean missingData = false; - public short getElevation( int ilon, int ilat ) - { + public short getElevation(int ilon, int ilat) { double lon = ilon / 1000000. - 180.; double lat = ilat / 1000000. - 90.; - if ( usingWeights ) - { - return getElevationFromShiftWeights( lon, lat ); + if (usingWeights) { + return getElevationFromShiftWeights(lon, lat); } // no weights calculated, use 2d linear interpolation - double dcol = (lon - xllcorner)/cellsize -0.5; - double drow = (lat - yllcorner)/cellsize -0.5; - int row = (int)drow; - int col = (int)dcol; - if ( col < 0 ) col = 0; - if ( col >= ncols-1 ) col = ncols - 2; - if ( row < 0 ) row = 0; - if ( row >= nrows-1 ) row = nrows - 2; - double wrow = drow-row; - double wcol = dcol-col; + double dcol = (lon - xllcorner) / cellsize - 0.5; + double drow = (lat - yllcorner) / cellsize - 0.5; + int row = (int) drow; + int col = (int) dcol; + if (col < 0) col = 0; + if (col >= ncols - 1) col = ncols - 2; + if (row < 0) row = 0; + if (row >= nrows - 1) row = nrows - 2; + double wrow = drow - row; + double wcol = dcol - col; missingData = false; // System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col ); - double eval = (1.-wrow)*(1.-wcol)*get(row ,col ) - + ( wrow)*(1.-wcol)*get(row+1,col ) - + (1.-wrow)*( wcol)*get(row ,col+1) - + ( wrow)*( wcol)*get(row+1,col+1); + double eval = (1. - wrow) * (1. - wcol) * get(row, col) + + (wrow) * (1. - wcol) * get(row + 1, col) + + (1. - wrow) * (wcol) * get(row, col + 1) + + (wrow) * (wcol) * get(row + 1, col + 1); // System.out.println( "eval=" + eval ); - return missingData ? Short.MIN_VALUE : (short)(eval*4); + return missingData ? Short.MIN_VALUE : (short) (eval * 4); } - private short get( int r, int c ) - { - short e = eval_array[ (nrows-1-r)*ncols + c ]; - if ( e == Short.MIN_VALUE ) missingData = true; + private short get(int r, int c) { + short e = eval_array[(nrows - 1 - r) * ncols + c]; + if (e == Short.MIN_VALUE) missingData = true; return e; } - private short getElevationFromShiftWeights( double lon, double lat ) - { + private short getElevationFromShiftWeights(double lon, double lat) { // calc lat-idx and -weight - double alat = lat < 0. ? - lat : lat; + double alat = lat < 0. ? -lat : lat; alat /= 5.; - int latIdx = (int)alat; + int latIdx = (int) alat; double wlat = alat - latIdx; - double dcol = (lon - xllcorner)/cellsize; - double drow = (lat - yllcorner)/cellsize; - int row = (int)drow; - int col = (int)dcol; + double dcol = (lon - xllcorner) / cellsize; + double drow = (lat - yllcorner) / cellsize; + int row = (int) drow; + int col = (int) dcol; - double dgx = (dcol-col)*gridSteps; - double dgy = (drow-row)*gridSteps; + double dgx = (dcol - col) * gridSteps; + double dgy = (drow - row) * gridSteps; // System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col ); - int gx = (int)(dgx); - int gy = (int)(dgy); + int gx = (int) (dgx); + int gy = (int) (dgy); - double wx = dgx-gx; - double wy = dgy-gy; + double wx = dgx - gx; + double wy = dgy - gy; - double w00 = (1.-wx)*(1.-wy); - double w01 = (1.-wx)*( wy); - double w10 = ( wx)*(1.-wy); - double w11 = ( wx)*( wy); + double w00 = (1. - wx) * (1. - wy); + double w01 = (1. - wx) * (wy); + double w10 = (wx) * (1. - wy); + double w11 = (wx) * (wy); - Weights[][] w0 = getWeights( latIdx ); - Weights[][] w1 = getWeights( latIdx+1 ); + Weights[][] w0 = getWeights(latIdx); + Weights[][] w1 = getWeights(latIdx + 1); missingData = false; - double m0 = w00*getElevation( w0[gx ][gy ], row, col ) - + w01*getElevation( w0[gx ][gy+1], row, col ) - + w10*getElevation( w0[gx+1][gy ], row, col ) - + w11*getElevation( w0[gx+1][gy+1], row, col ); - double m1 = w00*getElevation( w1[gx ][gy ], row, col ) - + w01*getElevation( w1[gx ][gy+1], row, col ) - + w10*getElevation( w1[gx+1][gy ], row, col ) - + w11*getElevation( w1[gx+1][gy+1], row, col ); + double m0 = w00 * getElevation(w0[gx][gy], row, col) + + w01 * getElevation(w0[gx][gy + 1], row, col) + + w10 * getElevation(w0[gx + 1][gy], row, col) + + w11 * getElevation(w0[gx + 1][gy + 1], row, col); + double m1 = w00 * getElevation(w1[gx][gy], row, col) + + w01 * getElevation(w1[gx][gy + 1], row, col) + + w10 * getElevation(w1[gx + 1][gy], row, col) + + w11 * getElevation(w1[gx + 1][gy + 1], row, col); - if ( missingData ) return Short.MIN_VALUE; - double m = (1.-wlat) * m0 + wlat * m1; - return (short)(m * 2); + if (missingData) return Short.MIN_VALUE; + double m = (1. - wlat) * m0 + wlat * m1; + return (short) (m * 2); } - private ReducedMedianFilter rmf = new ReducedMedianFilter( 256 ); + private ReducedMedianFilter rmf = new ReducedMedianFilter(256); - private double getElevation( Weights w, int row, int col ) - { - if ( missingData ) - { + private double getElevation(Weights w, int row, int col) { + if (missingData) { return 0.; } int nx = w.nx; @@ -126,64 +119,53 @@ public class SrtmRaster rmf.reset(); - for( int ix = 0; ix < nx; ix ++ ) - { - for( int iy = 0; iy < ny; iy ++ ) - { - short val = get( row + iy - my, col + ix - mx ); - rmf.addSample( w.getWeight( ix, iy ), val ); + for (int ix = 0; ix < nx; ix++) { + for (int iy = 0; iy < ny; iy++) { + short val = get(row + iy - my, col + ix - mx); + rmf.addSample(w.getWeight(ix, iy), val); } } - return missingData ? 0. : rmf.calcEdgeReducedMedian( filterCenterFraction ); + return missingData ? 0. : rmf.calcEdgeReducedMedian(filterCenterFraction); } - private static class Weights - { + private static class Weights { int nx; int ny; double[] weights; long total = 0; - Weights( int nx, int ny ) - { + Weights(int nx, int ny) { this.nx = nx; this.ny = ny; - weights = new double[nx*ny]; + weights = new double[nx * ny]; } - void inc( int ix, int iy ) - { - weights[ iy*nx + ix ] += 1.; + void inc(int ix, int iy) { + weights[iy * nx + ix] += 1.; total++; } - void normalize( boolean verbose ) - { - for( int iy =0; iy < ny; iy++ ) - { + void normalize(boolean verbose) { + for (int iy = 0; iy < ny; iy++) { StringBuilder sb = verbose ? new StringBuilder() : null; - for( int ix =0; ix < nx; ix++ ) - { - weights[ iy*nx + ix ] /= total; - if ( sb != null ) - { - int iweight = (int)(1000*weights[ iy*nx + ix ] + 0.5 ); + for (int ix = 0; ix < nx; ix++) { + weights[iy * nx + ix] /= total; + if (sb != null) { + int iweight = (int) (1000 * weights[iy * nx + ix] + 0.5); String sval = " " + iweight; - sb.append( sval.substring( sval.length() - 4 ) ); + sb.append(sval.substring(sval.length() - 4)); } } - if ( sb != null ) - { - System.out.println( sb ); + if (sb != null) { + System.out.println(sb); System.out.println(); } } } - double getWeight( int ix, int iy ) - { - return weights[ iy*nx + ix ]; + double getWeight(int ix, int iy) { + return weights[iy * nx + ix]; } } @@ -193,19 +175,16 @@ public class SrtmRaster private static double filterCenterFraction = 0.2; private static double filterDiscRadius = 4.999; // in pixels - static - { - String sRadius = System.getProperty( "filterDiscRadius" ); - if ( sRadius != null && sRadius.length() > 0 ) - { - filterDiscRadius = Integer.parseInt( sRadius ); - System.out.println( "using filterDiscRadius = " + filterDiscRadius ); + static { + String sRadius = System.getProperty("filterDiscRadius"); + if (sRadius != null && sRadius.length() > 0) { + filterDiscRadius = Integer.parseInt(sRadius); + System.out.println("using filterDiscRadius = " + filterDiscRadius); } - String sFraction = System.getProperty( "filterCenterFraction" ); - if ( sFraction != null && sFraction.length() > 0 ) - { - filterCenterFraction = Integer.parseInt( sFraction ) / 100.; - System.out.println( "using filterCenterFraction = " + filterCenterFraction ); + String sFraction = System.getProperty("filterCenterFraction"); + if (sFraction != null && sFraction.length() > 0) { + filterCenterFraction = Integer.parseInt(sFraction) / 100.; + System.out.println("using filterCenterFraction = " + filterCenterFraction); } } @@ -213,82 +192,73 @@ public class SrtmRaster // calculate interpolation weights from the overlap of a probe disc of given radius at given latitude // ( latIndex = 0 -> 0 deg, latIndex = 16 -> 80 degree) - private static Weights[][] getWeights( int latIndex ) - { + private static Weights[][] getWeights(int latIndex) { int idx = latIndex < 16 ? latIndex : 16; Weights[][] res = allShiftWeights[idx]; - if ( res == null ) - { - res = calcWeights( idx ); + if (res == null) { + res = calcWeights(idx); allShiftWeights[idx] = res; } return res; } - private static Weights[][] calcWeights( int latIndex ) - { - double coslat = Math.cos( latIndex * 5. / 57.3 ); + private static Weights[][] calcWeights(int latIndex) { + double coslat = Math.cos(latIndex * 5. / 57.3); // radius in pixel units double ry = filterDiscRadius; double rx = ry / coslat; // gridsize is 2*radius + 1 cell - int nx = ((int)rx) *2 + 3; - int ny = ((int)ry) *2 + 3; + int nx = ((int) rx) * 2 + 3; + int ny = ((int) ry) * 2 + 3; - System.out.println( "nx="+ nx + " ny=" + ny ); + System.out.println("nx=" + nx + " ny=" + ny); int mx = nx / 2; // mean pixels int my = ny / 2; // create a matrix for the relative intergrid-position - Weights[][] shiftWeights = new Weights[gridSteps+1][]; + Weights[][] shiftWeights = new Weights[gridSteps + 1][]; // loop the intergrid-position - for( int gx=0; gx<=gridSteps; gx++ ) - { - shiftWeights[gx] = new Weights[gridSteps+1]; - double x0 = mx + ( (double)gx ) / gridSteps; + for (int gx = 0; gx <= gridSteps; gx++) { + shiftWeights[gx] = new Weights[gridSteps + 1]; + double x0 = mx + ((double) gx) / gridSteps; - for( int gy=0; gy<=gridSteps; gy++ ) - { - double y0 = my + ( (double)gy ) / gridSteps; + for (int gy = 0; gy <= gridSteps; gy++) { + double y0 = my + ((double) gy) / gridSteps; // create the weight-matrix - Weights weights = new Weights( nx, ny ); + Weights weights = new Weights(nx, ny); shiftWeights[gx][gy] = weights; double sampleStep = 0.001; - for( double x = -1. + sampleStep/2.; x < 1.; x += sampleStep ) - { - double mx2 = 1. - x*x; + for (double x = -1. + sampleStep / 2.; x < 1.; x += sampleStep) { + double mx2 = 1. - x * x; - int x_idx = (int)(x0 + x*rx); + int x_idx = (int) (x0 + x * rx); - for( double y = -1. + sampleStep/2.; y < 1.; y += sampleStep ) - { - if ( y*y > mx2 ) - { + for (double y = -1. + sampleStep / 2.; y < 1.; y += sampleStep) { + if (y * y > mx2) { continue; } // we are in the ellipse, see what pixel we are on - int y_idx = (int)(y0 + y*ry); - weights.inc( x_idx, y_idx ); + int y_idx = (int) (y0 + y * ry); + weights.inc(x_idx, y_idx); } } - weights.normalize( true ); + weights.normalize(true); } } return shiftWeights; } - + @Override - public String toString() - { + public String toString() { return ncols + "," + nrows + "," + halfcol + "," + xllcorner + "," + yllcorner + "," + cellsize + "," + noDataValue + "," + usingWeights; } } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter.java index 28529b7..5bdb71e 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter.java @@ -1,112 +1,98 @@ -package btools.mapcreator; - -import java.io.File; - -import btools.util.DenseLongMap; -import btools.util.TinyDenseLongMap; - -/** - * WayCutter does 2 step in map-processing: - * - * - cut the way file into 45*30 - pieces - * - enrich ways with relation information - * - * @author ab - */ -public class WayCutter extends MapCreatorBase -{ - private DenseLongMap tileIndexMap; - - public static void main(String[] args) throws Exception - { - System.out.println("*** WayCutter: Soft-Cut way-data into tiles"); - if (args.length != 3) - { - System.out.println("usage: java WayCutter " ); - - return; - } - new WayCutter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) ); - } - - public void process( File nodeTilesIn, File wayFileIn, File wayTilesOut ) throws Exception - { - init( wayTilesOut ); - - new NodeIterator( this, false ).processDir( nodeTilesIn, ".tlf" ); - - // *** finally process the way-file, cutting into pieces - new WayIterator( this, true ).processFile( wayFileIn ); - finish(); - } - - public void init( File wayTilesOut ) throws Exception - { - this.outTileDir = wayTilesOut; - - // *** read all nodes into tileIndexMap - tileIndexMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap(); - } - - public void finish() throws Exception - { - closeTileOutStreams(); - } - - @Override - public void nextNode( NodeData n ) throws Exception - { - tileIndexMap.put( n.nid, getTileIndex( n.ilon, n.ilat ) ); - } - - @Override - public void nextWay( WayData data ) throws Exception - { - long waytileset = 0; - int nnodes = data.nodes.size(); - - // determine the tile-index for each node - for (int i=0; i 7 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat ); - return lon*6 + lat; - } - - public String getNameForTile( int tileIndex ) - { - int lon = (tileIndex / 6 ) * 45 - 180; - int lat = (tileIndex % 6 ) * 30 - 90; - String slon = lon < 0 ? "W" + (-lon) : "E" + lon; - String slat = lat < 0 ? "S" + (-lat) : "N" + lat; - return slon + "_" + slat + ".wtl"; - } - -} +package btools.mapcreator; + +import java.io.File; + +import btools.util.DenseLongMap; +import btools.util.TinyDenseLongMap; + +/** + * WayCutter does 2 step in map-processing: + *

+ * - cut the way file into 45*30 - pieces + * - enrich ways with relation information + * + * @author ab + */ +public class WayCutter extends MapCreatorBase { + private DenseLongMap tileIndexMap; + + public static void main(String[] args) throws Exception { + System.out.println("*** WayCutter: Soft-Cut way-data into tiles"); + if (args.length != 3) { + System.out.println("usage: java WayCutter "); + + return; + } + new WayCutter().process(new File(args[0]), new File(args[1]), new File(args[2])); + } + + public void process(File nodeTilesIn, File wayFileIn, File wayTilesOut) throws Exception { + init(wayTilesOut); + + new NodeIterator(this, false).processDir(nodeTilesIn, ".tlf"); + + // *** finally process the way-file, cutting into pieces + new WayIterator(this, true).processFile(wayFileIn); + finish(); + } + + public void init(File wayTilesOut) throws Exception { + this.outTileDir = wayTilesOut; + + // *** read all nodes into tileIndexMap + tileIndexMap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap() : new TinyDenseLongMap(); + } + + public void finish() throws Exception { + closeTileOutStreams(); + } + + @Override + public void nextNode(NodeData n) throws Exception { + tileIndexMap.put(n.nid, getTileIndex(n.ilon, n.ilat)); + } + + @Override + public void nextWay(WayData data) throws Exception { + long waytileset = 0; + int nnodes = data.nodes.size(); + + // determine the tile-index for each node + for (int i = 0; i < nnodes; i++) { + int tileIndex = tileIndexMap.getInt(data.nodes.get(i)); + if (tileIndex != -1) { + waytileset |= (1L << tileIndex); + } + } + + // now write way to all tiles hit + for (int tileIndex = 0; tileIndex < 54; tileIndex++) { + if ((waytileset & (1L << tileIndex)) == 0) { + continue; + } + data.writeTo(getOutStreamForTile(tileIndex)); + } + } + + + public int getTileIndexForNid(long nid) { + return tileIndexMap.getInt(nid); + } + + private int getTileIndex(int ilon, int ilat) { + int lon = ilon / 45000000; + int lat = ilat / 30000000; + if (lon < 0 || lon > 7 || lat < 0 || lat > 5) + throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat); + return lon * 6 + lat; + } + + public String getNameForTile(int tileIndex) { + int lon = (tileIndex / 6) * 45 - 180; + int lat = (tileIndex % 6) * 30 - 90; + String slon = lon < 0 ? "W" + (-lon) : "E" + lon; + String slat = lat < 0 ? "S" + (-lat) : "N" + lat; + return slon + "_" + slat + ".wtl"; + } + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter5.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter5.java index 577dccd..25f9268 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter5.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayCutter5.java @@ -1,212 +1,182 @@ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; - -import btools.util.DenseLongMap; -import btools.util.TinyDenseLongMap; - -/** - * WayCutter5 does 2 step in map-processing: - * - * - cut the 45*30 way files into 5*5 pieces - * - create a file containing all border node ids - * - * @author ab - */ -public class WayCutter5 extends MapCreatorBase -{ - private DataOutputStream borderNidsOutStream; - private DenseLongMap tileIndexMap; - private File nodeTilesIn; - private int lonoffset; - private int latoffset; - - public RelationMerger relMerger; - public NodeFilter nodeFilter; - public NodeCutter nodeCutter; - public RestrictionCutter5 restrictionCutter5; - - public static void main(String[] args) throws Exception - { - System.out.println("*** WayCutter5: Soft-Cut way-data into tiles"); - if (args.length != 4) - { - System.out.println("usage: java WayCutter5 " ); - return; - } - new WayCutter5().process( new File( args[0] ), new File( args[1] ), new File( args[2] ), new File( args[3] ) ); - } - - public void process( File nodeTilesIn, File wayTilesIn, File wayTilesOut, File borderNidsOut ) throws Exception - { - this.nodeTilesIn = nodeTilesIn; - this.outTileDir = wayTilesOut; - - borderNidsOutStream = createOutStream( borderNidsOut ); - - new WayIterator( this, true ).processDir( wayTilesIn, ".wtl" ); - - borderNidsOutStream.close(); - } - - @Override - public boolean wayFileStart( File wayfile ) throws Exception - { - // read corresponding node-file into tileIndexMap - String name = wayfile.getName(); - String nodefilename = name.substring( 0, name.length()-3 ) + "ntl"; - File nodefile = new File( nodeTilesIn, nodefilename ); - - tileIndexMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap(); - lonoffset = -1; - latoffset = -1; - - if ( nodeCutter != null ) - { - nodeCutter.nodeFileStart( null ); - } - new NodeIterator( this, nodeCutter != null ).processFile( nodefile ); - - if ( restrictionCutter5 != null ) - { - String resfilename = name.substring( 0, name.length()-3 ) + "rtl"; - File resfile = new File( "restrictions", resfilename ); - - if ( resfile.exists() ) - { - // read restrictions for nodes in nodesMap - DataInputStream di = new DataInputStream( new BufferedInputStream ( new FileInputStream( resfile ) ) ); - int ntr = 0; - try - { - for(;;) - { - RestrictionData res = new RestrictionData( di ); - restrictionCutter5.nextRestriction( res ); - ntr++; - } - } - catch( EOFException eof ) - { - di.close(); - } - System.out.println( "read " + ntr + " turn-restrictions" ); - } - } - return true; - } - - @Override - public void nextNode( NodeData n ) throws Exception - { - if ( nodeFilter != null ) - { - if ( !nodeFilter.isRelevant( n ) ) - { - return; - } - } - if ( nodeCutter != null ) - { - nodeCutter.nextNode( n ); - } - - tileIndexMap.put( n.nid, getTileIndex( n.ilon, n.ilat ) ); - } - - @Override - public void nextWay( WayData data ) throws Exception - { - long waytileset = 0; - int nnodes = data.nodes.size(); - int[] tiForNode = new int[nnodes]; - - // determine the tile-index for each node - for (int i=0; i 0 && tiForNode[i-1] != ti ) || (i+1 < nnodes && tiForNode[i+1] != ti ) ) - { - writeId( borderNidsOutStream, data.nodes.get(i) ); - } - } - } - } - - @Override - public void wayFileEnd( File wayFile ) throws Exception - { - closeTileOutStreams(); - if ( nodeCutter != null ) - { - nodeCutter.nodeFileEnd( null ); - } - if ( restrictionCutter5 != null ) - { - restrictionCutter5.finish(); - } - } - - public int getTileIndexForNid( long nid ) - { - return tileIndexMap.getInt( nid ); - } - - 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 + ".wt5"; - } -} +package btools.mapcreator; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; + +import btools.util.DenseLongMap; +import btools.util.TinyDenseLongMap; + +/** + * WayCutter5 does 2 step in map-processing: + *

+ * - cut the 45*30 way files into 5*5 pieces + * - create a file containing all border node ids + * + * @author ab + */ +public class WayCutter5 extends MapCreatorBase { + private DataOutputStream borderNidsOutStream; + private DenseLongMap tileIndexMap; + private File nodeTilesIn; + private int lonoffset; + private int latoffset; + + public RelationMerger relMerger; + public NodeFilter nodeFilter; + public NodeCutter nodeCutter; + public RestrictionCutter5 restrictionCutter5; + + public static void main(String[] args) throws Exception { + System.out.println("*** WayCutter5: Soft-Cut way-data into tiles"); + if (args.length != 4) { + System.out.println("usage: java WayCutter5 "); + return; + } + new WayCutter5().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3])); + } + + public void process(File nodeTilesIn, File wayTilesIn, File wayTilesOut, File borderNidsOut) throws Exception { + this.nodeTilesIn = nodeTilesIn; + this.outTileDir = wayTilesOut; + + borderNidsOutStream = createOutStream(borderNidsOut); + + new WayIterator(this, true).processDir(wayTilesIn, ".wtl"); + + borderNidsOutStream.close(); + } + + @Override + public boolean wayFileStart(File wayfile) throws Exception { + // read corresponding node-file into tileIndexMap + String name = wayfile.getName(); + String nodefilename = name.substring(0, name.length() - 3) + "ntl"; + File nodefile = new File(nodeTilesIn, nodefilename); + + tileIndexMap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap() : new TinyDenseLongMap(); + lonoffset = -1; + latoffset = -1; + + if (nodeCutter != null) { + nodeCutter.nodeFileStart(null); + } + new NodeIterator(this, nodeCutter != null).processFile(nodefile); + + if (restrictionCutter5 != null) { + String resfilename = name.substring(0, name.length() - 3) + "rtl"; + File resfile = new File("restrictions", resfilename); + + if (resfile.exists()) { + // read restrictions for nodes in nodesMap + DataInputStream di = new DataInputStream(new BufferedInputStream(new FileInputStream(resfile))); + int ntr = 0; + try { + for (; ; ) { + RestrictionData res = new RestrictionData(di); + restrictionCutter5.nextRestriction(res); + ntr++; + } + } catch (EOFException eof) { + di.close(); + } + System.out.println("read " + ntr + " turn-restrictions"); + } + } + return true; + } + + @Override + public void nextNode(NodeData n) throws Exception { + if (nodeFilter != null) { + if (!nodeFilter.isRelevant(n)) { + return; + } + } + if (nodeCutter != null) { + nodeCutter.nextNode(n); + } + + tileIndexMap.put(n.nid, getTileIndex(n.ilon, n.ilat)); + } + + @Override + public void nextWay(WayData data) throws Exception { + long waytileset = 0; + int nnodes = data.nodes.size(); + int[] tiForNode = new int[nnodes]; + + // determine the tile-index for each node + for (int i = 0; i < nnodes; i++) { + int tileIndex = tileIndexMap.getInt(data.nodes.get(i)); + if (tileIndex != -1) { + waytileset |= (1L << tileIndex); + } + tiForNode[i] = tileIndex; + } + + if (relMerger != null) { + relMerger.nextWay(data); + } + + // now write way to all tiles hit + for (int tileIndex = 0; tileIndex < 54; tileIndex++) { + if ((waytileset & (1L << tileIndex)) == 0) { + continue; + } + data.writeTo(getOutStreamForTile(tileIndex)); + } + + // and write edge nodes to the border-nid file + for (int i = 0; i < nnodes; i++) { + int ti = tiForNode[i]; + if (ti != -1) { + if ((i > 0 && tiForNode[i - 1] != ti) || (i + 1 < nnodes && tiForNode[i + 1] != ti)) { + writeId(borderNidsOutStream, data.nodes.get(i)); + } + } + } + } + + @Override + public void wayFileEnd(File wayFile) throws Exception { + closeTileOutStreams(); + if (nodeCutter != null) { + nodeCutter.nodeFileEnd(null); + } + if (restrictionCutter5 != null) { + restrictionCutter5.finish(); + } + } + + public int getTileIndexForNid(long nid) { + return tileIndexMap.getInt(nid); + } + + 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 + ".wt5"; + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayData.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayData.java index d6523cb..a41b004 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayData.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayData.java @@ -1,55 +1,51 @@ -package btools.mapcreator; - -import java.io.DataInputStream; -import java.io.DataOutputStream; - -import btools.util.LongList; - -/** - * Container for waydata on the preprocessor level - * - * @author ab - */ -public class WayData extends MapCreatorBase -{ - public long wid; - public byte[] description; - public LongList nodes; - - public WayData( long id ) - { - wid = id; - nodes = new LongList( 16 ); - } - - public WayData( long id, LongList nodes ) - { - wid = id; - this.nodes = nodes; - } - - public WayData( DataInputStream di ) throws Exception - { - nodes = new LongList( 16 ); - wid = readId( di) ; - int dlen = di.readByte(); description = new byte[dlen]; di.readFully( description ); - for (;;) - { - long nid = readId( di ); - if ( nid == -1 ) break; - nodes.add( nid ); - } - } - - public void writeTo( DataOutputStream dos ) throws Exception - { - writeId( dos, wid ); - dos.writeByte( description.length ); dos.write( description ); - int size = nodes.size(); - for( int i=0; i < size; i++ ) - { - writeId( dos, nodes.get( i ) ); - } - writeId( dos, -1 ); // stopbyte - } -} +package btools.mapcreator; + +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import btools.util.LongList; + +/** + * Container for waydata on the preprocessor level + * + * @author ab + */ +public class WayData extends MapCreatorBase { + public long wid; + public byte[] description; + public LongList nodes; + + public WayData(long id) { + wid = id; + nodes = new LongList(16); + } + + public WayData(long id, LongList nodes) { + wid = id; + this.nodes = nodes; + } + + public WayData(DataInputStream di) throws Exception { + nodes = new LongList(16); + wid = readId(di); + int dlen = di.readByte(); + description = new byte[dlen]; + di.readFully(description); + for (; ; ) { + long nid = readId(di); + if (nid == -1) break; + nodes.add(nid); + } + } + + public void writeTo(DataOutputStream dos) throws Exception { + writeId(dos, wid); + dos.writeByte(description.length); + dos.write(description); + int size = nodes.size(); + for (int i = 0; i < size; i++) { + writeId(dos, nodes.get(i)); + } + writeId(dos, -1); // stopbyte + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayIterator.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayIterator.java index 7c79b8a..7e7cd15 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayIterator.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayIterator.java @@ -1,80 +1,66 @@ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; - -/** - * Iterate over a singe wayfile or a directory - * of waytiles and feed the ways to the callback listener - * - * @author ab - */ -public class WayIterator extends MapCreatorBase -{ - private WayListener listener; - private boolean delete; - private boolean descendingSize; - - public WayIterator( WayListener wayListener, boolean deleteAfterReading ) - { - listener = wayListener; - delete = deleteAfterReading; - } - - public WayIterator( WayListener wayListener, boolean deleteAfterReading, boolean descendingSize ) - { - this( wayListener, deleteAfterReading ); - this.descendingSize = descendingSize; - } - - public void processDir( File indir, String inSuffix ) throws Exception - { - if ( !indir.isDirectory() ) - { - throw new IllegalArgumentException( "not a directory: " + indir ); - } - - File[] af = sortBySizeAsc( indir.listFiles() ); - for( int i=0; i= currentMasterSize ) - { + synchronized boolean setCurrentSlaveSize(long size) throws Exception { + if (size >= currentMasterSize) { return false; } - - while ( size + currentMasterSize + 50000000L > maxFileSize ) - { - System.out.println( "****** slave thread waiting for permission to process file of size " + size - + " currentMaster=" + currentMasterSize + " maxFileSize=" + maxFileSize ); - wait( 10000 ); + + while (size + currentMasterSize + 50000000L > maxFileSize) { + System.out.println("****** slave thread waiting for permission to process file of size " + size + + " currentMaster=" + currentMasterSize + " maxFileSize=" + maxFileSize); + wait(10000); } currentSlaveSize = size; return true; } } - - - private void reset() - { + + private void reset() { minLon = -1; minLat = -1; nodesMap = new CompactLongMap(); borderSet = new CompactLongSet(); } - public static void main( String[] args ) throws Exception - { - System.out.println( "*** WayLinker: Format a region of an OSM map for routing" ); - if ( args.length != 8 ) - { + public static void main(String[] args) throws Exception { + System.out.println("*** WayLinker: Format a region of an OSM map for routing"); + if (args.length != 8) { System.out - .println( "usage: java WayLinker " ); + .println("usage: java WayLinker "); return; } - - new WayLinker().process( new File( args[0] ), new File( args[1] ), new File( args[2] ), new File( args[3] ), new File( args[4] ), new File( args[5] ), new File( - args[6] ), args[7] ); - System.out.println( "dumping bad TRs" ); + new WayLinker().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3]), new File(args[4]), new File(args[5]), new File( + args[6]), args[7]); + + System.out.println("dumping bad TRs"); RestrictionData.dumpBadTRs(); } - public void process( File nodeTilesIn, File wayTilesIn, File borderFileIn, File restrictionsFileIn, File lookupFile, File profileFile, File dataTilesOut, - String dataTilesSuffix ) throws Exception - { + public void process(File nodeTilesIn, File wayTilesIn, File borderFileIn, File restrictionsFileIn, File lookupFile, File profileFile, File dataTilesOut, + String dataTilesSuffix) throws Exception { WayLinker master = new WayLinker(); WayLinker slave = new WayLinker(); slave.isSlave = true; master.isSlave = false; - + ThreadController tc = new ThreadController(); slave.tc = tc; master.tc = tc; - - master._process( nodeTilesIn, wayTilesIn, borderFileIn, restrictionsFileIn, lookupFile, profileFile, dataTilesOut, dataTilesSuffix ); - slave._process( nodeTilesIn, wayTilesIn, borderFileIn, restrictionsFileIn, lookupFile, profileFile, dataTilesOut, dataTilesSuffix ); - - Thread m = new Thread( master ); - Thread s = new Thread( slave ); + + master._process(nodeTilesIn, wayTilesIn, borderFileIn, restrictionsFileIn, lookupFile, profileFile, dataTilesOut, dataTilesSuffix); + slave._process(nodeTilesIn, wayTilesIn, borderFileIn, restrictionsFileIn, lookupFile, profileFile, dataTilesOut, dataTilesSuffix); + + Thread m = new Thread(master); + Thread s = new Thread(slave); m.start(); s.start(); m.join(); s.join(); } - private void _process( File nodeTilesIn, File wayTilesIn, File borderFileIn, File restrictionsFileIn, File lookupFile, File profileFile, File dataTilesOut, - String dataTilesSuffix ) throws Exception - { + private void _process(File nodeTilesIn, File wayTilesIn, File borderFileIn, File restrictionsFileIn, File lookupFile, File profileFile, File dataTilesOut, + String dataTilesSuffix) throws Exception { this.nodeTilesIn = nodeTilesIn; this.wayTilesIn = wayTilesIn; - this.trafficTilesIn = new File( "../traffic" ); + this.trafficTilesIn = new File("../traffic"); this.dataTilesOut = dataTilesOut; this.borderFileIn = borderFileIn; this.restrictionsFileIn = restrictionsFileIn; @@ -180,114 +163,92 @@ public class WayLinker extends MapCreatorBase implements Runnable BExpressionMetaData meta = new BExpressionMetaData(); // read lookup + profile for lookup-version + access-filter - expctxWay = new BExpressionContextWay( meta ); - meta.readMetaData( lookupFile ); + expctxWay = new BExpressionContextWay(meta); + meta.readMetaData(lookupFile); lookupVersion = meta.lookupVersion; lookupMinorVersion = meta.lookupMinorVersion; - expctxWay.parseFile( profileFile, "global" ); + expctxWay.parseFile(profileFile, "global"); creationTimeStamp = System.currentTimeMillis(); - abUnifier = new ByteArrayUnifier( 16384, false ); + abUnifier = new ByteArrayUnifier(16384, false); - skipEncodingCheck = Boolean.getBoolean( "skipEncodingCheck" ); + skipEncodingCheck = Boolean.getBoolean("skipEncodingCheck"); } - + @Override - public void run() - { - try - { + public void run() { + try { // then process all segments - new WayIterator( this, true, !isSlave ).processDir( wayTilesIn, ".wt5" ); - } - catch( Exception e ) - { - System.out.println( "******* thread (slave=" + isSlave + ") got Exception: " + e ); - throw new RuntimeException( e ); - } - finally - { - if (!isSlave) - { - tc.setCurrentMasterSize( 0L ); + new WayIterator(this, true, !isSlave).processDir(wayTilesIn, ".wt5"); + } catch (Exception e) { + System.out.println("******* thread (slave=" + isSlave + ") got Exception: " + e); + throw new RuntimeException(e); + } finally { + if (!isSlave) { + tc.setCurrentMasterSize(0L); } } } @Override - public boolean wayFileStart( File wayfile ) throws Exception - { + public boolean wayFileStart(File wayfile) throws Exception { // master/slave logic: // total memory size should stay below a maximum // and no file should be processed twice - long filesize = wayfile.length(); + long filesize = wayfile.length(); - System.out.println( "**** wayFileStart() for isSlave=" + isSlave + " size=" + filesize ); + System.out.println("**** wayFileStart() for isSlave=" + isSlave + " size=" + filesize); - if ( isSlave ) - { - if ( !tc.setCurrentSlaveSize( filesize ) ) - { + if (isSlave) { + if (!tc.setCurrentSlaveSize(filesize)) { + return false; + } + } else { + if (!tc.setCurrentMasterSize(filesize)) { return false; } } - else - { - if ( !tc.setCurrentMasterSize( filesize ) ) - { - return false; - } - } - - - - - - File trafficFile = fileFromTemplate( wayfile, trafficTilesIn, "trf" ); + + + File trafficFile = fileFromTemplate(wayfile, trafficTilesIn, "trf"); // process corresponding node-file, if any - File nodeFile = fileFromTemplate( wayfile, nodeTilesIn, "u5d" ); - if ( nodeFile.exists() ) - { + File nodeFile = fileFromTemplate(wayfile, nodeTilesIn, "u5d"); + if (nodeFile.exists()) { reset(); // read the border file readingBorder = true; - new NodeIterator( this, false ).processFile( borderFileIn ); - borderSet = new FrozenLongSet( borderSet ); + new NodeIterator(this, false).processFile(borderFileIn); + borderSet = new FrozenLongSet(borderSet); // read this tile's nodes readingBorder = false; - new NodeIterator( this, true ).processFile( nodeFile ); + new NodeIterator(this, true).processFile(nodeFile); // freeze the nodes-map - FrozenLongMap nodesMapFrozen = new FrozenLongMap( nodesMap ); + FrozenLongMap nodesMapFrozen = new FrozenLongMap(nodesMap); nodesMap = nodesMapFrozen; - File restrictionFile = fileFromTemplate( wayfile, new File( nodeTilesIn.getParentFile(), "restrictions55" ), "rt5" ); + File restrictionFile = fileFromTemplate(wayfile, new File(nodeTilesIn.getParentFile(), "restrictions55"), "rt5"); // read restrictions for nodes in nodesMap - if ( restrictionFile.exists() ) - { - DataInputStream di = new DataInputStream( new BufferedInputStream ( new FileInputStream( restrictionFile ) ) ); + if (restrictionFile.exists()) { + DataInputStream di = new DataInputStream(new BufferedInputStream(new FileInputStream(restrictionFile))); int ntr = 0; - try - { - for(;;) - { - RestrictionData res = new RestrictionData( di ); - OsmNodeP n = nodesMap.get( res.viaNid ); - if ( n != null ) - { - if ( ! ( n instanceof OsmNodePT ) ) - { - n = new OsmNodePT( n ); - nodesMap.put( res.viaNid, n ); + try { + for (; ; ) { + RestrictionData res = new RestrictionData(di); + OsmNodeP n = nodesMap.get(res.viaNid); + if (n != null) { + if (!(n instanceof OsmNodePT)) { + n = new OsmNodePT(n); + nodesMap.put(res.viaNid, n); } OsmNodePT nt = (OsmNodePT) n; res.viaLon = nt.ilon; @@ -297,55 +258,49 @@ public class WayLinker extends MapCreatorBase implements Runnable ntr++; } } - } - catch( EOFException eof ) - { + } catch (EOFException eof) { di.close(); } - System.out.println( "read " + ntr + " turn-restrictions" ); + System.out.println("read " + ntr + " turn-restrictions"); } nodesList = nodesMapFrozen.getValueList(); } // read a traffic-file, if any - if ( trafficFile.exists() ) - { - trafficMap = new OsmTrafficMap( expctxWay ); - trafficMap.loadAll( trafficFile, minLon, minLat, minLon + 5000000, minLat + 5000000, false ); + if (trafficFile.exists()) { + trafficMap = new OsmTrafficMap(expctxWay); + trafficMap.loadAll(trafficFile, minLon, minLat, minLon + 5000000, minLat + 5000000, false); } return true; } @Override - public void nextNode( NodeData data ) throws Exception - { - OsmNodeP n = data.description == null ? new OsmNodeP() : new OsmNodePT( data.description ); + public void nextNode(NodeData data) throws Exception { + OsmNodeP n = data.description == null ? new OsmNodeP() : new OsmNodePT(data.description); n.ilon = data.ilon; n.ilat = data.ilat; n.selev = data.selev; - if ( readingBorder || ( !borderSet.contains( data.nid ) ) ) - { - nodesMap.fastPut( data.nid, n ); + if (readingBorder || (!borderSet.contains(data.nid))) { + nodesMap.fastPut(data.nid, n); } - if ( readingBorder ) - { + if (readingBorder) { n.bits |= OsmNodeP.BORDER_BIT; - borderSet.fastAdd( data.nid ); + borderSet.fastAdd(data.nid); return; } // remember the segment coords - int min_lon = ( n.ilon / 5000000 ) * 5000000; - int min_lat = ( n.ilat / 5000000 ) * 5000000; - if ( minLon == -1 ) + int min_lon = (n.ilon / 5000000) * 5000000; + int min_lat = (n.ilat / 5000000) * 5000000; + if (minLon == -1) minLon = min_lon; - if ( minLat == -1 ) + if (minLat == -1) minLat = min_lat; - if ( minLat != min_lat || minLon != min_lon ) - throw new IllegalArgumentException( "inconsistent node: " + n.ilon + " " + n.ilat ); + if (minLat != min_lat || minLon != min_lon) + throw new IllegalArgumentException("inconsistent node: " + n.ilon + " " + n.ilat); } // check if one of the nodes has a turn-restriction with @@ -354,39 +309,30 @@ public class WayLinker extends MapCreatorBase implements Runnable // starts or ends at it's via node. However, we allow // ways not ending at the via node, and in this case we take // the leg according to the mapped direction - private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w ) - { - checkRestriction( n1, n2, w, true ); - checkRestriction( n2, n1, w, false ); + private void checkRestriction(OsmNodeP n1, OsmNodeP n2, WayData w) { + checkRestriction(n1, n2, w, true); + checkRestriction(n2, n1, w, false); } - private void checkRestriction( OsmNodeP n1, OsmNodeP n2, WayData w, boolean checkFrom ) - { + private void checkRestriction(OsmNodeP n1, OsmNodeP n2, WayData w, boolean checkFrom) { RestrictionData r = n2.getFirstRestriction(); - while ( r != null ) - { - if ( r.fromWid == w.wid ) - { - if ( r.fromLon == 0 || checkFrom ) - { + while (r != null) { + if (r.fromWid == w.wid) { + if (r.fromLon == 0 || checkFrom) { r.fromLon = n1.ilon; r.fromLat = n1.ilat; n1.bits |= OsmNodeP.DP_SURVIVOR_BIT; - if ( !isEndNode( n2, w ) ) - { + if (!isEndNode(n2, w)) { r.badWayMatch = true; } } } - if ( r.toWid == w.wid ) - { - if ( r.toLon == 0 || !checkFrom ) - { + if (r.toWid == w.wid) { + if (r.toLon == 0 || !checkFrom) { r.toLon = n1.ilon; r.toLat = n1.ilat; n1.bits |= OsmNodeP.DP_SURVIVOR_BIT; - if ( !isEndNode( n2, w ) ) - { + if (!isEndNode(n2, w)) { r.badWayMatch = true; } } @@ -394,56 +340,50 @@ public class WayLinker extends MapCreatorBase implements Runnable r = r.next; } } - - private boolean isEndNode( OsmNodeP n, WayData w ) - { - return n == nodesMap.get( w.nodes.get( 0 ) ) || n == nodesMap.get( w.nodes.get( w.nodes.size() - 1 ) ); + + private boolean isEndNode(OsmNodeP n, WayData w) { + return n == nodesMap.get(w.nodes.get(0)) || n == nodesMap.get(w.nodes.get(w.nodes.size() - 1)); } @Override - public void nextWay( WayData way ) throws Exception - { - byte[] description = abUnifier.unify( way.description ); + public void nextWay(WayData way) throws Exception { + byte[] description = abUnifier.unify(way.description); // filter according to profile - expctxWay.evaluate( false, description ); + expctxWay.evaluate(false, description); boolean ok = expctxWay.getCostfactor() < 10000.; - expctxWay.evaluate( true, description ); + expctxWay.evaluate(true, description); ok |= expctxWay.getCostfactor() < 10000.; - if ( !ok ) + if (!ok) return; byte wayBits = 0; - expctxWay.decode( description ); - if ( !expctxWay.getBooleanLookupValue( "bridge" ) ) + expctxWay.decode(description); + if (!expctxWay.getBooleanLookupValue("bridge")) wayBits |= OsmNodeP.NO_BRIDGE_BIT; - if ( !expctxWay.getBooleanLookupValue( "tunnel" ) ) + if (!expctxWay.getBooleanLookupValue("tunnel")) wayBits |= OsmNodeP.NO_TUNNEL_BIT; OsmNodeP n1 = null; OsmNodeP n2 = null; - for ( int i = 0; i < way.nodes.size(); i++ ) - { - long nid = way.nodes.get( i ); + for (int i = 0; i < way.nodes.size(); i++) { + long nid = way.nodes.get(i); n1 = n2; - n2 = nodesMap.get( nid ); + n2 = nodesMap.get(nid); - if ( n1 != null && n2 != null && n1 != n2 ) - { - checkRestriction( n1, n2, way ); - - OsmLinkP link = n2.createLink( n1 ); + if (n1 != null && n2 != null && n1 != n2) { + checkRestriction(n1, n2, way); + + OsmLinkP link = n2.createLink(n1); link.descriptionBitmap = description; - if ( n1.ilon / cellsize != n2.ilon / cellsize || n1.ilat / cellsize != n2.ilat / cellsize ) - { + if (n1.ilon / cellsize != n2.ilon / cellsize || n1.ilat / cellsize != n2.ilat / cellsize) { n2.incWayCount(); // force first node after cell-change to be a - // network node + // network node } } - if ( n2 != null ) - { + if (n2 != null) { n2.bits |= wayBits; n2.incWayCount(); } @@ -451,8 +391,7 @@ public class WayLinker extends MapCreatorBase implements Runnable } @Override - public void wayFileEnd( File wayfile ) throws Exception - { + public void wayFileEnd(File wayfile) throws Exception { int ncaches = divisor * divisor; int indexsize = ncaches * 4; @@ -466,126 +405,109 @@ public class WayLinker extends MapCreatorBase implements Runnable int maxLat = minLat + 5000000; // cleanup duplicate targets - for ( OsmNodeP n : nodesList ) - { - if ( n == null || n.getFirstLink() == null || n.isTransferNode() ) + for (OsmNodeP n : nodesList) { + if (n == null || n.getFirstLink() == null || n.isTransferNode()) continue; n.checkDuplicateTargets(); } // write segment data to individual files { - int nLonSegs = ( maxLon - minLon ) / 1000000; - int nLatSegs = ( maxLat - minLat ) / 1000000; + int nLonSegs = (maxLon - minLon) / 1000000; + int nLatSegs = (maxLat - minLat) / 1000000; // sort the nodes into segments - LazyArrayOfLists seglists = new LazyArrayOfLists( nLonSegs * nLatSegs ); - for ( OsmNodeP n : nodesList ) - { - if ( n == null || n.getFirstLink() == null || n.isTransferNode() ) + LazyArrayOfLists seglists = new LazyArrayOfLists(nLonSegs * nLatSegs); + for (OsmNodeP n : nodesList) { + if (n == null || n.getFirstLink() == null || n.isTransferNode()) continue; - if ( n.ilon < minLon || n.ilon >= maxLon || n.ilat < minLat || n.ilat >= maxLat ) + if (n.ilon < minLon || n.ilon >= maxLon || n.ilat < minLat || n.ilat >= maxLat) continue; - int lonIdx = ( n.ilon - minLon ) / 1000000; - int latIdx = ( n.ilat - minLat ) / 1000000; + int lonIdx = (n.ilon - minLon) / 1000000; + int latIdx = (n.ilat - minLat) / 1000000; int tileIndex = lonIdx * nLatSegs + latIdx; - seglists.getList( tileIndex ).add( n ); + seglists.getList(tileIndex).add(n); } nodesList = null; seglists.trimAll(); // open the output file - File outfile = fileFromTemplate( wayfile, dataTilesOut, dataTilesSuffix ); - DataOutputStream os = createOutStream( outfile ); + File outfile = fileFromTemplate(wayfile, dataTilesOut, dataTilesSuffix); + DataOutputStream os = createOutStream(outfile); long[] fileIndex = new long[25]; int[] fileHeaderCrcs = new int[25]; // write 5*5 index dummy - for ( int i55 = 0; i55 < 25; i55++ ) - { - os.writeLong( 0 ); + for (int i55 = 0; i55 < 25; i55++) { + os.writeLong(0); } long filepos = 200L; // sort further in 1/divisor-degree squares - for ( int lonIdx = 0; lonIdx < nLonSegs; lonIdx++ ) - { - for ( int latIdx = 0; latIdx < nLatSegs; latIdx++ ) - { + for (int lonIdx = 0; lonIdx < nLonSegs; lonIdx++) { + for (int latIdx = 0; latIdx < nLatSegs; latIdx++) { int tileIndex = lonIdx * nLatSegs + latIdx; - if ( seglists.getSize( tileIndex ) > 0 ) - { - List nlist = seglists.getList( tileIndex ); + if (seglists.getSize(tileIndex) > 0) { + List nlist = seglists.getList(tileIndex); - LazyArrayOfLists subs = new LazyArrayOfLists( ncaches ); + LazyArrayOfLists subs = new LazyArrayOfLists(ncaches); byte[][] subByteArrays = new byte[ncaches][]; - for ( int ni = 0; ni < nlist.size(); ni++ ) - { - OsmNodeP n = nlist.get( ni ); - int subLonIdx = ( n.ilon - minLon ) / cellsize - divisor * lonIdx; - int subLatIdx = ( n.ilat - minLat ) / cellsize - divisor * latIdx; + for (int ni = 0; ni < nlist.size(); ni++) { + OsmNodeP n = nlist.get(ni); + int subLonIdx = (n.ilon - minLon) / cellsize - divisor * lonIdx; + int subLatIdx = (n.ilat - minLat) / cellsize - divisor * latIdx; int si = subLatIdx * divisor + subLonIdx; - subs.getList( si ).add( n ); + subs.getList(si).add(n); } subs.trimAll(); int[] posIdx = new int[ncaches]; int pos = indexsize; - for ( int si = 0; si < ncaches; si++ ) - { - List subList = subs.getList( si ); + for (int si = 0; si < ncaches; si++) { + List subList = subs.getList(si); int size = subList.size(); - if ( size > 0 ) - { - OsmNodeP n0 = subList.get( 0 ); + if (size > 0) { + OsmNodeP n0 = subList.get(0); int lonIdxDiv = n0.ilon / cellsize; int latIdxDiv = n0.ilat / cellsize; - MicroCache mc = new MicroCache2( size, abBuf2, lonIdxDiv, latIdxDiv, divisor ); + MicroCache mc = new MicroCache2(size, abBuf2, lonIdxDiv, latIdxDiv, divisor); // sort via treemap TreeMap sortedList = new TreeMap(); - for ( OsmNodeP n : subList ) - { + for (OsmNodeP n : subList) { long longId = n.getIdFromPos(); - int shrinkid = mc.shrinkId( longId ); - if ( mc.expandId( shrinkid ) != longId ) - { - throw new IllegalArgumentException( "inconstistent shrinking: " + longId ); + int shrinkid = mc.shrinkId(longId); + if (mc.expandId(shrinkid) != longId) { + throw new IllegalArgumentException("inconstistent shrinking: " + longId); } - sortedList.put( Integer.valueOf( shrinkid ), n ); + sortedList.put(Integer.valueOf(shrinkid), n); } - for ( OsmNodeP n : sortedList.values() ) - { - n.writeNodeData( mc, trafficMap ); + for (OsmNodeP n : sortedList.values()) { + n.writeNodeData(mc, trafficMap); } - if ( mc.getSize() > 0 ) - { + if (mc.getSize() > 0) { byte[] subBytes; - for ( ;; ) - { - int len = mc.encodeMicroCache( abBuf1 ); + for (; ; ) { + int len = mc.encodeMicroCache(abBuf1); subBytes = new byte[len]; - System.arraycopy( abBuf1, 0, subBytes, 0, len ); - - if ( skipEncodingCheck ) - { + System.arraycopy(abBuf1, 0, subBytes, 0, len); + + if (skipEncodingCheck) { break; } // cross-check the encoding: re-instantiate the cache - MicroCache mc2 = new MicroCache2( new StatCoderContext( subBytes ), new DataBuffers( null ), lonIdxDiv, latIdxDiv, divisor, null, null ); + MicroCache mc2 = new MicroCache2(new StatCoderContext(subBytes), new DataBuffers(null), lonIdxDiv, latIdxDiv, divisor, null, null); // ..and check if still the same - String diffMessage = mc.compareWith( mc2 ); - if ( diffMessage != null ) - { - if ( MicroCache.debug ) - throw new RuntimeException( "encoding crosscheck failed: " + diffMessage ); + String diffMessage = mc.compareWith(mc2); + if (diffMessage != null) { + if (MicroCache.debug) + throw new RuntimeException("encoding crosscheck failed: " + diffMessage); else MicroCache.debug = true; - } - else + } else break; } pos += subBytes.length + 4; // reserve 4 bytes for crc @@ -595,16 +517,14 @@ public class WayLinker extends MapCreatorBase implements Runnable posIdx[si] = pos; } - byte[] abSubIndex = compileSubFileIndex( posIdx ); - fileHeaderCrcs[tileIndex] = Crc32.crc( abSubIndex, 0, abSubIndex.length ); - os.write( abSubIndex, 0, abSubIndex.length ); - for ( int si = 0; si < ncaches; si++ ) - { + byte[] abSubIndex = compileSubFileIndex(posIdx); + fileHeaderCrcs[tileIndex] = Crc32.crc(abSubIndex, 0, abSubIndex.length); + os.write(abSubIndex, 0, abSubIndex.length); + for (int si = 0; si < ncaches; si++) { byte[] ab = subByteArrays[si]; - if ( ab != null ) - { - os.write( ab ); - os.writeInt( Crc32.crc( ab, 0, ab.length ) ^ microCacheEncoding ); + if (ab != null) { + os.write(ab); + os.writeInt(Crc32.crc(ab, 0, ab.length) ^ microCacheEncoding); } } filepos += pos; @@ -613,52 +533,46 @@ public class WayLinker extends MapCreatorBase implements Runnable } } - byte[] abFileIndex = compileFileIndex( fileIndex, lookupVersion, lookupMinorVersion ); + byte[] abFileIndex = compileFileIndex(fileIndex, lookupVersion, lookupMinorVersion); // write extra data: timestamp + index-checksums - os.writeLong( creationTimeStamp ); - os.writeInt( Crc32.crc( abFileIndex, 0, abFileIndex.length ) ^ microCacheEncoding ); - for ( int i55 = 0; i55 < 25; i55++ ) - { - os.writeInt( fileHeaderCrcs[i55] ); + os.writeLong(creationTimeStamp); + os.writeInt(Crc32.crc(abFileIndex, 0, abFileIndex.length) ^ microCacheEncoding); + for (int i55 = 0; i55 < 25; i55++) { + os.writeInt(fileHeaderCrcs[i55]); } os.close(); // re-open random-access to write file-index - RandomAccessFile ra = new RandomAccessFile( outfile, "rw" ); - ra.write( abFileIndex, 0, abFileIndex.length ); + RandomAccessFile ra = new RandomAccessFile(outfile, "rw"); + ra.write(abFileIndex, 0, abFileIndex.length); ra.close(); } - if ( trafficMap != null ) - { + if (trafficMap != null) { trafficMap.finish(); trafficMap = null; } - System.out.println( "**** codec stats: *******\n" + StatCoderContext.getBitReport() ); + System.out.println("**** codec stats: *******\n" + StatCoderContext.getBitReport()); } - private byte[] compileFileIndex( long[] fileIndex, short lookupVersion, short lookupMinorVersion ) throws Exception - { + private byte[] compileFileIndex(long[] fileIndex, short lookupVersion, short lookupMinorVersion) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream( bos ); - for ( int i55 = 0; i55 < 25; i55++ ) - { + DataOutputStream dos = new DataOutputStream(bos); + for (int i55 = 0; i55 < 25; i55++) { long versionPrefix = i55 == 1 ? lookupMinorVersion : lookupVersion; versionPrefix <<= 48; - dos.writeLong( fileIndex[i55] | versionPrefix ); + dos.writeLong(fileIndex[i55] | versionPrefix); } dos.close(); return bos.toByteArray(); } - private byte[] compileSubFileIndex( int[] posIdx ) throws Exception - { + private byte[] compileSubFileIndex(int[] posIdx) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream( bos ); - for ( int si = 0; si < posIdx.length; si++ ) - { - dos.writeInt( posIdx[si] ); + DataOutputStream dos = new DataOutputStream(bos); + for (int si = 0; si < posIdx.length; si++) { + dos.writeInt(posIdx[si]); } dos.close(); return bos.toByteArray(); diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayListener.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayListener.java index d417f73..11f1000 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayListener.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayListener.java @@ -1,17 +1,16 @@ -package btools.mapcreator; - -import java.io.File; - -/** - * Callbacklistener for WayIterator - * - * @author ab - */ -public interface WayListener -{ - boolean wayFileStart( File wayfile ) throws Exception; - - void nextWay( WayData data ) throws Exception; - - void wayFileEnd( File wayfile ) throws Exception; -} +package btools.mapcreator; + +import java.io.File; + +/** + * Callbacklistener for WayIterator + * + * @author ab + */ +public interface WayListener { + boolean wayFileStart(File wayfile) throws Exception; + + void nextWay(WayData data) throws Exception; + + void wayFileEnd(File wayfile) throws Exception; +} diff --git a/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java b/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java index a9e555e..090d6d1 100644 --- a/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java +++ b/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java @@ -1,54 +1,53 @@ -package btools.mapcreator; - -import java.util.Random; -import java.util.HashMap; - -import org.junit.Assert; -import org.junit.Test; -import java.net.URL; -import java.io.File; - -public class MapcreatorTest -{ - @Test - public void mapcreatorTest() throws Exception - { - URL mapurl = this.getClass().getResource( "/dreieich.osm.gz" ); - Assert.assertTrue( "test-osm-map dreieich.osm not found", mapurl != null ); - File mapFile = new File(mapurl.getFile()); - File workingDir = mapFile.getParentFile(); - File profileDir = new File( workingDir, "/../../../../misc/profiles2" ); - File tmpdir = new File( workingDir, "tmp" ); - tmpdir.mkdir(); - - File nodes = new File( tmpdir, "nodetiles" ); - nodes.mkdir(); - File ways = new File( tmpdir, "waytiles" ); - ways.mkdir(); - File nodes55 = new File( tmpdir, "nodes55" ); - nodes55.mkdir(); - File ways55 = new File( tmpdir, "waytiles55" ); - ways55.mkdir(); - File lookupFile = new File( profileDir, "lookups.dat" ); - File relFile = new File( tmpdir, "cycleways.dat" ); - File resFile = new File( tmpdir, "restrictions.dat" ); - File profileAll = new File( profileDir, "all.brf" ); - File profileReport = new File( profileDir, "trekking.brf" ); - File profileCheck = new File( profileDir, "softaccess.brf" ); - File borderFile = new File( tmpdir, "bordernids.dat" ); - - new OsmFastCutter().doCut( lookupFile, nodes, ways, nodes55, ways55, borderFile, relFile, resFile, profileAll, profileReport, profileCheck, mapFile ); - - - // run PosUnifier - File unodes55 = new File( tmpdir, "unodes55" ); - File bordernodes = new File( tmpdir, "bordernodes.dat" ); - unodes55.mkdir(); - new PosUnifier().process( nodes55, unodes55, borderFile, bordernodes, "/private-backup/srtm" ); - - // run WayLinker - File segments = new File( tmpdir, "segments" ); - segments.mkdir(); - new WayLinker().process( unodes55, ways55, bordernodes, resFile, lookupFile, profileAll, segments, "rd5" ); - } -} +package btools.mapcreator; + +import java.util.Random; +import java.util.HashMap; + +import org.junit.Assert; +import org.junit.Test; + +import java.net.URL; +import java.io.File; + +public class MapcreatorTest { + @Test + public void mapcreatorTest() throws Exception { + URL mapurl = this.getClass().getResource("/dreieich.osm.gz"); + Assert.assertTrue("test-osm-map dreieich.osm not found", mapurl != null); + File mapFile = new File(mapurl.getFile()); + File workingDir = mapFile.getParentFile(); + File profileDir = new File(workingDir, "/../../../../misc/profiles2"); + File tmpdir = new File(workingDir, "tmp"); + tmpdir.mkdir(); + + File nodes = new File(tmpdir, "nodetiles"); + nodes.mkdir(); + File ways = new File(tmpdir, "waytiles"); + ways.mkdir(); + File nodes55 = new File(tmpdir, "nodes55"); + nodes55.mkdir(); + File ways55 = new File(tmpdir, "waytiles55"); + ways55.mkdir(); + File lookupFile = new File(profileDir, "lookups.dat"); + File relFile = new File(tmpdir, "cycleways.dat"); + File resFile = new File(tmpdir, "restrictions.dat"); + File profileAll = new File(profileDir, "all.brf"); + File profileReport = new File(profileDir, "trekking.brf"); + File profileCheck = new File(profileDir, "softaccess.brf"); + File borderFile = new File(tmpdir, "bordernids.dat"); + + new OsmFastCutter().doCut(lookupFile, nodes, ways, nodes55, ways55, borderFile, relFile, resFile, profileAll, profileReport, profileCheck, mapFile); + + + // run PosUnifier + File unodes55 = new File(tmpdir, "unodes55"); + File bordernodes = new File(tmpdir, "bordernodes.dat"); + unodes55.mkdir(); + new PosUnifier().process(nodes55, unodes55, borderFile, bordernodes, "/private-backup/srtm"); + + // run WayLinker + File segments = new File(tmpdir, "segments"); + segments.mkdir(); + new WayLinker().process(unodes55, ways55, bordernodes, resFile, lookupFile, profileAll, segments, "rd5"); + } +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java index dc7191e..216d8dd 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java @@ -14,113 +14,100 @@ import btools.util.IByteArrayUnifier; * DirectWeaver does the same decoding as MicroCache2, but decodes directly * into the instance-graph, not into the intermediate nodes-cache */ -public final class DirectWeaver extends ByteDataWriter -{ +public final class DirectWeaver extends ByteDataWriter { private long id64Base; private int size = 0; - public DirectWeaver( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes ) - { - super( null ); + public DirectWeaver(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes) { + super(null); int cellsize = 1000000 / divisor; - id64Base = ((long)(lonIdx*cellsize))<<32 | (latIdx*cellsize); + id64Base = ((long) (lonIdx * cellsize)) << 32 | (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); int[] faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2; - bc.decodeSortedArray( faid, 0, size, 29, 0 ); - + bc.decodeSortedArray(faid, 0, size, 29, 0); + OsmNode[] nodes = new OsmNode[size]; - for( int n = 0; n> 32 ); - int ilat = (int) ( id & 0xffffffff ); - OsmNode node = hollowNodes.get( ilon, ilat ); - if ( node == null ) - { - node = new OsmNode( ilon, ilat ); - } - else - { + for (int n = 0; n < size; n++) { + long id = expandId(faid[n]); + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + OsmNode node = hollowNodes.get(ilon, ilat); + if (node == null) { + node = new OsmNode(ilon, ilat); + } else { node.visitID = 1; - hollowNodes.remove( node ); + hollowNodes.remove(node); } nodes[n] = node; } - - int netdatasize = bc.decodeNoisyNumber( 10 ); // (not needed for direct weaving) + + int netdatasize = bc.decodeNoisyNumber(10); // (not needed for direct weaving) ab = dataBuffers.bbuf1; aboffset = 0; int selev = 0; - for( int n=0; n 0 ) - { + if (aboffset > 0) { geometry = new byte[aboffset]; - System.arraycopy( ab, 0, geometry, 0, aboffset ); + System.arraycopy(ab, 0, geometry, 0, aboffset); } - - if ( nodeIdx != n ) // valid internal (forward-) link + + if (nodeIdx != n) // valid internal (forward-) link { OsmNode node2 = nodes[nodeIdx]; - OsmLink link = node.isLinkUnused() ? node : ( node2.isLinkUnused() ? node2 : null ); - if ( link == null ) - { + OsmLink link = node.isLinkUnused() ? node : (node2.isLinkUnused() ? node2 : null); + if (link == null) { link = new OsmLink(); } link.descriptionBitmap = wayTags.data; link.geometry = geometry; - node.addLink( link, isReverse, node2 ); - } - else // weave external link + node.addLink(link, isReverse, node2); + } else // weave external link { - node.addLink( linklon, linklat, wayTags.data, geometry, hollowNodes, isReverse ); + node.addLink(linklon, linklat, wayTags.data, geometry, hollowNodes, isReverse); node.visitID = 1; } } } // ... loop over links } // ... loop over nodes - hollowNodes.cleanupAndCount( nodes ); + hollowNodes.cleanupAndCount(nodes); } private static final long[] id32_00 = new long[1024]; private static final long[] id32_10 = new long[1024]; private static final long[] id32_20 = new long[1024]; - static - { - for( int i=0; i<1024; i++ ) - { - id32_00[i] = _expandId( i ); - id32_10[i] = _expandId( i << 10 ); - id32_20[i] = _expandId( i << 20 ); + static { + for (int i = 0; i < 1024; i++) { + id32_00[i] = _expandId(i); + id32_10[i] = _expandId(i << 10); + id32_20[i] = _expandId(i << 20); } } - private static long _expandId( int id32 ) - { + private static long _expandId(int id32) { int dlon = 0; int dlat = 0; - for( int bm = 1; bm < 0x8000; bm <<= 1 ) - { - if ( (id32 & 1) != 0 ) dlon |= bm; - if ( (id32 & 2) != 0 ) dlat |= bm; + for (int bm = 1; bm < 0x8000; bm <<= 1) { + if ((id32 & 1) != 0) dlon |= bm; + if ((id32 & 2) != 0) dlat |= bm; id32 >>= 2; } - return ((long)dlon)<<32 | dlat; + return ((long) dlon) << 32 | dlat; } - public long expandId( int id32 ) - { - return id64Base + id32_00[ id32 & 1023 ] + id32_10[ (id32>>10) & 1023 ] + id32_20[ (id32>>20) & 1023 ]; + public long expandId(int id32) { + return id64Base + id32_00[id32 & 1023] + id32_10[(id32 >> 10) & 1023] + id32_20[(id32 >> 20) & 1023]; } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/GeometryDecoder.java b/brouter-mapaccess/src/main/java/btools/mapaccess/GeometryDecoder.java index f297547..ea2fd86 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/GeometryDecoder.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/GeometryDecoder.java @@ -8,9 +8,8 @@ package btools.mapaccess; import btools.util.ByteDataReader; -public final class GeometryDecoder -{ - private ByteDataReader r = new ByteDataReader( null ); +public final class GeometryDecoder { + private ByteDataReader r = new ByteDataReader(null); private OsmTransferNode[] cachedNodes; private int nCachedNodes = 128; @@ -19,63 +18,53 @@ public final class GeometryDecoder private boolean lastReverse; private byte[] lastGeometry; - public GeometryDecoder() - { + public GeometryDecoder() { // create some caches cachedNodes = new OsmTransferNode[nCachedNodes]; - for( int i=0; i( 4 ); + } else { + fileCache = new HashMap(4); fileRows = new OsmFile[180][]; dataBuffers = new DataBuffers(); - secondarySegmentsDir = StorageConfigHelper.getSecondarySegmentDir( segmentDir ); + secondarySegmentsDir = StorageConfigHelper.getSecondarySegmentDir(segmentDir); } ghostSum = cacheSum; } - - public void clean( boolean all ) - { - for ( OsmFile[] fileRow : fileRows ) - { - if ( fileRow == null ) - continue; - for ( OsmFile osmf : fileRow ) - { - osmf.clean( all); - } + + public void clean(boolean all) { + for (OsmFile[] fileRow : fileRows) { + if (fileRow == null) + continue; + for (OsmFile osmf : fileRow) { + osmf.clean(all); } + } } // if the cache sum exceeded a threshold, // clean all ghosts and enable garbage collection - private void checkEnableCacheCleaning() - { - if ( cacheSum < maxmemtiles ) - { + private void checkEnableCacheCleaning() { + if (cacheSum < maxmemtiles) { return; } - for ( int i = 0; i < fileRows.length; i++ ) - { + for (int i = 0; i < fileRows.length; i++) { OsmFile[] fileRow = fileRows[i]; - if ( fileRow == null ) - { + if (fileRow == null) { continue; } - for ( OsmFile osmf : fileRow ) - { - if ( garbageCollectionEnabled && !ghostCleaningDone ) - { + for (OsmFile osmf : fileRow) { + if (garbageCollectionEnabled && !ghostCleaningDone) { cacheSum -= osmf.cleanGhosts(); - } - else - { + } else { cacheSum -= osmf.collectAll(); } } } - - if ( garbageCollectionEnabled ) - { + + if (garbageCollectionEnabled) { ghostCleaningDone = true; maxmemtiles *= 2; - } - else - { + } else { cacheSumClean = cacheSum; garbageCollectionEnabled = true; } } - public int loadSegmentFor( int ilon, int ilat ) - { - MicroCache mc = getSegmentFor( ilon, ilat ); + public int loadSegmentFor(int ilon, int ilat) { + MicroCache mc = getSegmentFor(ilon, ilat); return mc == null ? 0 : mc.getSize(); } - public MicroCache getSegmentFor( int ilon, int ilat ) - { - try - { + public MicroCache getSegmentFor(int ilon, int ilat) { + try { int lonDegree = ilon / 1000000; int latDegree = ilat / 1000000; OsmFile osmf = null; OsmFile[] fileRow = fileRows[latDegree]; int ndegrees = fileRow == null ? 0 : fileRow.length; - for ( int i = 0; i < ndegrees; i++ ) - { - if ( fileRow[i].lonDegree == lonDegree ) - { + for (int i = 0; i < ndegrees; i++) { + if (fileRow[i].lonDegree == lonDegree) { osmf = fileRow[i]; break; } } - if ( osmf == null ) - { - osmf = fileForSegment( lonDegree, latDegree ); + if (osmf == null) { + osmf = fileForSegment(lonDegree, latDegree); OsmFile[] newFileRow = new OsmFile[ndegrees + 1]; - for ( int i = 0; i < ndegrees; i++ ) - { + for (int i = 0; i < ndegrees; i++) { newFileRow[i] = fileRow[i]; } newFileRow[ndegrees] = osmf; @@ -204,33 +171,25 @@ public final class NodesCache } currentFileName = osmf.filename; - if ( !osmf.hasData() ) - { + if (!osmf.hasData()) { return null; } - MicroCache segment = osmf.getMicroCache( ilon, ilat ); - if ( segment == null ) - { + MicroCache segment = osmf.getMicroCache(ilon, ilat); + if (segment == null) { checkEnableCacheCleaning(); - segment = osmf.createMicroCache( ilon, ilat, dataBuffers, expCtxWay, waypointMatcher, directWeaving ? nodesMap : null ); + segment = osmf.createMicroCache(ilon, ilat, dataBuffers, expCtxWay, waypointMatcher, directWeaving ? nodesMap : null); cacheSum += segment.getDataSize(); - } - else if ( segment.ghost ) - { + } else if (segment.ghost) { segment.unGhost(); ghostWakeup += segment.getDataSize(); } return segment; - } - catch (RuntimeException re) - { + } catch (RuntimeException re) { throw re; - } - catch (Exception e) - { - throw new RuntimeException( "error reading datafile " + currentFileName + ": " + e, e ); + } catch (Exception e) { + throw new RuntimeException("error reading datafile " + currentFileName + ": " + e, e); } } @@ -238,60 +197,50 @@ public final class NodesCache * make sure the given node is non-hollow, * which means it contains not just the id, * but also the actual data - * + * * @return true if successfull, false if node is still hollow */ - public boolean obtainNonHollowNode( OsmNode node ) - { - if ( !node.isHollow() ) + public boolean obtainNonHollowNode(OsmNode node) { + if (!node.isHollow()) return true; - MicroCache segment = getSegmentFor( node.ilon, node.ilat ); - if ( segment == null ) - { + MicroCache segment = getSegmentFor(node.ilon, node.ilat); + if (segment == null) { return false; } - if ( !node.isHollow() ) - { + if (!node.isHollow()) { return true; // direct weaving... } long id = node.getIdFromPos(); - if ( segment.getAndClear( id ) ) - { - node.parseNodeBody( segment, nodesMap, expCtxWay ); + if (segment.getAndClear(id)) { + node.parseNodeBody(segment, nodesMap, expCtxWay); } - if ( garbageCollectionEnabled ) // garbage collection + if (garbageCollectionEnabled) // garbage collection { - cacheSum -= segment.collect( segment.getSize() >> 1 ); // threshold = 1/2 of size is deleted + cacheSum -= segment.collect(segment.getSize() >> 1); // threshold = 1/2 of size is deleted } return !node.isHollow(); } - /** * make sure all link targets of the given node are non-hollow */ - public void expandHollowLinkTargets( OsmNode n ) - { - for( OsmLink link = n.firstlink; link != null; link = link.getNext( n ) ) - { - obtainNonHollowNode( link.getTarget( n ) ); + public void expandHollowLinkTargets(OsmNode n) { + for (OsmLink link = n.firstlink; link != null; link = link.getNext(n)) { + obtainNonHollowNode(link.getTarget(n)); } } /** * make sure all link targets of the given node are non-hollow */ - public boolean hasHollowLinkTargets( OsmNode n ) - { - for( OsmLink link = n.firstlink; link != null; link = link.getNext( n ) ) - { - if ( link.getTarget( n ).isHollow() ) - { + public boolean hasHollowLinkTargets(OsmNode n) { + for (OsmLink link = n.firstlink; link != null; link = link.getNext(n)) { + if (link.getTarget(n).isHollow()) { return true; } } @@ -300,129 +249,107 @@ public final class NodesCache /** * get a node for the given id with all link-targets also non-hollow - * + *

* It is required that an instance of the start-node does not yet * exist, not even a hollow instance, so getStartNode should only * be called once right after resetting the cache - * + * * @param id the id of the node to load - * * @return the fully expanded node for id, or null if it was not found */ - public OsmNode getStartNode( long id ) - { + public OsmNode getStartNode(long id) { // initialize the start-node - OsmNode n = new OsmNode( id ); + OsmNode n = new OsmNode(id); n.setHollow(); - nodesMap.put( n ); - if ( !obtainNonHollowNode( n ) ) - { + nodesMap.put(n); + if (!obtainNonHollowNode(n)) { return null; } - expandHollowLinkTargets( n ); + expandHollowLinkTargets(n); return n; } - public OsmNode getGraphNode( OsmNode template ) - { - OsmNode graphNode = new OsmNode( template.ilon, template.ilat ); + public OsmNode getGraphNode(OsmNode template) { + OsmNode graphNode = new OsmNode(template.ilon, template.ilat); graphNode.setHollow(); - OsmNode existing = nodesMap.put( graphNode ); - if ( existing == null ) - { + OsmNode existing = nodesMap.put(graphNode); + if (existing == null) { return graphNode; } - nodesMap.put( existing ); + nodesMap.put(existing); return existing; } - public void matchWaypointsToNodes( List unmatchedWaypoints, double maxDistance, OsmNodePairSet islandNodePairs ) - { - waypointMatcher = new WaypointMatcherImpl( unmatchedWaypoints, maxDistance, islandNodePairs ); - for( MatchedWaypoint mwp : unmatchedWaypoints ) - { - preloadPosition( mwp.waypoint ); + public void matchWaypointsToNodes(List unmatchedWaypoints, double maxDistance, OsmNodePairSet islandNodePairs) { + waypointMatcher = new WaypointMatcherImpl(unmatchedWaypoints, maxDistance, islandNodePairs); + for (MatchedWaypoint mwp : unmatchedWaypoints) { + preloadPosition(mwp.waypoint); } - if ( first_file_access_failed ) - { - throw new IllegalArgumentException( "datafile " + first_file_access_name + " not found" ); + if (first_file_access_failed) { + throw new IllegalArgumentException("datafile " + first_file_access_name + " not found"); } - for( MatchedWaypoint mwp : unmatchedWaypoints ) - { - if ( mwp.crosspoint == null ) - { - throw new IllegalArgumentException( mwp.name + "-position not mapped in existing datafile" ); + for (MatchedWaypoint mwp : unmatchedWaypoints) { + if (mwp.crosspoint == null) { + throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile"); } } } - private void preloadPosition( OsmNode n ) - { + private void preloadPosition(OsmNode n) { int d = 12500; first_file_access_failed = false; first_file_access_name = null; - loadSegmentFor( n.ilon, n.ilat ); - if ( first_file_access_failed ) - { - throw new IllegalArgumentException( "datafile " + first_file_access_name + " not found" ); + loadSegmentFor(n.ilon, n.ilat); + if (first_file_access_failed) { + throw new IllegalArgumentException("datafile " + first_file_access_name + " not found"); } - for( int idxLat=-1; idxLat<=1; idxLat++ ) - for( int idxLon=-1; idxLon<=1; idxLon++ ) - { - if ( idxLon != 0 || idxLat != 0 ) - { - loadSegmentFor( n.ilon + d*idxLon , n.ilat +d*idxLat ); + for (int idxLat = -1; idxLat <= 1; idxLat++) + for (int idxLon = -1; idxLon <= 1; idxLon++) { + if (idxLon != 0 || idxLat != 0) { + loadSegmentFor(n.ilon + d * idxLon, n.ilat + d * idxLat); } } } - private OsmFile fileForSegment( int lonDegree, int latDegree ) throws Exception - { + private OsmFile fileForSegment(int lonDegree, int latDegree) throws Exception { int lonMod5 = lonDegree % 5; int latMod5 = latDegree % 5; int lon = lonDegree - 180 - lonMod5; - String slon = lon < 0 ? "W" + ( -lon ) : "E" + lon; + String slon = lon < 0 ? "W" + (-lon) : "E" + lon; int lat = latDegree - 90 - latMod5; - String slat = lat < 0 ? "S" + ( -lat ) : "N" + lat; + String slat = lat < 0 ? "S" + (-lat) : "N" + lat; String filenameBase = slon + "_" + slat; currentFileName = filenameBase + ".rd5"; PhysicalFile ra = null; - if ( !fileCache.containsKey( filenameBase ) ) - { + if (!fileCache.containsKey(filenameBase)) { File f = null; - if ( !forceSecondaryData ) - { - File primary = new File( segmentDir, filenameBase + ".rd5" ); - if ( primary .exists() ) - { + if (!forceSecondaryData) { + File primary = new File(segmentDir, filenameBase + ".rd5"); + if (primary.exists()) { f = primary; } } - if ( f == null ) - { - File secondary = new File( secondarySegmentsDir, filenameBase + ".rd5" ); - if ( secondary.exists() ) - { + if (f == null) { + File secondary = new File(secondarySegmentsDir, filenameBase + ".rd5"); + if (secondary.exists()) { f = secondary; } } - if ( f != null ) - { + if (f != null) { currentFileName = f.getName(); - ra = new PhysicalFile( f, dataBuffers, lookupVersion, lookupMinorVersion ); + ra = new PhysicalFile(f, dataBuffers, lookupVersion, lookupMinorVersion); } - fileCache.put( filenameBase, ra ); + fileCache.put(filenameBase, ra); } - ra = fileCache.get( filenameBase ); - OsmFile osmf = new OsmFile( ra, lonDegree, latDegree, dataBuffers ); + ra = fileCache.get(filenameBase); + OsmFile osmf = new OsmFile(ra, lonDegree, latDegree, dataBuffers); - if ( first_file_access_name == null ) - { + if (first_file_access_name == null) { first_file_access_name = currentFileName; first_file_access_failed = osmf.filename == null; } @@ -430,17 +357,12 @@ public final class NodesCache return osmf; } - public void close() - { - for ( PhysicalFile f : fileCache.values() ) - { - try - { - if ( f != null ) + public void close() { + for (PhysicalFile f : fileCache.values()) { + try { + if (f != null) f.ra.close(); - } - catch (IOException ioe) - { + } catch (IOException ioe) { // ignore } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesList.java b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesList.java index 9c9b96d..8135ffd 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesList.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesList.java @@ -1,13 +1,12 @@ -/** - * Container for link between two Osm nodes - * - * @author ab - */ -package btools.mapaccess; - - -final class NodesList -{ - public OsmNode node; - public NodesList next; -} +/** + * Container for link between two Osm nodes + * + * @author ab + */ +package btools.mapaccess; + + +final class NodesList { + public OsmNode node; + public NodesList next; +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java index f2c481b..0f773cf 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java @@ -17,8 +17,7 @@ import btools.codec.WaypointMatcher; import btools.util.ByteDataReader; import btools.util.Crc32; -final class OsmFile -{ +final class OsmFile { private RandomAccessFile is = null; private long fileOffset; @@ -35,16 +34,14 @@ final class OsmFile private int ncaches; private int indexsize; - public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws IOException - { + public OsmFile(PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers) throws IOException { this.lonDegree = lonDegree; this.latDegree = latDegree; int lonMod5 = lonDegree % 5; int latMod5 = latDegree % 5; int tileIndex = lonMod5 * 5 + latMod5; - if ( rafile != null ) - { + if (rafile != null) { divisor = rafile.divisor; cellsize = 1000000 / divisor; @@ -56,197 +53,161 @@ final class OsmFile long[] index = rafile.fileIndex; fileOffset = tileIndex > 0 ? index[tileIndex - 1] : 200L; - if ( fileOffset == index[tileIndex] ) + if (fileOffset == index[tileIndex]) return; // empty is = rafile.ra; posIdx = new int[ncaches]; microCaches = new MicroCache[ncaches]; - is.seek( fileOffset ); - is.readFully( iobuffer, 0, indexsize ); + is.seek(fileOffset); + is.readFully(iobuffer, 0, indexsize); - if ( rafile.fileHeaderCrcs != null ) - { - int headerCrc = Crc32.crc( iobuffer, 0, indexsize ); - if ( rafile.fileHeaderCrcs[tileIndex] != headerCrc ) - { - throw new IOException( "sub index checksum error" ); + if (rafile.fileHeaderCrcs != null) { + int headerCrc = Crc32.crc(iobuffer, 0, indexsize); + if (rafile.fileHeaderCrcs[tileIndex] != headerCrc) { + throw new IOException("sub index checksum error"); } } - ByteDataReader dis = new ByteDataReader( iobuffer ); - for ( int i = 0; i < ncaches; i++ ) - { + ByteDataReader dis = new ByteDataReader(iobuffer); + for (int i = 0; i < ncaches; i++) { posIdx[i] = dis.readInt(); } } } - public boolean hasData() - { + public boolean hasData() { return microCaches != null; } - public MicroCache getMicroCache( int ilon, int ilat ) - { + public MicroCache getMicroCache(int ilon, int ilat) { int lonIdx = ilon / cellsize; int latIdx = ilat / cellsize; - int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree ); + int subIdx = (latIdx - divisor * latDegree) * divisor + (lonIdx - divisor * lonDegree); return microCaches[subIdx]; } - public MicroCache createMicroCache( int ilon, int ilat, DataBuffers dataBuffers, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes ) - throws Exception - { + public MicroCache createMicroCache(int ilon, int ilat, DataBuffers dataBuffers, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes) + throws Exception { int lonIdx = ilon / cellsize; int latIdx = ilat / cellsize; - MicroCache segment = createMicroCache( lonIdx, latIdx, dataBuffers, wayValidator, waypointMatcher, true, hollowNodes ); - int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree ); + MicroCache segment = createMicroCache(lonIdx, latIdx, dataBuffers, wayValidator, waypointMatcher, true, hollowNodes); + int subIdx = (latIdx - divisor * latDegree) * divisor + (lonIdx - divisor * lonDegree); microCaches[subIdx] = segment; return segment; } - private int getPosIdx( int idx ) - { + private int getPosIdx(int idx) { return idx == -1 ? indexsize : posIdx[idx]; } - public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws IOException - { - int startPos = getPosIdx( subIdx - 1 ); - int endPos = getPosIdx( subIdx ); + public int getDataInputForSubIdx(int subIdx, byte[] iobuffer) throws IOException { + int startPos = getPosIdx(subIdx - 1); + int endPos = getPosIdx(subIdx); int size = endPos - startPos; - if ( size > 0 ) - { - is.seek( fileOffset + startPos ); - if ( size <= iobuffer.length ) - { - is.readFully( iobuffer, 0, size ); + if (size > 0) { + is.seek(fileOffset + startPos); + if (size <= iobuffer.length) { + is.readFully(iobuffer, 0, size); } } return size; } - public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator, - WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes ) throws IOException - { - int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree ); + public MicroCache createMicroCache(int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator, + WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes) throws IOException { + int subIdx = (latIdx - divisor * latDegree) * divisor + (lonIdx - divisor * lonDegree); byte[] ab = dataBuffers.iobuffer; - int asize = getDataInputForSubIdx( subIdx, ab ); + int asize = getDataInputForSubIdx(subIdx, ab); - if ( asize == 0 ) - { + if (asize == 0) { return MicroCache.emptyCache(); } - if ( asize > ab.length ) - { + if (asize > ab.length) { ab = new byte[asize]; - asize = getDataInputForSubIdx( subIdx, ab ); + asize = getDataInputForSubIdx(subIdx, ab); } - StatCoderContext bc = new StatCoderContext( ab ); + StatCoderContext bc = new StatCoderContext(ab); - try - { - if ( !reallyDecode ) - { + try { + if (!reallyDecode) { return null; } - if ( hollowNodes == null ) - { - return new MicroCache2( bc, dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher ); + if (hollowNodes == null) { + return new MicroCache2(bc, dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher); } - new DirectWeaver( bc, dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher, hollowNodes ); + new DirectWeaver(bc, dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher, hollowNodes); return MicroCache.emptyNonVirgin; - } - finally - { + } finally { // crc check only if the buffer has not been fully read - int readBytes = (bc.getReadingBitPosition()+7)>>3; - if ( readBytes != asize-4 ) - { - int crcData = Crc32.crc( ab, 0, asize - 4 ); - int crcFooter = new ByteDataReader( ab, asize - 4 ).readInt(); - if ( crcData == crcFooter ) - { - throw new IOException( "old, unsupported data-format" ); - } - else if ( ( crcData ^ 2 ) != crcFooter ) - { - throw new IOException( "checkum error" ); + int readBytes = (bc.getReadingBitPosition() + 7) >> 3; + if (readBytes != asize - 4) { + int crcData = Crc32.crc(ab, 0, asize - 4); + int crcFooter = new ByteDataReader(ab, asize - 4).readInt(); + if (crcData == crcFooter) { + throw new IOException("old, unsupported data-format"); + } else if ((crcData ^ 2) != crcFooter) { + throw new IOException("checkum error"); } } } } // set this OsmFile to ghost-state: - long setGhostState() - { + long setGhostState() { long sum = 0; int nc = microCaches == null ? 0 : microCaches.length; - for ( int i = 0; i < nc; i++ ) - { + for (int i = 0; i < nc; i++) { MicroCache mc = microCaches[i]; - if ( mc == null ) + if (mc == null) continue; - if ( mc.virgin ) - { + if (mc.virgin) { mc.ghost = true; sum += mc.getDataSize(); - } - else - { + } else { microCaches[i] = null; } } return sum; } - long collectAll() - { + long collectAll() { long deleted = 0; int nc = microCaches == null ? 0 : microCaches.length; - for ( int i = 0; i < nc; i++ ) - { + for (int i = 0; i < nc; i++) { MicroCache mc = microCaches[i]; - if ( mc == null ) + if (mc == null) continue; - if ( !mc.ghost ) - { - deleted += mc.collect( 0 ); + if (!mc.ghost) { + deleted += mc.collect(0); } } return deleted; } - long cleanGhosts() - { + long cleanGhosts() { long deleted = 0; int nc = microCaches == null ? 0 : microCaches.length; - for ( int i = 0; i < nc; i++ ) - { + for (int i = 0; i < nc; i++) { MicroCache mc = microCaches[i]; - if ( mc == null ) + if (mc == null) continue; - if ( mc.ghost ) - { + if (mc.ghost) { microCaches[i] = null; } } return deleted; } - void clean( boolean all ) - { + void clean(boolean all) { int nc = microCaches == null ? 0 : microCaches.length; - for ( int i = 0; i < nc; i++ ) - { + for (int i = 0; i < nc; i++) { MicroCache mc = microCaches[i]; - if ( mc == null ) + if (mc == null) continue; - if ( all || !mc.virgin ) - { + if (all || !mc.virgin) { microCaches[i] = null; } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java index 4d548fc..963765e 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java @@ -1,182 +1,155 @@ -/** - * Container for link between two Osm nodes - * - * @author ab - */ -package btools.mapaccess; - - - -public class OsmLink -{ - /** - * The description bitmap contains the waytags (valid for both directions) - */ - public byte[] descriptionBitmap; - - /** - * The geometry contains intermediate nodes, null for none (valid for both directions) - */ - public byte[] geometry; - - // a link logically knows only its target, but for the reverse link, source and target are swapped - protected OsmNode n1; - protected OsmNode n2; - - // same for the next-link-for-node pointer: previous applies to the reverse link - protected OsmLink previous; - protected OsmLink next; - - private OsmLinkHolder reverselinkholder = null; - private OsmLinkHolder firstlinkholder = null; - - protected OsmLink() - { - } - - public OsmLink( OsmNode source, OsmNode target ) - { - n1 = source; - n2 = target; - } - - /** - * Get the relevant target-node for the given source - */ - public final OsmNode getTarget( OsmNode source ) - { - return n2 != source && n2 != null ? n2 : n1; - /* if ( n2 != null && n2 != source ) - { - return n2; - } - else if ( n1 != null && n1 != source ) - { - return n1; - } - else - { - new Throwable( "ups" ).printStackTrace(); - throw new IllegalArgumentException( "internal error: getTarget: unknown source; " + source + " n1=" + n1 + " n2=" + n2 ); - } */ - } - - /** - * Get the relevant next-pointer for the given source - */ - public final OsmLink getNext( OsmNode source ) - { - return n2 != source && n2 != null ? next : previous; - /* if ( n2 != null && n2 != source ) - { - return next; - } - else if ( n1 != null && n1 != source ) - { - return previous; - } - else - { - throw new IllegalArgumentException( "internal error: gextNext: unknown source" ); - } */ - } - - /** - * Reset this link for the given direction - */ - protected final OsmLink clear( OsmNode source ) - { - OsmLink n; - if ( n2 != null && n2 != source ) - { - n = next; - next = null; - n2 = null; - firstlinkholder = null; - } - else if ( n1 != null && n1 != source ) - { - n = previous; - previous = null; - n1 = null; - reverselinkholder = null; - } - else - { - throw new IllegalArgumentException( "internal error: setNext: unknown source" ); - } - if ( n1 == null && n2 == null ) - { - descriptionBitmap = null; - geometry = null; - } - return n; - } - - public final void setFirstLinkHolder( OsmLinkHolder holder, OsmNode source ) - { - if ( n2 != null && n2 != source ) - { - firstlinkholder = holder; - } - else if ( n1 != null && n1 != source ) - { - reverselinkholder = holder; - } - else - { - throw new IllegalArgumentException( "internal error: setFirstLinkHolder: unknown source" ); - } - } - - public final OsmLinkHolder getFirstLinkHolder( OsmNode source ) - { - if ( n2 != null && n2 != source ) - { - return firstlinkholder; - } - else if ( n1 != null && n1 != source ) - { - return reverselinkholder; - } - else - { - throw new IllegalArgumentException( "internal error: getFirstLinkHolder: unknown source" ); - } - } - - public final boolean isReverse( OsmNode source ) - { - return n1 != source && n1 != null; - /* if ( n2 != null && n2 != source ) - { - return false; - } - else if ( n1 != null && n1 != source ) - { - return true; - } - else - { - throw new IllegalArgumentException( "internal error: isReverse: unknown source" ); - } */ - } - - public final boolean isBidirectional() - { - return n1 != null && n2 != null; - } - - public final boolean isLinkUnused() - { - return n1 == null && n2 == null; - } - - public final void addLinkHolder( OsmLinkHolder holder, OsmNode source ) - { - OsmLinkHolder firstHolder = getFirstLinkHolder( source ); - if ( firstHolder != null ) { holder.setNextForLink( firstHolder ); } - setFirstLinkHolder( holder, source ); - } - -} +/** + * Container for link between two Osm nodes + * + * @author ab + */ +package btools.mapaccess; + + +public class OsmLink { + /** + * The description bitmap contains the waytags (valid for both directions) + */ + public byte[] descriptionBitmap; + + /** + * The geometry contains intermediate nodes, null for none (valid for both directions) + */ + public byte[] geometry; + + // a link logically knows only its target, but for the reverse link, source and target are swapped + protected OsmNode n1; + protected OsmNode n2; + + // same for the next-link-for-node pointer: previous applies to the reverse link + protected OsmLink previous; + protected OsmLink next; + + private OsmLinkHolder reverselinkholder = null; + private OsmLinkHolder firstlinkholder = null; + + protected OsmLink() { + } + + public OsmLink(OsmNode source, OsmNode target) { + n1 = source; + n2 = target; + } + + /** + * Get the relevant target-node for the given source + */ + public final OsmNode getTarget(OsmNode source) { + return n2 != source && n2 != null ? n2 : n1; + /* if ( n2 != null && n2 != source ) + { + return n2; + } + else if ( n1 != null && n1 != source ) + { + return n1; + } + else + { + new Throwable( "ups" ).printStackTrace(); + throw new IllegalArgumentException( "internal error: getTarget: unknown source; " + source + " n1=" + n1 + " n2=" + n2 ); + } */ + } + + /** + * Get the relevant next-pointer for the given source + */ + public final OsmLink getNext(OsmNode source) { + return n2 != source && n2 != null ? next : previous; + /* if ( n2 != null && n2 != source ) + { + return next; + } + else if ( n1 != null && n1 != source ) + { + return previous; + } + else + { + throw new IllegalArgumentException( "internal error: gextNext: unknown source" ); + } */ + } + + /** + * Reset this link for the given direction + */ + protected final OsmLink clear(OsmNode source) { + OsmLink n; + if (n2 != null && n2 != source) { + n = next; + next = null; + n2 = null; + firstlinkholder = null; + } else if (n1 != null && n1 != source) { + n = previous; + previous = null; + n1 = null; + reverselinkholder = null; + } else { + throw new IllegalArgumentException("internal error: setNext: unknown source"); + } + if (n1 == null && n2 == null) { + descriptionBitmap = null; + geometry = null; + } + return n; + } + + public final void setFirstLinkHolder(OsmLinkHolder holder, OsmNode source) { + if (n2 != null && n2 != source) { + firstlinkholder = holder; + } else if (n1 != null && n1 != source) { + reverselinkholder = holder; + } else { + throw new IllegalArgumentException("internal error: setFirstLinkHolder: unknown source"); + } + } + + public final OsmLinkHolder getFirstLinkHolder(OsmNode source) { + if (n2 != null && n2 != source) { + return firstlinkholder; + } else if (n1 != null && n1 != source) { + return reverselinkholder; + } else { + throw new IllegalArgumentException("internal error: getFirstLinkHolder: unknown source"); + } + } + + public final boolean isReverse(OsmNode source) { + return n1 != source && n1 != null; + /* if ( n2 != null && n2 != source ) + { + return false; + } + else if ( n1 != null && n1 != source ) + { + return true; + } + else + { + throw new IllegalArgumentException( "internal error: isReverse: unknown source" ); + } */ + } + + public final boolean isBidirectional() { + return n1 != null && n2 != null; + } + + public final boolean isLinkUnused() { + return n1 == null && n2 == null; + } + + public final void addLinkHolder(OsmLinkHolder holder, OsmNode source) { + OsmLinkHolder firstHolder = getFirstLinkHolder(source); + if (firstHolder != null) { + holder.setNextForLink(firstHolder); + } + setFirstLinkHolder(holder, source); + } + +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLinkHolder.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLinkHolder.java index da572d3..f84d50c 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLinkHolder.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLinkHolder.java @@ -1,13 +1,12 @@ -/** - * Container for routig configs - * - * @author ab - */ -package btools.mapaccess; - -public interface OsmLinkHolder -{ - void setNextForLink( OsmLinkHolder holder ); - - OsmLinkHolder getNextForLink(); -} +/** + * Container for routig configs + * + * @author ab + */ +package btools.mapaccess; + +public interface OsmLinkHolder { + void setNextForLink(OsmLinkHolder holder); + + OsmLinkHolder getNextForLink(); +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java index 0bcc2b3..446b873 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java @@ -11,8 +11,7 @@ import btools.util.ByteArrayUnifier; import btools.util.CheapRuler; import btools.util.IByteArrayUnifier; -public class OsmNode extends OsmLink implements OsmPos -{ +public class OsmNode extends OsmLink implements OsmPos { /** * The latitude */ @@ -34,11 +33,10 @@ public class OsmNode extends OsmLink implements OsmPos public byte[] nodeDescription; public TurnRestriction firstRestriction; - + public int visitID; - public void addTurnRestriction( TurnRestriction tr ) - { + public void addTurnRestriction(TurnRestriction tr) { tr.next = firstRestriction; firstRestriction = tr; } @@ -48,62 +46,50 @@ public class OsmNode extends OsmLink implements OsmPos */ public OsmLink firstlink; - public OsmNode() - { + public OsmNode() { } - public OsmNode( int ilon, int ilat ) - { + public OsmNode(int ilon, int ilat) { this.ilon = ilon; this.ilat = ilat; } - public OsmNode( long id ) - { - ilon = (int) ( id >> 32 ); - ilat = (int) ( id & 0xffffffff ); + public OsmNode(long id) { + ilon = (int) (id >> 32); + ilat = (int) (id & 0xffffffff); } // interface OsmPos - public final int getILat() - { + public final int getILat() { return ilat; } - public final int getILon() - { + public final int getILon() { return ilon; } - public final short getSElev() - { + public final short getSElev() { return selev; } - public final double getElev() - { + public final double getElev() { return selev / 4.; } - public final void addLink( OsmLink link, boolean isReverse, OsmNode tn ) - { - if ( link == firstlink ) - { - throw new IllegalArgumentException( "UUUUPS" ); + public final void addLink(OsmLink link, boolean isReverse, OsmNode tn) { + if (link == firstlink) { + throw new IllegalArgumentException("UUUUPS"); } - - if ( isReverse ) - { + + if (isReverse) { link.n1 = tn; link.n2 = this; link.next = tn.firstlink; link.previous = firstlink; tn.firstlink = link; firstlink = link; - } - else - { + } else { link.n1 = this; link.n2 = tn; link.next = firstlink; @@ -113,202 +99,167 @@ public class OsmNode extends OsmLink implements OsmPos } } - public final int calcDistance( OsmPos p ) - { - return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 ); + public final int calcDistance(OsmPos p) { + return (int) (CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0); } - public String toString() - { - return "n_" + (ilon-180000000) + "_" + (ilat-90000000); + public String toString() { + return "n_" + (ilon - 180000000) + "_" + (ilat - 90000000); } - public final void parseNodeBody( MicroCache mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay ) - { - if ( mc instanceof MicroCache2 ) - { - parseNodeBody2( (MicroCache2) mc, hollowNodes, expCtxWay ); - } - else - throw new IllegalArgumentException( "unknown cache version: " + mc.getClass() ); + public final void parseNodeBody(MicroCache mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay) { + if (mc instanceof MicroCache2) { + parseNodeBody2((MicroCache2) mc, hollowNodes, expCtxWay); + } else + throw new IllegalArgumentException("unknown cache version: " + mc.getClass()); } - public final void parseNodeBody2( MicroCache2 mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay ) - { + public final void parseNodeBody2(MicroCache2 mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay) { ByteArrayUnifier abUnifier = hollowNodes.getByteArrayUnifier(); // read turn restrictions - while( mc.readBoolean() ) - { + while (mc.readBoolean()) { TurnRestriction tr = new TurnRestriction(); - tr.exceptions = mc.readShort(); - tr.isPositive = mc.readBoolean(); + tr.exceptions = mc.readShort(); + tr.isPositive = mc.readBoolean(); tr.fromLon = mc.readInt(); tr.fromLat = mc.readInt(); tr.toLon = mc.readInt(); tr.toLat = mc.readInt(); - addTurnRestriction( tr ); + addTurnRestriction(tr); } selev = mc.readShort(); int nodeDescSize = mc.readVarLengthUnsigned(); - nodeDescription = nodeDescSize == 0 ? null : mc.readUnified( nodeDescSize, abUnifier ); + nodeDescription = nodeDescSize == 0 ? null : mc.readUnified(nodeDescSize, abUnifier); - while (mc.hasMoreData()) - { + while (mc.hasMoreData()) { // read link data int endPointer = mc.getEndPointer(); int linklon = ilon + mc.readVarLengthSigned(); int linklat = ilat + mc.readVarLengthSigned(); int sizecode = mc.readVarLengthUnsigned(); - boolean isReverse = ( sizecode & 1 ) != 0; + boolean isReverse = (sizecode & 1) != 0; byte[] description = null; int descSize = sizecode >> 1; - if ( descSize > 0 ) - { - description = mc.readUnified( descSize, expCtxWay ); + if (descSize > 0) { + description = mc.readUnified(descSize, expCtxWay); } - byte[] geometry = mc.readDataUntil( endPointer ); + byte[] geometry = mc.readDataUntil(endPointer); - addLink( linklon, linklat, description, geometry, hollowNodes, isReverse ); + addLink(linklon, linklat, description, geometry, hollowNodes, isReverse); } - hollowNodes.remove( this ); + hollowNodes.remove(this); } - public void addLink( int linklon, int linklat, byte[] description, byte[] geometry, OsmNodesMap hollowNodes, boolean isReverse ) - { - if ( linklon == ilon && linklat == ilat ) - { - return; // skip self-ref - } + public void addLink(int linklon, int linklat, byte[] description, byte[] geometry, OsmNodesMap hollowNodes, boolean isReverse) { + if (linklon == ilon && linklat == ilat) { + return; // skip self-ref + } - OsmNode tn = null; // find the target node - OsmLink link = null; + OsmNode tn = null; // find the target node + OsmLink link = null; - // ...in our known links - for ( OsmLink l = firstlink; l != null; l = l.getNext( this ) ) - { - OsmNode t = l.getTarget( this ); - if ( t.ilon == linklon && t.ilat == linklat ) - { - tn = t; - if ( isReverse || ( l.descriptionBitmap == null && !l.isReverse( this ) ) ) - { - link = l; // the correct one that needs our data - break; - } + // ...in our known links + for (OsmLink l = firstlink; l != null; l = l.getNext(this)) { + OsmNode t = l.getTarget(this); + if (t.ilon == linklon && t.ilat == linklat) { + tn = t; + if (isReverse || (l.descriptionBitmap == null && !l.isReverse(this))) { + link = l; // the correct one that needs our data + break; } } - if ( tn == null ) // .. not found, then check the hollow nodes + } + if (tn == null) // .. not found, then check the hollow nodes + { + tn = hollowNodes.get(linklon, linklat); // target node + if (tn == null) // node not yet known, create a new hollow proxy { - tn = hollowNodes.get( linklon, linklat ); // target node - if ( tn == null ) // node not yet known, create a new hollow proxy - { - tn = new OsmNode( linklon, linklat ); - tn.setHollow(); - hollowNodes.put( tn ); - addLink( link = tn, isReverse, tn ); // technical inheritance: link instance in node - } - } - if ( link == null ) - { - addLink( link = new OsmLink(), isReverse, tn ); - } - if ( !isReverse ) - { - link.descriptionBitmap = description; - link.geometry = geometry; + tn = new OsmNode(linklon, linklat); + tn.setHollow(); + hollowNodes.put(tn); + addLink(link = tn, isReverse, tn); // technical inheritance: link instance in node } + } + if (link == null) { + addLink(link = new OsmLink(), isReverse, tn); + } + if (!isReverse) { + link.descriptionBitmap = description; + link.geometry = geometry; + } } - public final boolean isHollow() - { + public final boolean isHollow() { return selev == -12345; } - public final void setHollow() - { + public final void setHollow() { selev = -12345; } - public final long getIdFromPos() - { - return ( (long) ilon ) << 32 | ilat; + public final long getIdFromPos() { + return ((long) ilon) << 32 | ilat; } - - public void vanish() - { - if ( !isHollow() ) - { + + public void vanish() { + if (!isHollow()) { OsmLink l = firstlink; - while( l != null ) - { - OsmNode target = l.getTarget( this ); - OsmLink nextLink = l.getNext( this ); - if ( !target.isHollow() ) - { - unlinkLink( l ); - if ( !l.isLinkUnused() ) - { - target.unlinkLink( l ); - } + while (l != null) { + OsmNode target = l.getTarget(this); + OsmLink nextLink = l.getNext(this); + if (!target.isHollow()) { + unlinkLink(l); + if (!l.isLinkUnused()) { + target.unlinkLink(l); + } } l = nextLink; } } } - public final void unlinkLink( OsmLink link ) - { - OsmLink n = link.clear( this ); + public final void unlinkLink(OsmLink link) { + OsmLink n = link.clear(this); - if ( link == firstlink ) - { + if (link == firstlink) { firstlink = n; return; } OsmLink l = firstlink; - while( l != null ) - { + while (l != null) { // if ( l.isReverse( this ) ) - if ( l.n1 != this && l.n1 != null ) // isReverse inline + if (l.n1 != this && l.n1 != null) // isReverse inline { OsmLink nl = l.previous; - if ( nl == link ) - { + if (nl == link) { l.previous = n; return; } l = nl; - } - else if ( l.n2 != this && l.n2 != null ) - { + } else if (l.n2 != this && l.n2 != null) { OsmLink nl = l.next; - if ( nl == link ) - { + if (nl == link) { l.next = n; return; } l = nl; - } - else - { - throw new IllegalArgumentException( "unlinkLink: unknown source" ); + } else { + throw new IllegalArgumentException("unlinkLink: unknown source"); } } } @Override - public final boolean equals( Object o ) - { - return ((OsmNode)o).ilon == ilon && ((OsmNode)o).ilat == ilat; + public final boolean equals(Object o) { + return ((OsmNode) o).ilon == ilon && ((OsmNode) o).ilat == ilat; } @Override - public final int hashCode() - { + public final int hashCode() { return ilon + ilat; } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java index b790e12..6c1e38e 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java @@ -7,8 +7,7 @@ package btools.mapaccess; import btools.util.CompactLongMap; -public class OsmNodePairSet -{ +public class OsmNodePairSet { private long[] n1a; private long[] n2a; private int tempNodes = 0; @@ -16,108 +15,86 @@ public class OsmNodePairSet private int npairs = 0; private int freezecount = 0; - public OsmNodePairSet( int maxTempNodeCount ) - { + public OsmNodePairSet(int maxTempNodeCount) { maxTempNodes = maxTempNodeCount; n1a = new long[maxTempNodes]; n2a = new long[maxTempNodes]; } - private static class OsmNodePair - { + private static class OsmNodePair { public long node2; public OsmNodePair next; } - + private CompactLongMap map; - public void addTempPair( long n1, long n2 ) - { - if ( tempNodes < maxTempNodes ) - { + public void addTempPair(long n1, long n2) { + if (tempNodes < maxTempNodes) { n1a[tempNodes] = n1; n2a[tempNodes] = n2; tempNodes++; } } - - public void freezeTempPairs() - { + + public void freezeTempPairs() { freezecount++; - for( int i=0; i(); } npairs++; - - OsmNodePair e = getElement( n1, n2 ); - if ( e == null ) - { + + OsmNodePair e = getElement(n1, n2); + if (e == null) { e = new OsmNodePair(); e.node2 = n2; - OsmNodePair e0 = map.get( n1 ); - if ( e0 != null ) - { - while( e0.next != null ) - { + OsmNodePair e0 = map.get(n1); + if (e0 != null) { + while (e0.next != null) { e0 = e0.next; } e0.next = e; - } - else - { - map.fastPut( n1, e ); + } else { + map.fastPut(n1, e); } } } - - public int size() - { + + public int size() { return npairs; } - public int tempSize() - { + public int tempSize() { return tempNodes; } - public int getMaxTmpNodes() - { + public int getMaxTmpNodes() { return maxTempNodes; } - public int getFreezeCount() - { + public int getFreezeCount() { return freezecount; } - - public boolean hasPair( long n1, long n2 ) - { - return map != null && ( getElement( n1, n2 ) != null || getElement( n2, n1 ) != null ); + + public boolean hasPair(long n1, long n2) { + return map != null && (getElement(n1, n2) != null || getElement(n2, n1) != null); } - private OsmNodePair getElement( long n1, long n2 ) - { - OsmNodePair e = map.get( n1 ); - while (e != null) - { - if ( e.node2 == n2 ) - { + private OsmNodePair getElement(long n1, long n2) { + OsmNodePair e = map.get(n1); + while (e != null) { + if (e.node2 == n2) { return e; } e = e.next; diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodesMap.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodesMap.java index 4ffb103..b71c54d 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodesMap.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodesMap.java @@ -1,348 +1,293 @@ -/** - * Container for link between two Osm nodes - * - * @author ab - */ -package btools.mapaccess; - -import java.util.ArrayList; -import java.util.HashMap; - -import btools.util.ByteArrayUnifier; -import btools.util.SortedHeap; - -public final class OsmNodesMap -{ - private HashMap hmap = new HashMap(4096); - - private ByteArrayUnifier abUnifier = new ByteArrayUnifier( 16384, false ); - - private OsmNode testKey = new OsmNode(); - - public int nodesCreated; - public long maxmem; - private long currentmaxmem = 4000000; // start with 4 MB - public int lastVisitID = 1000; - public int baseID = 1000; - - public OsmNode destination; - public int currentPathCost; - public int currentMaxCost = 1000000000; - - public OsmNode endNode1; - public OsmNode endNode2; - - public int cleanupMode = 0; - - public void cleanupAndCount( OsmNode[] nodes ) - { - if ( cleanupMode == 0 ) - { - justCount( nodes ); - } - else - { - cleanupPeninsulas( nodes ); - } - } - - private void justCount( OsmNode[] nodes ) - { - for( int i=0; i n.visitID ) // peninsula ? - { - nodesCreated = nodesCreatedUntilHere; - n.unlinkLink( l ); - t.unlinkLink( l ); - } - } - else if ( minIdSub < baseID ) - { - continue; - } - else if ( cleanupMode == 2 ) - { - minIdSub = baseID; // in tree-mode, hitting anything is like a gateway - } - if ( minIdSub < minId ) minId = minIdSub; - } - return minId; - } - - - - public boolean isInMemoryBounds( int npaths, boolean extend ) - { -// long total = nodesCreated * 76L + linksCreated * 48L; - long total = nodesCreated * 95L + npaths * 200L; - - if ( extend ) - { - total += 100000; - - // when extending, try to have 1 MB space - long delta = total + 1900000 - currentmaxmem; - if ( delta > 0 ) - { - currentmaxmem += delta; - if ( currentmaxmem > maxmem ) - { - currentmaxmem = maxmem; - } - } - } - return total <= currentmaxmem; - } - - private void addActiveNode( ArrayList nodes2check, OsmNode n ) - { - n.visitID = lastVisitID; - nodesCreated++; - nodes2check.add( n ); - } - - // is there an escape from this node - // to a hollow node (or destination node) ? - public boolean canEscape( OsmNode n0 ) - { - boolean sawLowIDs = false; - lastVisitID++; - nodes2check.clear(); - nodes2check.add( n0 ); - while ( !nodes2check.isEmpty() ) - { - OsmNode n = nodes2check.remove( nodes2check.size()-1 ); - if ( n.visitID < baseID ) - { - n.visitID = lastVisitID; - nodesCreated++; - for( OsmLink l = n.firstlink; l != null; l = l.getNext( n ) ) - { - OsmNode t = l.getTarget( n ); - nodes2check.add( t ); - } - } - else if ( n.visitID < lastVisitID ) - { - sawLowIDs = true; - } - } - if ( sawLowIDs ) - { - return true; - } - - nodes2check.add( n0 ); - while ( !nodes2check.isEmpty() ) - { - OsmNode n = nodes2check.remove( nodes2check.size()-1 ); - if ( n.visitID == lastVisitID ) - { - n.visitID = lastVisitID; - nodesCreated--; - for( OsmLink l = n.firstlink; l != null; l = l.getNext( n ) ) - { - OsmNode t = l.getTarget( n ); - nodes2check.add( t ); - } - n.vanish(); - } - } - - return false; - } - - private ArrayList nodes2check; - - public void clearTemp() - { - nodes2check = null; - } - - public void collectOutreachers() - { - nodes2check = new ArrayList(nodesCreated); - nodesCreated=0; - for( OsmNode n : hmap.values() ) - { - addActiveNode( nodes2check, n ); - } - - lastVisitID++; - baseID = lastVisitID; - - while ( !nodes2check.isEmpty() ) - { - OsmNode n = nodes2check.remove( nodes2check.size()-1 ); - n.visitID = lastVisitID; - - for( OsmLink l = n.firstlink; l != null; l = l.getNext( n ) ) - { - OsmNode t = l.getTarget( n ); - if ( t.visitID != lastVisitID ) - { - addActiveNode( nodes2check, t ); - } - } - if ( destination != null && currentMaxCost < 1000000000 ) - { - int distance = n.calcDistance( destination ); - if ( distance > currentMaxCost - currentPathCost + 100 ) - { - n.vanish(); - } - } - if ( n.firstlink == null ) - { - nodesCreated--; - } - } - } - - - public ByteArrayUnifier getByteArrayUnifier() - { - return abUnifier; - } - - /** - * Get a node from the map - * @return the node for the given id if exist, else null - */ - public OsmNode get( int ilon, int ilat ) - { - testKey.ilon = ilon; - testKey.ilat = ilat; - return hmap.get( testKey ); - } - - - public void remove( OsmNode node ) - { - if ( node != endNode1 && node != endNode2 ) // keep endnodes in hollow-map even when loaded - { // (needed for escape analysis) - hmap.remove( node ); - } - } - - /** - * Put a node into the map - * @return the previous node if that id existed, else null - */ - public OsmNode put( OsmNode node ) - { - return hmap.put( node, node ); - } - - // ********************** test cleanup ********************** - - private static void addLinks( OsmNode[] nodes, int idx, boolean isBorder, int[] links ) - { - OsmNode n = nodes[idx]; - n.visitID = isBorder ? 1 : 0; - n.selev = (short)idx; - for( int i : links ) - { - OsmNode t = nodes[i]; - OsmLink link = n.isLinkUnused() ? n : ( t.isLinkUnused() ? t : null ); - if ( link == null ) - { - link = new OsmLink(); - } - n.addLink( link, false, t ); - } - } - - public static void main( String[] args ) - { - OsmNode[] nodes = new OsmNode[12]; - for( int i=0; i n.visitID) // peninsula ? + { + nodesCreated = nodesCreatedUntilHere; + n.unlinkLink(l); + t.unlinkLink(l); + } + } else if (minIdSub < baseID) { + continue; + } else if (cleanupMode == 2) { + minIdSub = baseID; // in tree-mode, hitting anything is like a gateway + } + if (minIdSub < minId) minId = minIdSub; + } + return minId; + } + + + public boolean isInMemoryBounds(int npaths, boolean extend) { +// long total = nodesCreated * 76L + linksCreated * 48L; + long total = nodesCreated * 95L + npaths * 200L; + + if (extend) { + total += 100000; + + // when extending, try to have 1 MB space + long delta = total + 1900000 - currentmaxmem; + if (delta > 0) { + currentmaxmem += delta; + if (currentmaxmem > maxmem) { + currentmaxmem = maxmem; + } + } + } + return total <= currentmaxmem; + } + + private void addActiveNode(ArrayList nodes2check, OsmNode n) { + n.visitID = lastVisitID; + nodesCreated++; + nodes2check.add(n); + } + + // is there an escape from this node + // to a hollow node (or destination node) ? + public boolean canEscape(OsmNode n0) { + boolean sawLowIDs = false; + lastVisitID++; + nodes2check.clear(); + nodes2check.add(n0); + while (!nodes2check.isEmpty()) { + OsmNode n = nodes2check.remove(nodes2check.size() - 1); + if (n.visitID < baseID) { + n.visitID = lastVisitID; + nodesCreated++; + for (OsmLink l = n.firstlink; l != null; l = l.getNext(n)) { + OsmNode t = l.getTarget(n); + nodes2check.add(t); + } + } else if (n.visitID < lastVisitID) { + sawLowIDs = true; + } + } + if (sawLowIDs) { + return true; + } + + nodes2check.add(n0); + while (!nodes2check.isEmpty()) { + OsmNode n = nodes2check.remove(nodes2check.size() - 1); + if (n.visitID == lastVisitID) { + n.visitID = lastVisitID; + nodesCreated--; + for (OsmLink l = n.firstlink; l != null; l = l.getNext(n)) { + OsmNode t = l.getTarget(n); + nodes2check.add(t); + } + n.vanish(); + } + } + + return false; + } + + private ArrayList nodes2check; + + public void clearTemp() { + nodes2check = null; + } + + public void collectOutreachers() { + nodes2check = new ArrayList(nodesCreated); + nodesCreated = 0; + for (OsmNode n : hmap.values()) { + addActiveNode(nodes2check, n); + } + + lastVisitID++; + baseID = lastVisitID; + + while (!nodes2check.isEmpty()) { + OsmNode n = nodes2check.remove(nodes2check.size() - 1); + n.visitID = lastVisitID; + + for (OsmLink l = n.firstlink; l != null; l = l.getNext(n)) { + OsmNode t = l.getTarget(n); + if (t.visitID != lastVisitID) { + addActiveNode(nodes2check, t); + } + } + if (destination != null && currentMaxCost < 1000000000) { + int distance = n.calcDistance(destination); + if (distance > currentMaxCost - currentPathCost + 100) { + n.vanish(); + } + } + if (n.firstlink == null) { + nodesCreated--; + } + } + } + + + public ByteArrayUnifier getByteArrayUnifier() { + return abUnifier; + } + + /** + * Get a node from the map + * + * @return the node for the given id if exist, else null + */ + public OsmNode get(int ilon, int ilat) { + testKey.ilon = ilon; + testKey.ilat = ilat; + return hmap.get(testKey); + } + + + public void remove(OsmNode node) { + if (node != endNode1 && node != endNode2) // keep endnodes in hollow-map even when loaded + { // (needed for escape analysis) + hmap.remove(node); + } + } + + /** + * Put a node into the map + * + * @return the previous node if that id existed, else null + */ + public OsmNode put(OsmNode node) { + return hmap.put(node, node); + } + + // ********************** test cleanup ********************** + + private static void addLinks(OsmNode[] nodes, int idx, boolean isBorder, int[] links) { + OsmNode n = nodes[idx]; + n.visitID = isBorder ? 1 : 0; + n.selev = (short) idx; + for (int i : links) { + OsmNode t = nodes[i]; + OsmLink link = n.isLinkUnused() ? n : (t.isLinkUnused() ? t : null); + if (link == null) { + link = new OsmLink(); + } + n.addLink(link, false, t); + } + } + + public static void main(String[] args) { + OsmNode[] nodes = new OsmNode[12]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = new OsmNode((i + 1000) * 1000, (i + 1000) * 1000); + + } + + addLinks(nodes, 0, true, new int[]{1, 5}); // 0 + addLinks(nodes, 1, true, new int[]{}); // 1 + addLinks(nodes, 2, false, new int[]{3, 4}); // 2 + addLinks(nodes, 3, false, new int[]{4}); // 3 + addLinks(nodes, 4, false, new int[]{}); // 4 + addLinks(nodes, 5, true, new int[]{6, 9}); // 5 + addLinks(nodes, 6, false, new int[]{7, 8}); // 6 + addLinks(nodes, 7, false, new int[]{}); // 7 + addLinks(nodes, 8, false, new int[]{}); // 8 + addLinks(nodes, 9, false, new int[]{10, 11}); // 9 + addLinks(nodes, 10, false, new int[]{11}); // 10 + addLinks(nodes, 11, false, new int[]{}); // 11 + + OsmNodesMap nm = new OsmNodesMap(); + + nm.cleanupMode = 2; + + nm.cleanupAndCount(nodes); + + System.out.println("nodesCreated=" + nm.nodesCreated); + nm.cleanupAndCount(nodes); + + System.out.println("nodesCreated=" + nm.nodesCreated); + + } + +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmPos.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmPos.java index e6f48cb..15ece82 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmPos.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmPos.java @@ -1,23 +1,22 @@ -/** - * Interface for a position (OsmNode or OsmPath) - * - * @author ab - */ -package btools.mapaccess; - - -public interface OsmPos -{ - public int getILat(); - - public int getILon(); - - public short getSElev(); - - public double getElev(); - - public int calcDistance( OsmPos p ); - - public long getIdFromPos(); - -} +/** + * Interface for a position (OsmNode or OsmPath) + * + * @author ab + */ +package btools.mapaccess; + + +public interface OsmPos { + public int getILat(); + + public int getILon(); + + public short getSElev(); + + public double getElev(); + + public int calcDistance(OsmPos p); + + public long getIdFromPos(); + +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmTransferNode.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmTransferNode.java index ac9ba1f..1109f45 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmTransferNode.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmTransferNode.java @@ -1,19 +1,16 @@ -/** - * Container for link between two Osm nodes - * - * @author ab - */ -package btools.mapaccess; - - - - -public final class OsmTransferNode -{ - public OsmTransferNode next; - - public int ilon; - public int ilat; - public short selev; - -} +/** + * Container for link between two Osm nodes + * + * @author ab + */ +package btools.mapaccess; + + +public final class OsmTransferNode { + public OsmTransferNode next; + + public int ilon; + public int ilat; + public short selev; + +} diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java index 1798fd3..75ac995 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java @@ -14,29 +14,27 @@ import btools.codec.MicroCache; import btools.util.ByteDataReader; import btools.util.Crc32; -final public class PhysicalFile -{ +final public class PhysicalFile { RandomAccessFile ra = null; long[] fileIndex = new long[25]; int[] fileHeaderCrcs; - + private int fileIndexCrc; public long creationTime; String fileName; - + public int divisor = 80; - public static void main( String[] args ) - { + public static void main(String[] args) { MicroCache.debug = true; try { - checkFileIntegrity( new File( args[0] ) ); + checkFileIntegrity(new File(args[0])); } catch (IOException e) { - System.err.println( "************************************" ); + System.err.println("************************************"); e.printStackTrace(); - System.err.println( "************************************" ); + System.err.println("************************************"); } } @@ -45,56 +43,46 @@ final public class PhysicalFile * * @return the error message if file corrupt, else null */ - public static String checkFileIntegrity( File f ) throws IOException - { + public static String checkFileIntegrity(File f) throws IOException { PhysicalFile pf = null; - try - { + try { DataBuffers dataBuffers = new DataBuffers(); - pf = new PhysicalFile( f, dataBuffers, -1, -1 ); + pf = new PhysicalFile(f, dataBuffers, -1, -1); int div = pf.divisor; - for ( int lonDegree = 0; lonDegree < 5; lonDegree++ ) // does'nt really matter.. + for (int lonDegree = 0; lonDegree < 5; lonDegree++) // does'nt really matter.. { - for ( int latDegree = 0; latDegree < 5; latDegree++ ) // ..where on earth we are + for (int latDegree = 0; latDegree < 5; latDegree++) // ..where on earth we are { - OsmFile osmf = new OsmFile( pf, lonDegree, latDegree, dataBuffers ); - if ( osmf.hasData() ) - for ( int lonIdx = 0; lonIdx < div; lonIdx++ ) - for ( int latIdx = 0; latIdx < div; latIdx++ ) - osmf.createMicroCache( lonDegree * div + lonIdx, latDegree * div + latIdx, dataBuffers, null, null, MicroCache.debug, null ); + OsmFile osmf = new OsmFile(pf, lonDegree, latDegree, dataBuffers); + if (osmf.hasData()) + for (int lonIdx = 0; lonIdx < div; lonIdx++) + for (int latIdx = 0; latIdx < div; latIdx++) + osmf.createMicroCache(lonDegree * div + lonIdx, latDegree * div + latIdx, dataBuffers, null, null, MicroCache.debug, null); } } - } - finally - { - if ( pf != null ) - try - { + } finally { + if (pf != null) + try { pf.ra.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } return null; } - public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws IOException - { + public PhysicalFile(File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion) throws IOException { fileName = f.getName(); byte[] iobuffer = dataBuffers.iobuffer; - ra = new RandomAccessFile( f, "r" ); - ra.readFully( iobuffer, 0, 200 ); - fileIndexCrc = Crc32.crc( iobuffer, 0, 200 ); - ByteDataReader dis = new ByteDataReader( iobuffer ); - for( int i=0; i<25; i++ ) - { + ra = new RandomAccessFile(f, "r"); + ra.readFully(iobuffer, 0, 200); + fileIndexCrc = Crc32.crc(iobuffer, 0, 200); + ByteDataReader dis = new ByteDataReader(iobuffer); + for (int i = 0; i < 25; i++) { long lv = dis.readLong(); - short readVersion = (short)(lv >> 48); - if ( i == 0 && lookupVersion != -1 && readVersion != lookupVersion ) - { - throw new IOException( "lookup version mismatch (old rd5?) lookups.dat=" - + lookupVersion + " " + f. getAbsolutePath() + "=" + readVersion ); + short readVersion = (short) (lv >> 48); + if (i == 0 && lookupVersion != -1 && readVersion != lookupVersion) { + throw new IOException("lookup version mismatch (old rd5?) lookups.dat=" + + lookupVersion + " " + f.getAbsolutePath() + "=" + readVersion); } fileIndex[i] = lv & 0xffffffffffffL; } @@ -103,36 +91,30 @@ final public class PhysicalFile long len = ra.length(); long pos = fileIndex[24]; - int extraLen = 8 + 26*4; + int extraLen = 8 + 26 * 4; - if ( len == pos ) return; // old format o.k. + if (len == pos) return; // old format o.k. - if ( len < pos+extraLen ) // > is o.k. for future extensions! + if (len < pos + extraLen) // > is o.k. for future extensions! { - throw new IOException( "file of size " + len + " too short, should be " + (pos+extraLen) ); + throw new IOException("file of size " + len + " too short, should be " + (pos + extraLen)); } - - ra.seek( pos ); - ra.readFully( iobuffer, 0, extraLen ); - dis = new ByteDataReader( iobuffer ); + + ra.seek(pos); + ra.readFully(iobuffer, 0, extraLen); + dis = new ByteDataReader(iobuffer); creationTime = dis.readLong(); int crcData = dis.readInt(); - if ( crcData == fileIndexCrc ) - { + if (crcData == fileIndexCrc) { divisor = 80; // old format - } - else if ( (crcData ^ 2) == fileIndexCrc ) - { + } else if ((crcData ^ 2) == fileIndexCrc) { divisor = 32; // new format - } - else - { - throw new IOException( "top index checksum error" ); + } else { + throw new IOException("top index checksum error"); } fileHeaderCrcs = new int[25]; - for( int i=0; i<25; i++ ) - { + for (int i = 0; i < 25; i++) { fileHeaderCrcs[i] = dis.readInt(); } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java index 3b33904..fb887da 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java @@ -13,90 +13,77 @@ import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -final public class Rd5DiffManager -{ - public static void main( String[] args ) throws Exception - { - calcDiffs( new File( args[0] ),new File( args[1] ) ); +final public class Rd5DiffManager { + public static void main(String[] args) throws Exception { + calcDiffs(new File(args[0]), new File(args[1])); } /** * Compute diffs for all RD5 files */ - public static void calcDiffs( File oldDir, File newDir ) throws Exception - { - File oldDiffDir = new File( oldDir, "diff" ); - File newDiffDir = new File( newDir, "diff" ); + public static void calcDiffs(File oldDir, File newDir) throws Exception { + File oldDiffDir = new File(oldDir, "diff"); + File newDiffDir = new File(newDir, "diff"); File[] filesNew = newDir.listFiles(); - - for( File fn : filesNew ) - { + + for (File fn : filesNew) { String name = fn.getName(); - if ( !name.endsWith( ".rd5" ) ) - { + if (!name.endsWith(".rd5")) { continue; } - if ( fn.length() < 1024*1024 ) - { + if (fn.length() < 1024 * 1024) { continue; // exclude very small files from diffing } - String basename = name.substring( 0, name.length() - 4 ); - File fo = new File( oldDir, name ); - if ( !fo.isFile() ) - { + String basename = name.substring(0, name.length() - 4); + File fo = new File(oldDir, name); + if (!fo.isFile()) { continue; } - - // calculate MD5 of old file - String md5 = getMD5( fo ); - String md5New = getMD5( fn ); - - System.out.println( "name=" + name + " md5=" + md5 ); - - File specificNewDiffs = new File( newDiffDir, basename ); + // calculate MD5 of old file + String md5 = getMD5(fo); + + String md5New = getMD5(fn); + + System.out.println("name=" + name + " md5=" + md5); + + File specificNewDiffs = new File(newDiffDir, basename); specificNewDiffs.mkdirs(); - + String diffFileName = md5 + ".df5"; - File diffFile = new File( specificNewDiffs, diffFileName ); - + File diffFile = new File(specificNewDiffs, diffFileName); + String dummyDiffFileName = md5New + ".df5"; - File dummyDiffFile = new File( specificNewDiffs, dummyDiffFileName ); + File dummyDiffFile = new File(specificNewDiffs, dummyDiffFileName); dummyDiffFile.createNewFile(); // calc the new diff - Rd5DiffTool.diff2files( fo, fn, diffFile ); - + Rd5DiffTool.diff2files(fo, fn, diffFile); + // ... and add that to old diff files - File specificOldDiffs = new File( oldDiffDir, basename ); - if ( specificOldDiffs.isDirectory() ) - { + File specificOldDiffs = new File(oldDiffDir, basename); + if (specificOldDiffs.isDirectory()) { File[] oldDiffs = specificOldDiffs.listFiles(); - for( File od : oldDiffs ) - { - if ( !od.getName().endsWith( ".df5" ) ) - { + for (File od : oldDiffs) { + if (!od.getName().endsWith(".df5")) { continue; } - if ( System.currentTimeMillis() - od.lastModified() > 9*86400000L ) - { + if (System.currentTimeMillis() - od.lastModified() > 9 * 86400000L) { continue; // limit diff history to 9 days } - - File updatedDiff = new File( specificNewDiffs, od.getName() ); - if ( !updatedDiff.exists() ) - { - Rd5DiffTool.addDeltas( od, diffFile, updatedDiff ); - updatedDiff.setLastModified( od.lastModified() ); + + File updatedDiff = new File(specificNewDiffs, od.getName()); + if (!updatedDiff.exists()) { + Rd5DiffTool.addDeltas(od, diffFile, updatedDiff); + updatedDiff.setLastModified(od.lastModified()); } } } } } - public static String getMD5( File f ) throws IOException - { + public static String getMD5(File f) throws IOException { try { MessageDigest md = MessageDigest.getInstance("MD5"); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f)); @@ -121,9 +108,8 @@ final public class Rd5DiffManager throw new IOException("MD5 algorithm not available", e); } } - - private static char hexChar( int v ) - { - return (char) ( v > 9 ? 'a' + (v-10) : '0' + v ); + + private static char hexChar(int v) { + return (char) (v > 9 ? 'a' + (v - 10) : '0' + v); } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java index c5ffbf1..f8ab3ff 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java @@ -22,179 +22,147 @@ import btools.codec.StatCoderContext; import btools.util.Crc32; import btools.util.ProgressListener; -final public class Rd5DiffTool implements ProgressListener -{ - public static void main( String[] args ) throws Exception - { - if ( args.length == 2 ) - { - reEncode( new File( args[0] ),new File( args[1] ) ); +final public class Rd5DiffTool implements ProgressListener { + public static void main(String[] args) throws Exception { + if (args.length == 2) { + reEncode(new File(args[0]), new File(args[1])); return; } - if ( args[1].endsWith( ".df5" ) ) - { - if ( args[0].endsWith( ".df5" ) ) - { - addDeltas( new File( args[0] ),new File( args[1] ), new File( args[2] ) ); + if (args[1].endsWith(".df5")) { + if (args[0].endsWith(".df5")) { + addDeltas(new File(args[0]), new File(args[1]), new File(args[2])); + } else { + recoverFromDelta(new File(args[0]), new File(args[1]), new File(args[2]), new Rd5DiffTool() /*, new File( args[3] ) */); } - else - { - recoverFromDelta( new File( args[0] ),new File( args[1] ), new File( args[2] ), new Rd5DiffTool() /*, new File( args[3] ) */ ); - } - } - else - { - diff2files( new File( args[0] ),new File( args[1] ), new File( args[2] ) ); + } else { + diff2files(new File(args[0]), new File(args[1]), new File(args[2])); } } - @Override - public void updateProgress(String task, int progress) { - System.out.println(task + ": " + progress + "%"); - } - - @Override - public boolean isCanceled() - { - return false; - } + @Override + public void updateProgress(String task, int progress) { + System.out.println(task + ": " + progress + "%"); + } - private static long[] readFileIndex( DataInputStream dis, DataOutputStream dos ) throws IOException - { + @Override + public boolean isCanceled() { + return false; + } + + private static long[] readFileIndex(DataInputStream dis, DataOutputStream dos) throws IOException { long[] fileIndex = new long[25]; - for( int i=0; i<25; i++ ) - { + for (int i = 0; i < 25; i++) { long lv = dis.readLong(); fileIndex[i] = lv & 0xffffffffffffL; - if ( dos != null ) - { - dos.writeLong( lv ); + if (dos != null) { + dos.writeLong(lv); } } return fileIndex; } - private static long getTileStart( long[] index, int tileIndex ) - { + private static long getTileStart(long[] index, int tileIndex) { return tileIndex > 0 ? index[tileIndex - 1] : 200L; } - private static long getTileEnd( long[] index, int tileIndex ) - { + private static long getTileEnd(long[] index, int tileIndex) { return index[tileIndex]; } - private static int[] readPosIndex( DataInputStream dis, DataOutputStream dos ) throws IOException - { + private static int[] readPosIndex(DataInputStream dis, DataOutputStream dos) throws IOException { int[] posIndex = new int[1024]; - for( int i=0; i<1024; i++ ) - { + for (int i = 0; i < 1024; i++) { int iv = dis.readInt(); posIndex[i] = iv; - if ( dos != null ) - { - dos.writeInt( iv ); + if (dos != null) { + dos.writeInt(iv); } } return posIndex; } - private static int getPosIdx( int[] posIdx, int idx ) - { + private static int getPosIdx(int[] posIdx, int idx) { return idx == -1 ? 4096 : posIdx[idx]; } - private static byte[] createMicroCache( int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode ) throws IOException - { - if ( posIdx == null ) - { + private static byte[] createMicroCache(int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode) throws IOException { + if (posIdx == null) { return null; } - int size = getPosIdx( posIdx, tileIdx ) - getPosIdx( posIdx, tileIdx-1 ); - if ( size == 0 ) - { + int size = getPosIdx(posIdx, tileIdx) - getPosIdx(posIdx, tileIdx - 1); + if (size == 0) { return null; } - if ( deltaMode ) - { + if (deltaMode) { size = dis.readInt(); } byte[] ab = new byte[size]; - dis.readFully( ab ); + dis.readFully(ab); return ab; } - private static MicroCache createMicroCache( byte[] ab, DataBuffers dataBuffers ) - { - if ( ab == null || ab.length == 0 ) - { + private static MicroCache createMicroCache(byte[] ab, DataBuffers dataBuffers) { + if (ab == null || ab.length == 0) { return MicroCache.emptyCache(); } - StatCoderContext bc = new StatCoderContext( ab ); - return new MicroCache2( bc, dataBuffers, 0, 0, 32, null, null ); + StatCoderContext bc = new StatCoderContext(ab); + return new MicroCache2(bc, dataBuffers, 0, 0, 32, null, null); } /** * Compute the delta between 2 RD5 files and * show statistics on the expected size of the delta file */ - public static void diff2files( File f1, File f2, File outFile ) throws Exception - { + public static void diff2files(File f1, File f2, File outFile) throws Exception { byte[] abBuf1 = new byte[10 * 1024 * 1024]; byte[] abBuf2 = new byte[10 * 1024 * 1024]; - + int nodesDiff = 0; int diffedTiles = 0; - + long bytesDiff = 0L; - DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) ); - DataInputStream dis2 = new DataInputStream( new BufferedInputStream( new FileInputStream( f2 ) ) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); - MCOutputStream mcOut = new MCOutputStream( dos, abBuf1 ); + DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1))); + DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2))); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); + MCOutputStream mcOut = new MCOutputStream(dos, abBuf1); // copy header to outfile - long[] fileIndex1 = readFileIndex( dis1, null ); - long[] fileIndex2 = readFileIndex( dis2, dos ); + long[] fileIndex1 = readFileIndex(dis1, null); + long[] fileIndex2 = readFileIndex(dis2, dos); long t0 = System.currentTimeMillis(); - try - { + try { DataBuffers dataBuffers = new DataBuffers(); - for ( int subFileIdx = 0; subFileIdx < 25; subFileIdx++ ) - { - boolean hasData1 = getTileStart( fileIndex1, subFileIdx ) < getTileEnd( fileIndex1, subFileIdx ); - boolean hasData2 = getTileStart( fileIndex2, subFileIdx ) < getTileEnd( fileIndex2, subFileIdx ); + for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) { + boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx); + boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx); - int[] posIdx1 = hasData1 ? readPosIndex( dis1, null ) : null; - int[] posIdx2 = hasData2 ? readPosIndex( dis2, dos ) : null; + int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null; + int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null; - for ( int tileIdx = 0; tileIdx < 1024; tileIdx++ ) - { - byte[] ab1 = createMicroCache( posIdx1, tileIdx, dis1, false ); - byte[] ab2 = createMicroCache( posIdx2, tileIdx, dis2, false ); + for (int tileIdx = 0; tileIdx < 1024; tileIdx++) { + byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false); + byte[] ab2 = createMicroCache(posIdx2, tileIdx, dis2, false); - MicroCache mc; - if ( Arrays.equals( ab1, ab2 ) ) - { - mc = MicroCache.emptyCache(); // empty diff - } - else // calc diff of the 2 tiles - { - MicroCache mc1 = createMicroCache( ab1, dataBuffers ); - MicroCache mc2 = createMicroCache( ab2, dataBuffers ); - mc = new MicroCache2( mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32 ); - mc.calcDelta( mc1, mc2 ); - } + MicroCache mc; + if (Arrays.equals(ab1, ab2)) { + mc = MicroCache.emptyCache(); // empty diff + } else // calc diff of the 2 tiles + { + MicroCache mc1 = createMicroCache(ab1, dataBuffers); + MicroCache mc2 = createMicroCache(ab2, dataBuffers); + mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32); + mc.calcDelta(mc1, mc2); + } - int len = mcOut.writeMC( mc ); - if ( len > 0 ) - { - bytesDiff += len; - nodesDiff += mc.getSize(); - diffedTiles++; + int len = mcOut.writeMC(mc); + if (len > 0) { + bytesDiff += len; + nodesDiff += mc.getSize(); + diffedTiles++; /* // do some consistemcy checks on the encoding @@ -238,116 +206,93 @@ final public class Rd5DiffTool implements ProgressListener } // write any remaining data to the output file - for(;;) - { - int len = dis2.read( abBuf1 ); - if (len < 0) - { + for (; ; ) { + int len = dis2.read(abBuf1); + if (len < 0) { break; } - dos.write( abBuf1, 0, len ); + dos.write(abBuf1, 0, len); } long t1 = System.currentTimeMillis(); - System.out.println( "nodesDiff=" + nodesDiff + " bytesDiff=" + bytesDiff + " diffedTiles=" + diffedTiles + " took " + (t1-t0) + "ms" ); - } - finally - { - if ( dis1 != null ) - { - try - { + System.out.println("nodesDiff=" + nodesDiff + " bytesDiff=" + bytesDiff + " diffedTiles=" + diffedTiles + " took " + (t1 - t0) + "ms"); + } finally { + if (dis1 != null) { + try { dis1.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dis2 != null ) - { - try - { + if (dis2 != null) { + try { dis2.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dos != null ) - { - try - { + if (dos != null) { + try { dos.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } } } - public static void recoverFromDelta( File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */ ) throws IOException - { - if ( f2.length() == 0L ) - { - copyFile( f1, outFile, progress ); + public static void recoverFromDelta(File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */) throws IOException { + if (f2.length() == 0L) { + copyFile(f1, outFile, progress); return; } - + byte[] abBuf1 = new byte[10 * 1024 * 1024]; byte[] abBuf2 = new byte[10 * 1024 * 1024]; - + boolean canceled = false; long t0 = System.currentTimeMillis(); - DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) ); - DataInputStream dis2 = new DataInputStream( new BufferedInputStream( new FileInputStream( f2 ) ) ); + DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1))); + DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2))); // DataInputStream disCmp = new DataInputStream( new BufferedInputStream( new FileInputStream( cmpFile ) ) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); // copy header to outfile - long[] fileIndex1 = readFileIndex( dis1, null ); - long[] fileIndex2 = readFileIndex( dis2, dos ); + long[] fileIndex1 = readFileIndex(dis1, null); + long[] fileIndex2 = readFileIndex(dis2, dos); // long[] fileIndexCmp = readFileIndex( disCmp, null ); int lastPct = -1; - try - { + try { DataBuffers dataBuffers = new DataBuffers(); - MCInputStream mcIn = new MCInputStream( dis2, dataBuffers ); + MCInputStream mcIn = new MCInputStream(dis2, dataBuffers); - for ( int subFileIdx = 0; subFileIdx < 25; subFileIdx++ ) - { - boolean hasData1 = getTileStart( fileIndex1, subFileIdx ) < getTileEnd( fileIndex1, subFileIdx ); // has the basefile data - boolean hasData2 = getTileStart( fileIndex2, subFileIdx ) < getTileEnd( fileIndex2, subFileIdx ); // has the *result* data + for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) { + boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx); // has the basefile data + boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx); // has the *result* data // boolean hasDataCmp = getTileStart( fileIndexCmp, subFileIdx ) < getTileEnd( fileIndexCmp, subFileIdx ); - int[] posIdx1 = hasData1 ? readPosIndex( dis1, null ) : null; - int[] posIdx2 = hasData2 ? readPosIndex( dis2, dos ) : null; + int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null; + int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null; - // int[] posIdxCmp = hasDataCmp ? readPosIndex( disCmp, null ) : null; + // int[] posIdxCmp = hasDataCmp ? readPosIndex( disCmp, null ) : null; - for ( int tileIdx = 0; tileIdx < 1024; tileIdx++ ) - { - if ( progress.isCanceled() ) - { - canceled = true; - return; - } - double bytesProcessed = getTileStart( fileIndex1, subFileIdx ) + ( posIdx1 == null ? 0 : getPosIdx( posIdx1, tileIdx-1 ) ); - int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 ); - if ( pct != lastPct ) - { - progress.updateProgress("Applying delta", pct); - lastPct = pct; - } + for (int tileIdx = 0; tileIdx < 1024; tileIdx++) { + if (progress.isCanceled()) { + canceled = true; + return; + } + double bytesProcessed = getTileStart(fileIndex1, subFileIdx) + (posIdx1 == null ? 0 : getPosIdx(posIdx1, tileIdx - 1)); + int pct = (int) (100. * bytesProcessed / getTileEnd(fileIndex1, 24) + 0.5); + if (pct != lastPct) { + progress.updateProgress("Applying delta", pct); + lastPct = pct; + } - byte[] ab1 = createMicroCache( posIdx1, tileIdx, dis1, false ); - MicroCache mc2 = mcIn.readMC(); - int targetSize = posIdx2 == null ? 0 : getPosIdx( posIdx2, tileIdx ) - getPosIdx( posIdx2, tileIdx-1 ); + byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false); + MicroCache mc2 = mcIn.readMC(); + int targetSize = posIdx2 == null ? 0 : getPosIdx(posIdx2, tileIdx) - getPosIdx(posIdx2, tileIdx - 1); /* int targetSizeCmp = getPosIdx( posIdxCmp, tileIdx ) - getPosIdx( posIdxCmp, tileIdx-1 ); if ( targetSizeCmp != targetSize ) throw new IllegalArgumentException( "target size mismatch: "+ targetSize + "," + targetSizeCmp ); @@ -355,38 +300,33 @@ final public class Rd5DiffTool implements ProgressListener disCmp.readFully( abCmp ); */ - // no-delta shortcut: just copy base data - if ( mc2.getSize() == 0 ) - { - if ( ab1 != null ) - { - dos.write( ab1 ); - } - int newTargetSize = ab1 == null ? 0 : ab1.length; - if ( targetSize != newTargetSize ) - { - throw new RuntimeException( "size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "!=" + newTargetSize ); - } - continue; - } + // no-delta shortcut: just copy base data + if (mc2.getSize() == 0) { + if (ab1 != null) { + dos.write(ab1); + } + int newTargetSize = ab1 == null ? 0 : ab1.length; + if (targetSize != newTargetSize) { + throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "!=" + newTargetSize); + } + continue; + } - // this is the real delta case (using decode->delta->encode ) + // this is the real delta case (using decode->delta->encode ) - MicroCache mc1 = createMicroCache( ab1, dataBuffers ); + MicroCache mc1 = createMicroCache(ab1, dataBuffers); - MicroCache mc = new MicroCache2( mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32 ); - mc.addDelta( mc1, mc2, false ); - - if ( mc.size() == 0 ) - { - if ( targetSize != 0 ) - { - throw new RuntimeException( "size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + ">0" ); - } - continue; - } + MicroCache mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32); + mc.addDelta(mc1, mc2, false); - int len = mc.encodeMicroCache( abBuf1 ); + if (mc.size() == 0) { + if (targetSize != 0) { + throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + ">0"); + } + continue; + } + + int len = mc.encodeMicroCache(abBuf1); /* System.out.println( "comparing for subFileIdx=" + subFileIdx + " tileIdx=" + tileIdx ); boolean isequal = true; @@ -406,377 +346,290 @@ final public class Rd5DiffTool implements ProgressListener } } */ - dos.write( abBuf1, 0, len ); - dos.writeInt( Crc32.crc( abBuf1, 0, len ) ^ 2 ); - if ( targetSize != len+4 ) - { - throw new RuntimeException( "size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "<>" + (len+4) ); - } + dos.write(abBuf1, 0, len); + dos.writeInt(Crc32.crc(abBuf1, 0, len) ^ 2); + if (targetSize != len + 4) { + throw new RuntimeException("size mismatch at " + subFileIdx + "/" + tileIdx + " " + targetSize + "<>" + (len + 4)); + } } mcIn.finish(); } // write any remaining data to the output file - for(;;) - { - int len = dis2.read( abBuf1 ); - if (len < 0) - { + for (; ; ) { + int len = dis2.read(abBuf1); + if (len < 0) { break; } - dos.write( abBuf1, 0, len ); + dos.write(abBuf1, 0, len); } - long t1 = System.currentTimeMillis(); - System.out.println( "recovering from diffs took " + (t1-t0) + "ms" ); - } - finally - { - if ( dis1 != null ) - { - try - { + long t1 = System.currentTimeMillis(); + System.out.println("recovering from diffs took " + (t1 - t0) + "ms"); + } finally { + if (dis1 != null) { + try { dis1.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dis2 != null ) - { - try - { + if (dis2 != null) { + try { dis2.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dos != null ) - { - try - { + if (dos != null) { + try { dos.close(); + } catch (Exception ee) { } - catch (Exception ee) - { - } - if ( canceled ) - { + if (canceled) { outFile.delete(); } } } } - public static void copyFile( File f1, File outFile, ProgressListener progress ) throws IOException - { + public static void copyFile(File f1, File outFile, ProgressListener progress) throws IOException { boolean canceled = false; - DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); + DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1))); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); int lastPct = -1; long sizeTotal = f1.length(); long sizeRead = 0L; - try - { - byte buf[] = new byte[65536]; - for (;;) - { - if ( progress.isCanceled() ) - { + try { + byte buf[] = new byte[65536]; + for (; ; ) { + if (progress.isCanceled()) { canceled = true; return; } - int pct = (int)( (100. * sizeRead) / (sizeTotal+1) + 0.5 ); - if ( pct != lastPct ) - { + int pct = (int) ((100. * sizeRead) / (sizeTotal + 1) + 0.5); + if (pct != lastPct) { progress.updateProgress("Copying", pct); lastPct = pct; } - int len = dis1.read( buf ); - if ( len <= 0 ) - { + int len = dis1.read(buf); + if (len <= 0) { break; } sizeRead += len; - dos.write( buf, 0, len ); + dos.write(buf, 0, len); } - } - finally - { - if ( dis1 != null ) - { - try - { + } finally { + if (dis1 != null) { + try { dis1.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dos != null ) - { - try - { + if (dos != null) { + try { dos.close(); + } catch (Exception ee) { } - catch (Exception ee) - { - } - if ( canceled ) - { + if (canceled) { outFile.delete(); } } } } - public static void addDeltas( File f1, File f2, File outFile ) throws Exception - { + public static void addDeltas(File f1, File f2, File outFile) throws Exception { byte[] abBuf1 = new byte[10 * 1024 * 1024]; byte[] abBuf2 = new byte[10 * 1024 * 1024]; - DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) ); - DataInputStream dis2 = new DataInputStream( new BufferedInputStream( new FileInputStream( f2 ) ) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); + DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1))); + DataInputStream dis2 = new DataInputStream(new BufferedInputStream(new FileInputStream(f2))); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); // copy subfile-header to outfile - long[] fileIndex1 = readFileIndex( dis1, null ); - long[] fileIndex2 = readFileIndex( dis2, dos ); + long[] fileIndex1 = readFileIndex(dis1, null); + long[] fileIndex2 = readFileIndex(dis2, dos); long t0 = System.currentTimeMillis(); - try - { + try { DataBuffers dataBuffers = new DataBuffers(); - MCInputStream mcIn1 = new MCInputStream( dis1, dataBuffers ); - MCInputStream mcIn2 = new MCInputStream( dis2, dataBuffers ); - MCOutputStream mcOut = new MCOutputStream( dos, abBuf1 ); + MCInputStream mcIn1 = new MCInputStream(dis1, dataBuffers); + MCInputStream mcIn2 = new MCInputStream(dis2, dataBuffers); + MCOutputStream mcOut = new MCOutputStream(dos, abBuf1); - for ( int subFileIdx = 0; subFileIdx < 25; subFileIdx++ ) - { - // copy tile-header to outfile - boolean hasData1 = getTileStart( fileIndex1, subFileIdx ) < getTileEnd( fileIndex1, subFileIdx ); - boolean hasData2 = getTileStart( fileIndex2, subFileIdx ) < getTileEnd( fileIndex2, subFileIdx ); - int[] posIdx1 = hasData1 ? readPosIndex( dis1, null ) : null; - int[] posIdx2 = hasData2 ? readPosIndex( dis2, dos ) : null; + for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) { + // copy tile-header to outfile + boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx); + boolean hasData2 = getTileStart(fileIndex2, subFileIdx) < getTileEnd(fileIndex2, subFileIdx); + int[] posIdx1 = hasData1 ? readPosIndex(dis1, null) : null; + int[] posIdx2 = hasData2 ? readPosIndex(dis2, dos) : null; - for ( int tileIdx = 0; tileIdx < 1024; tileIdx++ ) - { - MicroCache mc1 = mcIn1.readMC(); - MicroCache mc2 = mcIn2.readMC(); - MicroCache mc; - if ( mc1.getSize() == 0 && mc2.getSize() == 0 ) - { - mc = mc1; - } - else - { - mc = new MicroCache2( mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32 ); - mc.addDelta( mc1, mc2, true ); - } - mcOut.writeMC( mc ); + for (int tileIdx = 0; tileIdx < 1024; tileIdx++) { + MicroCache mc1 = mcIn1.readMC(); + MicroCache mc2 = mcIn2.readMC(); + MicroCache mc; + if (mc1.getSize() == 0 && mc2.getSize() == 0) { + mc = mc1; + } else { + mc = new MicroCache2(mc1.getSize() + mc2.getSize(), abBuf2, 0, 0, 32); + mc.addDelta(mc1, mc2, true); + } + mcOut.writeMC(mc); } mcIn1.finish(); mcIn2.finish(); mcOut.finish(); } // write any remaining data to the output file - for(;;) - { - int len = dis2.read( abBuf1 ); - if (len < 0) - { + for (; ; ) { + int len = dis2.read(abBuf1); + if (len < 0) { break; } - dos.write( abBuf1, 0, len ); + dos.write(abBuf1, 0, len); } long t1 = System.currentTimeMillis(); - System.out.println( "adding diffs took " + (t1-t0) + "ms" ); - } - finally - { - if ( dis1 != null ) - { - try - { + System.out.println("adding diffs took " + (t1 - t0) + "ms"); + } finally { + if (dis1 != null) { + try { dis1.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dis2 != null ) - { - try - { + if (dis2 != null) { + try { dis2.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dos != null ) - { - try - { + if (dos != null) { + try { dos.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } } } - public static void reEncode( File f1, File outFile ) throws Exception - { + public static void reEncode(File f1, File outFile) throws Exception { byte[] abBuf1 = new byte[10 * 1024 * 1024]; - DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); + DataInputStream dis1 = new DataInputStream(new BufferedInputStream(new FileInputStream(f1))); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile))); // copy header to outfile - long[] fileIndex1 = readFileIndex( dis1, dos ); + long[] fileIndex1 = readFileIndex(dis1, dos); long t0 = System.currentTimeMillis(); - try - { + try { DataBuffers dataBuffers = new DataBuffers(); - for ( int subFileIdx = 0; subFileIdx < 25; subFileIdx++ ) - { - boolean hasData1 = getTileStart( fileIndex1, subFileIdx ) < getTileEnd( fileIndex1, subFileIdx ); + for (int subFileIdx = 0; subFileIdx < 25; subFileIdx++) { + boolean hasData1 = getTileStart(fileIndex1, subFileIdx) < getTileEnd(fileIndex1, subFileIdx); - int[] posIdx1 = hasData1 ? readPosIndex( dis1, dos ) : null; + int[] posIdx1 = hasData1 ? readPosIndex(dis1, dos) : null; - for ( int tileIdx = 0; tileIdx < 1024; tileIdx++ ) - { - byte[] ab1 = createMicroCache( posIdx1, tileIdx, dis1, false ); + for (int tileIdx = 0; tileIdx < 1024; tileIdx++) { + byte[] ab1 = createMicroCache(posIdx1, tileIdx, dis1, false); - if ( ab1 == null ) continue; + if (ab1 == null) continue; - MicroCache mc1 = createMicroCache( ab1, dataBuffers ); + MicroCache mc1 = createMicroCache(ab1, dataBuffers); - int len = mc1.encodeMicroCache( abBuf1 ); + int len = mc1.encodeMicroCache(abBuf1); - dos.write( abBuf1, 0, len ); - dos.writeInt( Crc32.crc( abBuf1, 0, len ) ^ 2 ); + dos.write(abBuf1, 0, len); + dos.writeInt(Crc32.crc(abBuf1, 0, len) ^ 2); } } // write any remaining data to the output file - for(;;) - { - int len = dis1.read( abBuf1 ); - if (len < 0) - { + for (; ; ) { + int len = dis1.read(abBuf1); + if (len < 0) { break; } - dos.write( abBuf1, 0, len ); + dos.write(abBuf1, 0, len); } long t1 = System.currentTimeMillis(); - System.out.println( "re-encoding took " + (t1-t0) + "ms" ); - } - finally - { - if ( dis1 != null ) - { - try - { + System.out.println("re-encoding took " + (t1 - t0) + "ms"); + } finally { + if (dis1 != null) { + try { dis1.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } - if ( dos != null ) - { - try - { + if (dos != null) { + try { dos.close(); - } - catch (Exception ee) - { + } catch (Exception ee) { } } } } - private static class MCOutputStream - { + private static class MCOutputStream { private DataOutputStream dos; private byte[] buffer; private short skips = 0; - public MCOutputStream(DataOutputStream dos, byte[] buffer) - { + public MCOutputStream(DataOutputStream dos, byte[] buffer) { this.dos = dos; this.buffer = buffer; } - public int writeMC(MicroCache mc) throws Exception - { - if ( mc.getSize() == 0 ) - { + public int writeMC(MicroCache mc) throws Exception { + if (mc.getSize() == 0) { skips++; return 0; } - dos.writeShort( skips ); + dos.writeShort(skips); skips = 0; - int len = mc.encodeMicroCache( buffer ); - if ( len == 0 ) - { - throw new IllegalArgumentException( "encoded buffer of non-empty micro-cache cannot be empty" ); + int len = mc.encodeMicroCache(buffer); + if (len == 0) { + throw new IllegalArgumentException("encoded buffer of non-empty micro-cache cannot be empty"); } - dos.writeInt( len ); - dos.write( buffer, 0, len ); + dos.writeInt(len); + dos.write(buffer, 0, len); return len; } - public void finish() throws Exception - { - if ( skips > 0 ) - { - dos.writeShort( skips ); + public void finish() throws Exception { + if (skips > 0) { + dos.writeShort(skips); skips = 0; } } } - private static class MCInputStream - { + private static class MCInputStream { private short skips = -1; private DataInputStream dis; private DataBuffers dataBuffers; private MicroCache empty = MicroCache.emptyCache(); - public MCInputStream( DataInputStream dis, DataBuffers dataBuffers ) - { + public MCInputStream(DataInputStream dis, DataBuffers dataBuffers) { this.dis = dis; this.dataBuffers = dataBuffers; } - public MicroCache readMC() throws IOException - { - if (skips < 0 ) - { + public MicroCache readMC() throws IOException { + if (skips < 0) { skips = dis.readShort(); } MicroCache mc = empty; - if ( skips == 0 ) - { + if (skips == 0) { int size = dis.readInt(); byte[] ab = new byte[size]; - dis.readFully( ab ); - StatCoderContext bc = new StatCoderContext( ab ); - mc = new MicroCache2( bc, dataBuffers, 0, 0, 32, null, null ); + dis.readFully(ab); + StatCoderContext bc = new StatCoderContext(ab); + mc = new MicroCache2(bc, dataBuffers, 0, 0, 32, null, null); } skips--; return mc; } - public void finish() - { + public void finish() { skips = -1; } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffValidator.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffValidator.java index 99bca62..b251cfc 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffValidator.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffValidator.java @@ -7,62 +7,54 @@ package btools.mapaccess; import java.io.File; -final public class Rd5DiffValidator -{ - public static void main( String[] args ) throws Exception - { - validateDiffs( new File( args[0] ),new File( args[1] ) ); +final public class Rd5DiffValidator { + public static void main(String[] args) throws Exception { + validateDiffs(new File(args[0]), new File(args[1])); } /** * Validate diffs for all DF5 files */ - public static void validateDiffs( File oldDir, File newDir ) throws Exception - { - File oldDiffDir = new File( oldDir, "diff" ); - File newDiffDir = new File( newDir, "diff" ); + public static void validateDiffs(File oldDir, File newDir) throws Exception { + File oldDiffDir = new File(oldDir, "diff"); + File newDiffDir = new File(newDir, "diff"); File[] filesNew = newDir.listFiles(); - - for( File fn : filesNew ) - { + + for (File fn : filesNew) { String name = fn.getName(); - if ( !name.endsWith( ".rd5" ) ) - { + if (!name.endsWith(".rd5")) { continue; } - if ( fn.length() < 1024*1024 ) - { + if (fn.length() < 1024 * 1024) { continue; // expecting no diff for small files } - String basename = name.substring( 0, name.length() - 4 ); - File fo = new File( oldDir, name ); - if ( !fo.isFile() ) - { + String basename = name.substring(0, name.length() - 4); + File fo = new File(oldDir, name); + if (!fo.isFile()) { continue; } // calculate MD5 of old file - String md5 = Rd5DiffManager.getMD5( fo ); + String md5 = Rd5DiffManager.getMD5(fo); - String md5New = Rd5DiffManager.getMD5( fn ); + String md5New = Rd5DiffManager.getMD5(fn); - System.out.println( "name=" + name + " md5=" + md5 ); + System.out.println("name=" + name + " md5=" + md5); - File specificNewDiffs = new File( newDiffDir, basename ); + File specificNewDiffs = new File(newDiffDir, basename); String diffFileName = md5 + ".df5"; - File diffFile = new File( specificNewDiffs, diffFileName ); + File diffFile = new File(specificNewDiffs, diffFileName); - File fcmp = new File( oldDir, name + "_tmp" ); + File fcmp = new File(oldDir, name + "_tmp"); // merge old file and diff - Rd5DiffTool.recoverFromDelta( fo, diffFile, fcmp, new Rd5DiffTool() ); - String md5Cmp = Rd5DiffManager.getMD5( fcmp ); + Rd5DiffTool.recoverFromDelta(fo, diffFile, fcmp, new Rd5DiffTool()); + String md5Cmp = Rd5DiffManager.getMD5(fcmp); - if ( !md5Cmp.equals( md5New ) ) - { - throw new RuntimeException( "**************** md5 mismatch!! *****************" ); + if (!md5Cmp.equals(md5New)) { + throw new RuntimeException("**************** md5 mismatch!! *****************"); } } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/StorageConfigHelper.java b/brouter-mapaccess/src/main/java/btools/mapaccess/StorageConfigHelper.java index 094eb0e..32cc672 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/StorageConfigHelper.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/StorageConfigHelper.java @@ -9,51 +9,38 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; -public class StorageConfigHelper -{ - public static File getSecondarySegmentDir( File segmentDir ) - { - return getStorageLocation( segmentDir, "secondary_segment_dir=" ); +public class StorageConfigHelper { + public static File getSecondarySegmentDir(File segmentDir) { + return getStorageLocation(segmentDir, "secondary_segment_dir="); } - public static File getAdditionalMaptoolDir( File segmentDir ) - { - return getStorageLocation( segmentDir, "additional_maptool_dir=" ); + public static File getAdditionalMaptoolDir(File segmentDir) { + return getStorageLocation(segmentDir, "additional_maptool_dir="); } - private static File getStorageLocation( File segmentDir, String tag ) - { + private static File getStorageLocation(File segmentDir, String tag) { File res = null; BufferedReader br = null; - File configFile = new File (segmentDir, "storageconfig.txt"); - try - { - br = new BufferedReader( new FileReader( configFile ) ); - for ( ;; ) - { + File configFile = new File(segmentDir, "storageconfig.txt"); + try { + br = new BufferedReader(new FileReader(configFile)); + for (; ; ) { String line = br.readLine(); - if ( line == null ) break; + if (line == null) break; line = line.trim(); - if ( line.startsWith( "#" ) ) continue; - if ( line.startsWith( tag ) ) - { - String path = line.substring( tag.length() ).trim(); - res = path.startsWith( "/" ) ? new File( path ) : new File( segmentDir, path ); - if ( !res.exists() ) res = null; + if (line.startsWith("#")) continue; + if (line.startsWith(tag)) { + String path = line.substring(tag.length()).trim(); + res = path.startsWith("/") ? new File(path) : new File(segmentDir, path); + if (!res.exists()) res = null; break; } } - } - catch (Exception e) { /* ignore */ } - finally - { - if ( br != null ) - { - try - { + } catch (Exception e) { /* ignore */ } finally { + if (br != null) { + try { br.close(); - } - catch (Exception ee) { /* ignore */ } + } catch (Exception ee) { /* ignore */ } } } return res; diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/TurnRestriction.java b/brouter-mapaccess/src/main/java/btools/mapaccess/TurnRestriction.java index ea6b3d3..c92de8e 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/TurnRestriction.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/TurnRestriction.java @@ -5,8 +5,7 @@ */ package btools.mapaccess; -public final class TurnRestriction -{ +public final class TurnRestriction { public boolean isPositive; public short exceptions; @@ -17,57 +16,45 @@ public final class TurnRestriction public int toLat; public TurnRestriction next; - - public boolean exceptBikes() - { - return ( exceptions & 1 ) != 0; + + public boolean exceptBikes() { + return (exceptions & 1) != 0; } - public boolean exceptMotorcars() - { - return ( exceptions & 2 ) != 0; + public boolean exceptMotorcars() { + return (exceptions & 2) != 0; } - public static boolean isTurnForbidden( TurnRestriction first, int fromLon, int fromLat, int toLon, int toLat, boolean bikeMode, boolean carMode ) - { + public static boolean isTurnForbidden(TurnRestriction first, int fromLon, int fromLat, int toLon, int toLat, boolean bikeMode, boolean carMode) { boolean hasAnyPositive = false; boolean hasPositive = false; boolean hasNegative = false; TurnRestriction tr = first; - while (tr != null) - { - if ( ( tr.exceptBikes() && bikeMode ) || ( tr.exceptMotorcars() && carMode ) ) - { + while (tr != null) { + if ((tr.exceptBikes() && bikeMode) || (tr.exceptMotorcars() && carMode)) { tr = tr.next; continue; } - if ( tr.fromLon == fromLon && tr.fromLat == fromLat ) - { - if ( tr.isPositive ) - { + if (tr.fromLon == fromLon && tr.fromLat == fromLat) { + if (tr.isPositive) { hasAnyPositive = true; } - if ( tr.toLon == toLon && tr.toLat == toLat ) - { - if ( tr.isPositive ) - { + if (tr.toLon == toLon && tr.toLat == toLat) { + if (tr.isPositive) { hasPositive = true; - } - else - { + } else { hasNegative = true; } } } tr = tr.next; } - return !hasPositive && ( hasAnyPositive || hasNegative ); + return !hasPositive && (hasAnyPositive || hasNegative); } @Override - public String toString() - { + public String toString() { return "pos=" + isPositive + " fromLon=" + fromLon + " fromLat=" + fromLat + " toLon=" + toLon + " toLat=" + toLat; } - + } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java b/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java index b67938d..0d71d3d 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java @@ -10,12 +10,11 @@ import btools.util.CheapRuler; /** * the WaypointMatcher is feeded by the decoder with geoemtries of ways that are * already check for allowed access according to the current routing profile - * + *

* It matches these geometries against the list of waypoints to find the best * match for each waypoint */ -public final class WaypointMatcherImpl implements WaypointMatcher -{ +public final class WaypointMatcherImpl implements WaypointMatcher { private List waypoints; private OsmNodePairSet islandPairs; @@ -27,57 +26,50 @@ public final class WaypointMatcherImpl implements WaypointMatcher private int lonLast; private int latLast; - public WaypointMatcherImpl( List waypoints, double maxDistance, OsmNodePairSet islandPairs ) - { + public WaypointMatcherImpl(List waypoints, double maxDistance, OsmNodePairSet islandPairs) { this.waypoints = waypoints; this.islandPairs = islandPairs; - for ( MatchedWaypoint mwp : waypoints ) - { + for (MatchedWaypoint mwp : waypoints) { mwp.radius = maxDistance; } } - private void checkSegment( int lon1, int lat1, int lon2, int lat2 ) - { + private void checkSegment(int lon1, int lat1, int lon2, int lat2) { // todo: bounding-box pre-filter - double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 ); + 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 ); + double dx = (lon2 - lon1) * dlon2m; + double dy = (lat2 - lat1) * dlat2m; + double d = Math.sqrt(dy * dy + dx * dx); - if ( d == 0. ) + if (d == 0.) return; - for ( MatchedWaypoint mwp : waypoints ) - { + for (MatchedWaypoint mwp : waypoints) { OsmNode wp = mwp.waypoint; - double x1 = ( lon1 - wp.ilon ) * dlon2m; - double y1 = ( lat1 - wp.ilat ) * dlat2m; - double x2 = ( lon2 - wp.ilon ) * dlon2m; - double y2 = ( lat2 - wp.ilat ) * dlat2m; + double x1 = (lon1 - wp.ilon) * dlon2m; + double y1 = (lat1 - wp.ilat) * dlat2m; + double x2 = (lon2 - wp.ilon) * dlon2m; + double y2 = (lat2 - wp.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; + double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d; - if ( radius < mwp.radius ) - { + if (radius < mwp.radius) { double s1 = x1 * dx + y1 * dy; double s2 = x2 * dx + y2 * dy; - if ( s1 < 0. ) - { + if (s1 < 0.) { s1 = -s1; s2 = -s2; } - if ( s2 > 0. ) - { - radius = Math.sqrt( s1 < s2 ? r12 : r22 ); - if ( radius > mwp.radius ) + if (s2 > 0.) { + radius = Math.sqrt(s1 < s2 ? r12 : r22); + if (radius > mwp.radius) continue; } // new match for that waypoint @@ -85,23 +77,18 @@ public final class WaypointMatcherImpl implements WaypointMatcher mwp.hasUpdate = true; anyUpdate = true; // calculate crosspoint - if ( mwp.crosspoint == null ) + if (mwp.crosspoint == null) mwp.crosspoint = new OsmNode(); - if ( s2 < 0. ) - { - double wayfraction = -s2 / ( d * d ); + if (s2 < 0.) { + double wayfraction = -s2 / (d * d); double xm = x2 - wayfraction * dx; double ym = y2 - wayfraction * dy; - mwp.crosspoint.ilon = (int) ( xm / dlon2m + wp.ilon ); - mwp.crosspoint.ilat = (int) ( ym / dlat2m + wp.ilat ); - } - else if ( s1 > s2 ) - { + mwp.crosspoint.ilon = (int) (xm / dlon2m + wp.ilon); + mwp.crosspoint.ilat = (int) (ym / dlat2m + wp.ilat); + } else if (s1 > s2) { mwp.crosspoint.ilon = lon2; mwp.crosspoint.ilat = lat2; - } - else - { + } else { mwp.crosspoint.ilon = lon1; mwp.crosspoint.ilat = lat1; } @@ -110,14 +97,11 @@ public final class WaypointMatcherImpl implements WaypointMatcher } @Override - public boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget ) - { - if ( islandPairs.size() > 0 ) - { - long n1 = ( (long) ilonStart ) << 32 | ilatStart; - long n2 = ( (long) ilonTarget ) << 32 | ilatTarget; - if ( islandPairs.hasPair( n1, n2 ) ) - { + public boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget) { + if (islandPairs.size() > 0) { + long n1 = ((long) ilonStart) << 32 | ilatStart; + long n2 = ((long) ilonTarget) << 32 | ilatTarget; + if (islandPairs.hasPair(n1, n2)) { return false; } } @@ -130,26 +114,21 @@ public final class WaypointMatcherImpl implements WaypointMatcher } @Override - public void transferNode( int ilon, int ilat ) - { - checkSegment( lonLast, latLast, ilon, ilat ); + public void transferNode(int ilon, int ilat) { + checkSegment(lonLast, latLast, ilon, ilat); lonLast = ilon; latLast = ilat; } @Override - public void end() - { - checkSegment( lonLast, latLast, lonTarget, latTarget ); - if ( anyUpdate ) - { - for ( MatchedWaypoint mwp : waypoints ) - { - if ( mwp.hasUpdate ) - { + public void end() { + checkSegment(lonLast, latLast, lonTarget, latTarget); + if (anyUpdate) { + for (MatchedWaypoint mwp : waypoints) { + if (mwp.hasUpdate) { mwp.hasUpdate = false; - mwp.node1 = new OsmNode( lonStart, latStart ); - mwp.node2 = new OsmNode( lonTarget, latTarget ); + mwp.node1 = new OsmNode(lonStart, latStart); + mwp.node2 = new OsmNode(lonTarget, latTarget); } } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java index f4af71f..8694f72 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java @@ -216,13 +216,13 @@ public class BInstallerActivity extends AppCompatActivity { builder .setTitle("Confirm Delete") .setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - deleteSelectedTiles(); - } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - } - }); + public void onClick(DialogInterface dialog, int id) { + deleteSelectedTiles(); + } + }).setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + } + }); return builder.create(); default: diff --git a/brouter-server/src/main/java/btools/server/Area.java b/brouter-server/src/main/java/btools/server/Area.java index 5dd19a0..2c4fbd6 100644 --- a/brouter-server/src/main/java/btools/server/Area.java +++ b/brouter-server/src/main/java/btools/server/Area.java @@ -7,52 +7,39 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -public class Area -{ - private List poslist = new ArrayList(); - private List neglist = new ArrayList(); +public class Area { + private List poslist = new ArrayList(); + private List neglist = new ArrayList(); - public static void main( String[] args ) throws IOException - { - Area a = new Area( new File( args[0] ) ); - - System.out.println( args[1] + " is in " + args[0] + "=" + a.isInArea( Long.parseLong( args[1] ) ) ); + public static void main(String[] args) throws IOException { + Area a = new Area(new File(args[0])); + + System.out.println(args[1] + " is in " + args[0] + "=" + a.isInArea(Long.parseLong(args[1]))); } - - public Area( File f ) throws IOException - { - BufferedReader br = new BufferedReader( new FileReader( f ) ); + + public Area(File f) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(f)); br.readLine(); - - for(;;) - { + + for (; ; ) { String head = br.readLine(); - if ( head == null || "END".equals( head ) ) - { + if (head == null || "END".equals(head)) { break; } - Polygon pol = new Polygon( br ); - if ( head.startsWith( "!" ) ) - { - neglist.add( pol ); - } - else - { - poslist.add( pol ); + Polygon pol = new Polygon(br); + if (head.startsWith("!")) { + neglist.add(pol); + } else { + poslist.add(pol); } } } - - public boolean isInArea( long id ) - { - for( int i=0; i maxKm * 1000 ) - { - System.out.println( "airDistance " + (airDistance/1000) + "km exceeds limit for online router (" + maxKm + "km)" ); - return; - } - } - - long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout - String sMaxRunningTime = System.getProperty( "maxRunningTime" ); - if ( sMaxRunningTime != null ) - { - maxRunningTime = Integer.parseInt( sMaxRunningTime ) * 1000; - } - - List wplist = new ArrayList(); - wplist.add( from ); - wplist.add( to ); - - RoutingEngine re = new RoutingEngine( null, null, new File (args[0]), wplist, readRoutingContext(a2) ); - re.doRun( maxRunningTime ); - if ( re.getErrorMessage() != null ) - { - System.out.println( re.getErrorMessage() ); - } - } - catch( Throwable e ) - { - System.out.println( "unexpected exception: " + e ); - } - System.exit(0); - } - System.out.println("BRouter "+ OsmTrack.version + " / " + OsmTrack.versionDate); - if ( args.length < 6 ) - { - System.out.println("Find routes in an OSM map"); - System.out.println("usage: java -jar brouter.jar "); - return; - } - List wplist = new ArrayList(); - wplist.add( readPosition( args, 1, "from" ) ); - RoutingEngine re = null; - if ( "seed".equals( args[3] ) ) - { - int searchRadius = Integer.parseInt( args[4] ); // if = 0 search a 5x5 square - - String filename = SearchBoundary.getFileName( wplist.get(0) ); - DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( "traffic/" + filename ) ) ); - - for( int direction = 0; direction < 8; direction++ ) - { - RoutingContext rc = readRoutingContext(args); - SearchBoundary boundary = new SearchBoundary( wplist.get(0), searchRadius, direction/2 ); - rc.trafficOutputStream = dos; - rc.inverseDirection = (direction & 1 ) != 0; - re = new RoutingEngine( "mytrack", "mylog", new File ( args[0] ), wplist, rc ); - re.boundary = boundary; - re.airDistanceCostFactor = rc.trafficDirectionFactor; - rc.countTraffic = true; - re.doSearch(); - if ( re.getErrorMessage() != null ) - { - break; - } - } - dos.close(); - } - else - { - wplist.add( readPosition( args, 3, "to" ) ); - RoutingContext rc = readRoutingContext(args); - re = new RoutingEngine( "mytrack", "mylog", new File ( args[0] ), wplist, rc ); - re.doRun( 0 ); - - } - if ( re.getErrorMessage() != null ) - { - System.out.println( re.getErrorMessage() ); - } - } - - - private static OsmNodeNamed readPosition( String[] args, int idx, String name ) - { - OsmNodeNamed n = new OsmNodeNamed(); - n.name = name; - n.ilon = (int)( ( Double.parseDouble( args[idx ] ) + 180. ) *1000000. + 0.5); - n.ilat = (int)( ( Double.parseDouble( args[idx+1] ) + 90. ) *1000000. + 0.5); - return n; - } - - private static RoutingContext readRoutingContext( String[] args ) - { - RoutingContext c = new RoutingContext(); - if ( args.length > 5 ) - { - c.localFunction = args[5]; - if ( args.length > 6 ) - { - c.setAlternativeIdx( Integer.parseInt( args[6] ) ); - } - } - c.memoryclass = (int) ( Runtime.getRuntime().maxMemory() / 1024 / 1024 ); - // c.startDirection= Integer.valueOf( 150 ); - return c; - } -} +package btools.server; + +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.FileOutputStream; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.io.File; + +import btools.router.OsmNodeNamed; +import btools.router.OsmTrack; +import btools.router.RoutingContext; +import btools.router.RoutingEngine; +import btools.router.SearchBoundary; + +public class BRouter { + public static void main(String[] args) throws Exception { + if (args.length == 2) // cgi-input-mode + { + try { + String queryString = args[1]; + int sepIdx = queryString.indexOf('='); + if (sepIdx >= 0) queryString = queryString.substring(sepIdx + 1); + queryString = URLDecoder.decode(queryString, "ISO-8859-1"); + int ntokens = 1; + for (int ic = 0; ic < queryString.length(); ic++) { + if (queryString.charAt(ic) == '_') ntokens++; + } + String[] a2 = new String[ntokens + 1]; + int idx = 1; + int pos = 0; + for (; ; ) { + int p = queryString.indexOf('_', pos); + if (p < 0) { + a2[idx++] = queryString.substring(pos); + break; + } + a2[idx++] = queryString.substring(pos, p); + pos = p + 1; + } + + // cgi-header + System.out.println("Content-type: text/plain"); + System.out.println(); + OsmNodeNamed from = readPosition(a2, 1, "from"); + OsmNodeNamed to = readPosition(a2, 3, "to"); + + + int airDistance = from.calcDistance(to); + + String airDistanceLimit = System.getProperty("airDistanceLimit"); + if (airDistanceLimit != null) { + int maxKm = Integer.parseInt(airDistanceLimit); + if (airDistance > maxKm * 1000) { + System.out.println("airDistance " + (airDistance / 1000) + "km exceeds limit for online router (" + maxKm + "km)"); + return; + } + } + + long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout + String sMaxRunningTime = System.getProperty("maxRunningTime"); + if (sMaxRunningTime != null) { + maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000; + } + + List wplist = new ArrayList(); + wplist.add(from); + wplist.add(to); + + RoutingEngine re = new RoutingEngine(null, null, new File(args[0]), wplist, readRoutingContext(a2)); + re.doRun(maxRunningTime); + if (re.getErrorMessage() != null) { + System.out.println(re.getErrorMessage()); + } + } catch (Throwable e) { + System.out.println("unexpected exception: " + e); + } + System.exit(0); + } + System.out.println("BRouter " + OsmTrack.version + " / " + OsmTrack.versionDate); + if (args.length < 6) { + System.out.println("Find routes in an OSM map"); + System.out.println("usage: java -jar brouter.jar "); + return; + } + List wplist = new ArrayList(); + wplist.add(readPosition(args, 1, "from")); + RoutingEngine re = null; + if ("seed".equals(args[3])) { + int searchRadius = Integer.parseInt(args[4]); // if = 0 search a 5x5 square + + String filename = SearchBoundary.getFileName(wplist.get(0)); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("traffic/" + filename))); + + for (int direction = 0; direction < 8; direction++) { + RoutingContext rc = readRoutingContext(args); + SearchBoundary boundary = new SearchBoundary(wplist.get(0), searchRadius, direction / 2); + rc.trafficOutputStream = dos; + rc.inverseDirection = (direction & 1) != 0; + re = new RoutingEngine("mytrack", "mylog", new File(args[0]), wplist, rc); + re.boundary = boundary; + re.airDistanceCostFactor = rc.trafficDirectionFactor; + rc.countTraffic = true; + re.doSearch(); + if (re.getErrorMessage() != null) { + break; + } + } + dos.close(); + } else { + wplist.add(readPosition(args, 3, "to")); + RoutingContext rc = readRoutingContext(args); + re = new RoutingEngine("mytrack", "mylog", new File(args[0]), wplist, rc); + re.doRun(0); + + } + if (re.getErrorMessage() != null) { + System.out.println(re.getErrorMessage()); + } + } + + + private static OsmNodeNamed readPosition(String[] args, int idx, String name) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = name; + n.ilon = (int) ((Double.parseDouble(args[idx]) + 180.) * 1000000. + 0.5); + n.ilat = (int) ((Double.parseDouble(args[idx + 1]) + 90.) * 1000000. + 0.5); + return n; + } + + private static RoutingContext readRoutingContext(String[] args) { + RoutingContext c = new RoutingContext(); + if (args.length > 5) { + c.localFunction = args[5]; + if (args.length > 6) { + c.setAlternativeIdx(Integer.parseInt(args[6])); + } + } + c.memoryclass = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024); + // c.startDirection= Integer.valueOf( 150 ); + return c; + } +} diff --git a/brouter-server/src/main/java/btools/server/CgiUpload.java b/brouter-server/src/main/java/btools/server/CgiUpload.java index d71c77f..5ceb04d 100644 --- a/brouter-server/src/main/java/btools/server/CgiUpload.java +++ b/brouter-server/src/main/java/btools/server/CgiUpload.java @@ -1,111 +1,93 @@ -package btools.server; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.URLDecoder; - -public class CgiUpload -{ - public static void main(String[] args) - { - try - { - _main(args); - } - catch( Exception e ) - { - System.out.println( "unexpected exception: " + e ); - } - } - - private static void _main(String[] args) throws Exception - { - String htmlTemplate = args[0]; - String customeProfileDir = args[1]; - - String id = "" + System.currentTimeMillis(); - - - // cgi-header - System.out.println( "Content-type: text/html" ); - System.out.println(); - - // write the post message to a file - BufferedWriter bw = new BufferedWriter( - new OutputStreamWriter( - new FileOutputStream( customeProfileDir + "/" + id + ".brf" ) ) ); - BufferedReader ir = new BufferedReader( new InputStreamReader( System.in ) ); - String postData = ir.readLine(); - String[] coordValues = new String[4]; - if( postData != null ) - { - int coordsIdx = postData.indexOf( "coords=" ); - if ( coordsIdx >= 0) - { - int coordsEnd = postData.indexOf( '&' ); - if ( coordsEnd >= 0) - { - String coordsString = postData.substring( coordsIdx + 7, coordsEnd ); - postData = postData.substring( coordsEnd+1 ); - int pos = 0; - for(int idx=0; idx<4; idx++) - { - int p = coordsString.indexOf( '_', pos ); - coordValues[idx] = coordsString.substring( pos, p ); - pos = p+1; - } - } - } - int sepIdx = postData.indexOf( '=' ); - if ( sepIdx >= 0 ) postData = postData.substring( sepIdx + 1 ); - postData = URLDecoder.decode( postData, "ISO-8859-1" ); - bw.write( postData ); - } - bw.close(); - - // echo the template with a custom select item - BufferedReader br = new BufferedReader( - new InputStreamReader( - new FileInputStream( htmlTemplate ) ) ); - - for(;;) - { - String line = br.readLine(); - if ( line == null ) break; - if ( line.indexOf( "" ) >= 0 ) - { - line = " "; - } - else if ( line.indexOf( "paste your profile here" ) >= 0 ) - { - System.out.println( ""; - } - else - { - line = replaceCoord( line, "lonfrom", coordValues[0] ); - line = replaceCoord( line, "latfrom", coordValues[1] ); - line = replaceCoord( line, "lonto", coordValues[2] ); - line = replaceCoord( line, "latto", coordValues[3] ); - } - - System.out.println( line ); - } - br.close(); - } - - private static String replaceCoord( String line, String name, String value ) - { - String inputTag = "= 0 ) - { - return inputTag + " value=\"" + value + "\">"; - } - return line; - } -} +package btools.server; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URLDecoder; + +public class CgiUpload { + public static void main(String[] args) { + try { + _main(args); + } catch (Exception e) { + System.out.println("unexpected exception: " + e); + } + } + + private static void _main(String[] args) throws Exception { + String htmlTemplate = args[0]; + String customeProfileDir = args[1]; + + String id = "" + System.currentTimeMillis(); + + + // cgi-header + System.out.println("Content-type: text/html"); + System.out.println(); + + // write the post message to a file + BufferedWriter bw = new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(customeProfileDir + "/" + id + ".brf"))); + BufferedReader ir = new BufferedReader(new InputStreamReader(System.in)); + String postData = ir.readLine(); + String[] coordValues = new String[4]; + if (postData != null) { + int coordsIdx = postData.indexOf("coords="); + if (coordsIdx >= 0) { + int coordsEnd = postData.indexOf('&'); + if (coordsEnd >= 0) { + String coordsString = postData.substring(coordsIdx + 7, coordsEnd); + postData = postData.substring(coordsEnd + 1); + int pos = 0; + for (int idx = 0; idx < 4; idx++) { + int p = coordsString.indexOf('_', pos); + coordValues[idx] = coordsString.substring(pos, p); + pos = p + 1; + } + } + } + int sepIdx = postData.indexOf('='); + if (sepIdx >= 0) postData = postData.substring(sepIdx + 1); + postData = URLDecoder.decode(postData, "ISO-8859-1"); + bw.write(postData); + } + bw.close(); + + // echo the template with a custom select item + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(htmlTemplate))); + + for (; ; ) { + String line = br.readLine(); + if (line == null) break; + if (line.indexOf("") >= 0) { + line = " "; + } else if (line.indexOf("paste your profile here") >= 0) { + System.out.println(""; + } else { + line = replaceCoord(line, "lonfrom", coordValues[0]); + line = replaceCoord(line, "latfrom", coordValues[1]); + line = replaceCoord(line, "lonto", coordValues[2]); + line = replaceCoord(line, "latto", coordValues[3]); + } + + System.out.println(line); + } + br.close(); + } + + private static String replaceCoord(String line, String name, String value) { + String inputTag = "= 0) { + return inputTag + " value=\"" + value + "\">"; + } + return line; + } +} diff --git a/brouter-server/src/main/java/btools/server/IpAccessMonitor.java b/brouter-server/src/main/java/btools/server/IpAccessMonitor.java index 3f3da83..f2b1515 100644 --- a/brouter-server/src/main/java/btools/server/IpAccessMonitor.java +++ b/brouter-server/src/main/java/btools/server/IpAccessMonitor.java @@ -3,50 +3,41 @@ package btools.server; import java.util.HashMap; import java.util.Map; -public class IpAccessMonitor -{ +public class IpAccessMonitor { private static Object sync = new Object(); - private static HashMap ipAccess = new HashMap(); + private static HashMap ipAccess = new HashMap(); private static long MAX_IDLE = 900000; // 15 minutes private static long CLEANUP_INTERVAL = 10000; // 10 seconds private static long lastCleanup; - public static boolean touchIpAccess( String ip ) - { + public static boolean touchIpAccess(String ip) { long t = System.currentTimeMillis(); - synchronized( sync ) - { - Long lastTime = ipAccess.get( ip ); - ipAccess.put( ip, Long.valueOf( t ) ); + synchronized (sync) { + Long lastTime = ipAccess.get(ip); + ipAccess.put(ip, Long.valueOf(t)); return lastTime == null || t - lastTime.longValue() > MAX_IDLE; } } - public static int getSessionCount() - { + public static int getSessionCount() { long t = System.currentTimeMillis(); - synchronized( sync ) - { - if ( t - lastCleanup > CLEANUP_INTERVAL ) - { - cleanup( t ); + synchronized (sync) { + if (t - lastCleanup > CLEANUP_INTERVAL) { + cleanup(t); lastCleanup = t; } return ipAccess.size(); } } - - private static void cleanup( long t ) - { - HashMap newMap = new HashMap(ipAccess.size()); - for( Map.Entry e : ipAccess.entrySet() ) - { - if ( t - e.getValue().longValue() <= MAX_IDLE ) - { - newMap.put( e.getKey(), e.getValue() ); + + private static void cleanup(long t) { + HashMap newMap = new HashMap(ipAccess.size()); + for (Map.Entry e : ipAccess.entrySet()) { + if (t - e.getValue().longValue() <= MAX_IDLE) { + newMap.put(e.getKey(), e.getValue()); } } ipAccess = newMap; } - + } diff --git a/brouter-server/src/main/java/btools/server/NearRecentWps.java b/brouter-server/src/main/java/btools/server/NearRecentWps.java index ab0f9f6..ae6ab03 100644 --- a/brouter-server/src/main/java/btools/server/NearRecentWps.java +++ b/brouter-server/src/main/java/btools/server/NearRecentWps.java @@ -5,44 +5,33 @@ import java.util.List; import btools.mapaccess.OsmNode; import btools.router.OsmNodeNamed; -public class NearRecentWps -{ +public class NearRecentWps { private OsmNodeNamed[] recentWaypoints = new OsmNodeNamed[2000]; private int nextRecentIndex = 0; - public void add( List wplist ) - { - synchronized( recentWaypoints ) - { - for( OsmNodeNamed wp : wplist ) - { - add( wp ); + public void add(List wplist) { + synchronized (recentWaypoints) { + for (OsmNodeNamed wp : wplist) { + add(wp); } } } - public void add( OsmNodeNamed wp ) - { + public void add(OsmNodeNamed wp) { recentWaypoints[nextRecentIndex++] = wp; - if ( nextRecentIndex >= recentWaypoints.length ) - { + if (nextRecentIndex >= recentWaypoints.length) { nextRecentIndex = 0; } } - public int count( long id ) - { + public int count(long id) { int cnt = 0; - OsmNode n = new OsmNode( id ); - synchronized( recentWaypoints ) - { - for ( int i=0; i lines = new ArrayList(); - - for(;;) - { + + for (; ; ) { String line = br.readLine(); - if ( line == null || "END".equals( line ) ) - { + if (line == null || "END".equals(line)) { break; } - lines.add( line ); + lines.add(line); } int n = lines.size(); ax = new int[n]; ay = new int[n]; - for( int i=0; i maxx ) maxx = x; - if ( y > maxy ) maxy = y; + StringTokenizer tk = new StringTokenizer(line); + double lon = Double.parseDouble(tk.nextToken()); + double lat = Double.parseDouble(tk.nextToken()); + + int x = ax[i] = (int) (lon * 1000000. + 180000000); + int y = ay[i] = (int) (lat * 1000000. + 90000000); + + if (x < minx) minx = x; + if (y < miny) miny = y; + if (x > maxx) maxx = x; + if (y > maxy) maxy = y; } } - - public boolean isInPolygon( long id ) - { - int x = (int) ( id >> 32 ); - int y = (int) ( id & 0xffffffff ); - if ( x < minx || x > maxx || y < miny || y > maxy ) - { + public boolean isInPolygon(long id) { + int x = (int) (id >> 32); + int y = (int) (id & 0xffffffff); + + if (x < minx || x > maxx || y < miny || y > maxy) { return false; } - int n = ax.length-1; // these are closed polygons + int n = ax.length - 1; // these are closed polygons boolean inside = false; int j = n - 1; - for (int i = 0 ;i < n ; j = i++) - { - if ( (ay[i] > y) != (ay[j] > y) ) - { + for (int i = 0; i < n; j = i++) { + if ((ay[i] > y) != (ay[j] > y)) { long v = ax[j] - ax[i]; v *= y - ay[i]; v /= ay[j] - ay[i]; - if ( x <= v + ax[i]) - { + if (x <= v + ax[i]) { inside = !inside; } } @@ -78,10 +68,9 @@ public class Polygon return inside; } - public boolean isInBoundingBox( long id ) - { - int x = (int) ( id >> 32 ); - int y = (int) ( id & 0xffffffff ); + public boolean isInBoundingBox(long id) { + int x = (int) (id >> 32); + int y = (int) (id & 0xffffffff); return x >= minx && x <= maxx && y >= miny && y <= maxy; } diff --git a/brouter-server/src/main/java/btools/server/RouteServer.java b/brouter-server/src/main/java/btools/server/RouteServer.java index 238db21..62c74e5 100644 --- a/brouter-server/src/main/java/btools/server/RouteServer.java +++ b/brouter-server/src/main/java/btools/server/RouteServer.java @@ -1,515 +1,444 @@ -package btools.server; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URLDecoder; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.StringTokenizer; -import java.util.zip.GZIPOutputStream; - -import btools.router.OsmNodeNamed; -import btools.router.OsmTrack; -import btools.router.ProfileCache; -import btools.router.RoutingContext; -import btools.router.RoutingEngine; -import btools.server.request.ProfileUploadHandler; -import btools.server.request.RequestHandler; -import btools.server.request.ServerHandler; -import btools.util.StackSampler; - -public class RouteServer extends Thread implements Comparable -{ - public static final String PROFILE_UPLOAD_URL = "/brouter/profile"; - static final String HTTP_STATUS_OK = "200 OK"; - static final String HTTP_STATUS_BAD_REQUEST = "400 Bad Request"; - static final String HTTP_STATUS_FORBIDDEN = "403 Forbidden"; - static final String HTTP_STATUS_NOT_FOUND = "404 Not Found"; - static final String HTTP_STATUS_INTERNAL_SERVER_ERROR = "500 Internal Server Error"; - - public ServiceContext serviceContext; - - private Socket clientSocket = null; - private RoutingEngine cr = null; - private volatile boolean terminated; - private long starttime; - - private static Object threadPoolSync = new Object(); - private static boolean debug = Boolean.getBoolean( "debugThreadPool" ); - - public void stopRouter() - { - RoutingEngine e = cr; - if ( e != null ) e.terminate(); - } - - private static DateFormat tsFormat = new SimpleDateFormat( "dd.MM.yy HH:mm", new Locale( "en", "US" ) ); - - private static String formattedTimeStamp( long t ) - { - synchronized( tsFormat ) - { - return tsFormat.format( new Date( System.currentTimeMillis() ) ); - } - } - - public void run() - { - BufferedReader br = null; - BufferedWriter bw = null; - - // first line - String getline = null; - String sessionInfo = null; - String sIp = null; - - try - { - br = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() , "UTF-8") ); - bw = new BufferedWriter( new OutputStreamWriter( clientSocket.getOutputStream(), "UTF-8" ) ); - - String agent = null; - String encodings = null; - String xff = null; // X-Forwarded-For - String referer = null; - - // more headers until first empty line - for(;;) - { - // headers - String line = br.readLine(); - if ( line == null ) - { - writeHttpHeader(bw, HTTP_STATUS_BAD_REQUEST); - bw.flush(); - return; - } - if ( line.length() == 0 ) - { - break; - } - if ( getline == null ) - { - getline = line; - } - line = line.toLowerCase(); - if ( line.startsWith( "user-agent: " ) ) - { - agent = line.substring( "user-agent: ".length() ); - } - if ( line.startsWith( "accept-encoding: " ) ) - { - encodings = line.substring( "accept-encoding: ".length() ); - } - if ( line.startsWith( "x-forwarded-for: " ) ) - { - xff = line.substring( "x-forwarded-for: ".length() ); - } - if ( line.startsWith( "Referer: " ) ) - { - referer = line.substring( "Referer: ".length() ); - } - if ( line.startsWith( "Referrer: " ) ) - { - referer = line.substring( "Referrer: ".length() ); - } - } - - InetAddress ip = clientSocket.getInetAddress(); - sIp = xff == null ? (ip==null ? "null" : ip.toString() ) : xff; - boolean newSession = IpAccessMonitor.touchIpAccess( sIp ); - sessionInfo = " new"; - if ( !newSession ) - { - int sessionCount = IpAccessMonitor.getSessionCount(); - sessionInfo = " " + Math.min( sessionCount, 999 ); - sessionInfo = sessionInfo.substring( sessionInfo.length() - 4 ); - } - - String excludedAgents = System.getProperty( "excludedAgents" ); - if ( agent != null && excludedAgents != null ) - { - StringTokenizer tk = new StringTokenizer( excludedAgents, "," ); - while( tk.hasMoreTokens() ) - { - if ( agent.indexOf( tk.nextToken() ) >= 0 ) - { - writeHttpHeader( bw, HTTP_STATUS_FORBIDDEN ); - bw.write( "Bad agent: " + agent ); - bw.flush(); - return; - } - } - } - - if ( referer != null && referer.indexOf( "brouter.de/brouter-web" ) >= 0 ) - { - if ( getline.indexOf( "%7C" ) >= 0 && getline.indexOf( "%2C" ) >= 0 ) - { - writeHttpHeader( bw, HTTP_STATUS_FORBIDDEN ); - bw.write( "Spam? please stop" ); - bw.flush(); - return; - } - } - - if ( getline.startsWith("GET /favicon.ico") ) - { - writeHttpHeader( bw, HTTP_STATUS_NOT_FOUND ); - bw.flush(); - return; - } - if ( getline.startsWith("GET /robots.txt") ) - { - writeHttpHeader( bw, HTTP_STATUS_OK ); - bw.write( "User-agent: *\n" ); - bw.write( "Disallow: /\n" ); - bw.flush(); - return; - } - - String url = getline.split(" ")[1]; - HashMap params = getUrlParams(url); - - long maxRunningTime = getMaxRunningTime(); - - RequestHandler handler; - if ( params.containsKey( "lonlats" ) && params.containsKey( "profile" ) ) - { - handler = new ServerHandler( serviceContext, params ); - } - else if ( url.startsWith( PROFILE_UPLOAD_URL ) ) - { - if ( getline.startsWith("OPTIONS") ) - { - // handle CORS preflight request (Safari) - String corsHeaders = "Access-Control-Allow-Methods: GET, POST\n" - + "Access-Control-Allow-Headers: Content-Type\n"; - writeHttpHeader( bw, "text/plain", null, corsHeaders, HTTP_STATUS_OK ); - bw.flush(); - return; - } - else - { - writeHttpHeader(bw, "application/json", HTTP_STATUS_OK); - - String profileId = null; - if ( url.length() > PROFILE_UPLOAD_URL.length() + 1 ) - { - // e.g. /brouter/profile/custom_1400767688382 - profileId = url.substring(PROFILE_UPLOAD_URL.length() + 1); - } - - ProfileUploadHandler uploadHandler = new ProfileUploadHandler( serviceContext ); - uploadHandler.handlePostRequest( profileId, br, bw ); - - bw.flush(); - return; - } - } - else if ( url.startsWith( "/brouter/suspects" ) ) - { - writeHttpHeader(bw, url.endsWith( ".json" ) ? "application/json" : "text/html", HTTP_STATUS_OK); - SuspectManager.process( url, bw ); - return; - } - else - { - writeHttpHeader( bw, HTTP_STATUS_NOT_FOUND ); - bw.flush(); - return; - } - RoutingContext rc = handler.readRoutingContext(); - List wplist = handler.readWayPointList(); - - if ( wplist.size() < 10 ) - { - SuspectManager.nearRecentWps.add( wplist ); - } - for( Map.Entry e : params.entrySet() ) - { - if ( "timode".equals( e.getKey() ) ) - { - rc.turnInstructionMode = Integer.parseInt( e.getValue() ); - } - else if ( "heading".equals( e.getKey() ) ) - { - rc.startDirection = Integer.valueOf( Integer.parseInt( e.getValue() ) ); - rc.forceUseStartDirection = true; - } - else if ( e.getKey().startsWith( "profile:" ) ) - { - if ( rc.keyValues == null ) - { - rc.keyValues = new HashMap(); - } - rc.keyValues.put( e.getKey().substring( 8 ), e.getValue() ); - } - } - cr = new RoutingEngine( null, null, serviceContext.segmentDir, wplist, rc ); - cr.quite = true; - cr.doRun( maxRunningTime ); - - if ( cr.getErrorMessage() != null ) - { - writeHttpHeader(bw, HTTP_STATUS_BAD_REQUEST); - bw.write( cr.getErrorMessage() ); - bw.write( "\n" ); - } - else - { - OsmTrack track = cr.getFoundTrack(); - - String headers = encodings == null || encodings.indexOf( "gzip" ) < 0 ? null : "Content-Encoding: gzip\n"; - writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK ); - if ( track != null ) - { - if ( headers != null ) // compressed - { - java.io.ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Writer w = new OutputStreamWriter( new GZIPOutputStream( baos ), "UTF-8" ); - w.write( handler.formatTrack(track) ); - w.close(); - bw.flush(); - clientSocket.getOutputStream().write( baos.toByteArray() ); - } - else - { - bw.write( handler.formatTrack(track) ); - } - } - } - bw.flush(); - } - catch (Throwable e) - { - try { - writeHttpHeader(bw, HTTP_STATUS_INTERNAL_SERVER_ERROR); - bw.flush(); - } - catch (IOException _ignore){} - System.out.println("RouteServer got exception (will continue): "+e); - e.printStackTrace(); - } - finally - { - cr = null; - if ( br != null ) try { br.close(); } catch( Exception e ) {} - if ( bw != null ) try { bw.close(); } catch( Exception e ) {} - if ( clientSocket != null ) try { clientSocket.close(); } catch( Exception e ) {} - terminated = true; - synchronized( threadPoolSync ) - { - threadPoolSync.notifyAll(); - } - long t = System.currentTimeMillis(); - long ms = t - starttime; - System.out.println( formattedTimeStamp(t) + sessionInfo + " ip=" + sIp + " ms=" + ms + " -> " + getline ); - } - } - - - public static void main(String[] args) throws Exception - { - System.out.println("BRouter " + OsmTrack.version + " / " + OsmTrack.versionDate); - if ( args.length != 5 && args.length != 6) - { - System.out.println("serve BRouter protocol"); - System.out.println("usage: java RouteServer [bindaddress]"); - return; - } - - ServiceContext serviceContext = new ServiceContext(); - serviceContext.segmentDir = new File ( args[0] ); - serviceContext.profileDir = args[1]; - System.setProperty( "profileBaseDir", serviceContext.profileDir ); - String dirs = args[2]; - StringTokenizer tk = new StringTokenizer( dirs, "," ); - serviceContext.customProfileDir = tk.nextToken(); - serviceContext.sharedProfileDir = tk.hasMoreTokens() ? tk.nextToken() : serviceContext.customProfileDir; - - int maxthreads = Integer.parseInt( args[4] ); - - ProfileCache.setSize( 2*maxthreads ); - - PriorityQueue threadQueue = new PriorityQueue(); - - ServerSocket serverSocket = args.length > 5 ? new ServerSocket(Integer.parseInt(args[3]),100,InetAddress.getByName(args[5])) : new ServerSocket(Integer.parseInt(args[3])); - - // stacksample for performance profiling - // ( caution: start stacksampler only after successfully creating the server socket - // because that thread prevents the process from terminating, so the start-attempt - // by the watchdog cron would create zombies ) - File stackLog = new File( "stacks.txt" ); - if ( stackLog.exists() ) - { - StackSampler stackSampler = new StackSampler( stackLog, 1000 ); - stackSampler.start(); - System.out.println( "*** sampling stacks into stacks.txt *** "); - } - - for (;;) - { - Socket clientSocket = serverSocket.accept(); - RouteServer server = new RouteServer(); - server.serviceContext = serviceContext; - server.clientSocket = clientSocket; - server.starttime = System.currentTimeMillis(); - - // kill an old thread if thread limit reached - - cleanupThreadQueue( threadQueue ); - - if ( debug ) System.out.println( "threadQueue.size()=" + threadQueue.size() ); - if ( threadQueue.size() >= maxthreads ) - { - synchronized( threadPoolSync ) - { - // wait up to 2000ms (maybe notified earlier) - // to prevent killing short-running threads - long maxage = server.starttime - threadQueue.peek().starttime; - long maxWaitTime = 2000L-maxage; - if ( debug ) System.out.println( "maxage=" + maxage + " maxWaitTime=" + maxWaitTime ); - if ( debug ) - { - for ( RouteServer t : threadQueue ) - { - System.out.println( "age=" + (server.starttime - t.starttime) ); - } - } - if ( maxWaitTime > 0 ) - { - threadPoolSync.wait( maxWaitTime ); - } - long t = System.currentTimeMillis(); - System.out.println( formattedTimeStamp(t) + " contention! ms waited " + (t - server.starttime) ); - } - cleanupThreadQueue( threadQueue ); - if ( threadQueue.size() >= maxthreads ) - { - if ( debug ) System.out.println( "stopping oldest thread..." ); - // no way... stop the oldest thread - RouteServer oldest = threadQueue.poll(); - oldest.stopRouter(); - long t = System.currentTimeMillis(); - System.out.println( formattedTimeStamp(t) + " contention! ms killed " + (t - oldest.starttime) ); - } - } - - threadQueue.add( server ); - - server.start(); - if ( debug ) System.out.println( "thread started..." ); - } - } - - - private static HashMap getUrlParams( String url ) throws UnsupportedEncodingException - { - HashMap params = new HashMap(); - String decoded = URLDecoder.decode( url, "UTF-8" ); - StringTokenizer tk = new StringTokenizer( decoded, "?&" ); - while( tk.hasMoreTokens() ) - { - String t = tk.nextToken(); - StringTokenizer tk2 = new StringTokenizer( t, "=" ); - if ( tk2.hasMoreTokens() ) - { - String key = tk2.nextToken(); - if ( tk2.hasMoreTokens() ) - { - String value = tk2.nextToken(); - params.put( key, value ); - } - } - } - return params; - } - - private static long getMaxRunningTime() { - long maxRunningTime = 60000; - String sMaxRunningTime = System.getProperty( "maxRunningTime" ); - if ( sMaxRunningTime != null ) - { - maxRunningTime = Integer.parseInt( sMaxRunningTime ) * 1000; - } - return maxRunningTime; - } - - private static void writeHttpHeader( BufferedWriter bw, String status ) throws IOException - { - writeHttpHeader( bw, "text/plain", status ); - } - - private static void writeHttpHeader( BufferedWriter bw, String mimeType, String status ) throws IOException - { - writeHttpHeader( bw, mimeType, null, status ); - } - - private static void writeHttpHeader( BufferedWriter bw, String mimeType, String fileName, String status ) throws IOException - { - writeHttpHeader( bw, mimeType, fileName, null, status); - } - - private static void writeHttpHeader( BufferedWriter bw, String mimeType, String fileName, String headers, String status ) throws IOException - { - // http-header - bw.write( String.format("HTTP/1.1 %s\n", status) ); - bw.write( "Connection: close\n" ); - bw.write( "Content-Type: " + mimeType + "; charset=utf-8\n" ); - if ( fileName != null ) - { - bw.write( "Content-Disposition: attachment; filename=\"" + fileName + "\"\n" ); - } - bw.write( "Access-Control-Allow-Origin: *\n" ); - if ( headers != null ) - { - bw.write( headers ); - } - bw.write( "\n" ); - } - - private static void cleanupThreadQueue( PriorityQueue threadQueue ) - { - for ( ;; ) - { - boolean removedItem = false; - for ( RouteServer t : threadQueue ) - { - if ( t.terminated ) - { - threadQueue.remove( t ); - removedItem = true; - break; - } - } - if ( !removedItem ) - { - break; - } - } - } - - @Override - public int compareTo( RouteServer t ) - { - return starttime < t.starttime ? -1 : ( starttime > t.starttime ? 1 : 0 ); - } - -} +package btools.server; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URLDecoder; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.StringTokenizer; +import java.util.zip.GZIPOutputStream; + +import btools.router.OsmNodeNamed; +import btools.router.OsmTrack; +import btools.router.ProfileCache; +import btools.router.RoutingContext; +import btools.router.RoutingEngine; +import btools.server.request.ProfileUploadHandler; +import btools.server.request.RequestHandler; +import btools.server.request.ServerHandler; +import btools.util.StackSampler; + +public class RouteServer extends Thread implements Comparable { + public static final String PROFILE_UPLOAD_URL = "/brouter/profile"; + static final String HTTP_STATUS_OK = "200 OK"; + static final String HTTP_STATUS_BAD_REQUEST = "400 Bad Request"; + static final String HTTP_STATUS_FORBIDDEN = "403 Forbidden"; + static final String HTTP_STATUS_NOT_FOUND = "404 Not Found"; + static final String HTTP_STATUS_INTERNAL_SERVER_ERROR = "500 Internal Server Error"; + + public ServiceContext serviceContext; + + private Socket clientSocket = null; + private RoutingEngine cr = null; + private volatile boolean terminated; + private long starttime; + + private static Object threadPoolSync = new Object(); + private static boolean debug = Boolean.getBoolean("debugThreadPool"); + + public void stopRouter() { + RoutingEngine e = cr; + if (e != null) e.terminate(); + } + + private static DateFormat tsFormat = new SimpleDateFormat("dd.MM.yy HH:mm", new Locale("en", "US")); + + private static String formattedTimeStamp(long t) { + synchronized (tsFormat) { + return tsFormat.format(new Date(System.currentTimeMillis())); + } + } + + public void run() { + BufferedReader br = null; + BufferedWriter bw = null; + + // first line + String getline = null; + String sessionInfo = null; + String sIp = null; + + try { + br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8")); + bw = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); + + String agent = null; + String encodings = null; + String xff = null; // X-Forwarded-For + String referer = null; + + // more headers until first empty line + for (; ; ) { + // headers + String line = br.readLine(); + if (line == null) { + writeHttpHeader(bw, HTTP_STATUS_BAD_REQUEST); + bw.flush(); + return; + } + if (line.length() == 0) { + break; + } + if (getline == null) { + getline = line; + } + line = line.toLowerCase(); + if (line.startsWith("user-agent: ")) { + agent = line.substring("user-agent: ".length()); + } + if (line.startsWith("accept-encoding: ")) { + encodings = line.substring("accept-encoding: ".length()); + } + if (line.startsWith("x-forwarded-for: ")) { + xff = line.substring("x-forwarded-for: ".length()); + } + if (line.startsWith("Referer: ")) { + referer = line.substring("Referer: ".length()); + } + if (line.startsWith("Referrer: ")) { + referer = line.substring("Referrer: ".length()); + } + } + + InetAddress ip = clientSocket.getInetAddress(); + sIp = xff == null ? (ip == null ? "null" : ip.toString()) : xff; + boolean newSession = IpAccessMonitor.touchIpAccess(sIp); + sessionInfo = " new"; + if (!newSession) { + int sessionCount = IpAccessMonitor.getSessionCount(); + sessionInfo = " " + Math.min(sessionCount, 999); + sessionInfo = sessionInfo.substring(sessionInfo.length() - 4); + } + + String excludedAgents = System.getProperty("excludedAgents"); + if (agent != null && excludedAgents != null) { + StringTokenizer tk = new StringTokenizer(excludedAgents, ","); + while (tk.hasMoreTokens()) { + if (agent.indexOf(tk.nextToken()) >= 0) { + writeHttpHeader(bw, HTTP_STATUS_FORBIDDEN); + bw.write("Bad agent: " + agent); + bw.flush(); + return; + } + } + } + + if (referer != null && referer.indexOf("brouter.de/brouter-web") >= 0) { + if (getline.indexOf("%7C") >= 0 && getline.indexOf("%2C") >= 0) { + writeHttpHeader(bw, HTTP_STATUS_FORBIDDEN); + bw.write("Spam? please stop"); + bw.flush(); + return; + } + } + + if (getline.startsWith("GET /favicon.ico")) { + writeHttpHeader(bw, HTTP_STATUS_NOT_FOUND); + bw.flush(); + return; + } + if (getline.startsWith("GET /robots.txt")) { + writeHttpHeader(bw, HTTP_STATUS_OK); + bw.write("User-agent: *\n"); + bw.write("Disallow: /\n"); + bw.flush(); + return; + } + + String url = getline.split(" ")[1]; + HashMap params = getUrlParams(url); + + long maxRunningTime = getMaxRunningTime(); + + RequestHandler handler; + if (params.containsKey("lonlats") && params.containsKey("profile")) { + handler = new ServerHandler(serviceContext, params); + } else if (url.startsWith(PROFILE_UPLOAD_URL)) { + if (getline.startsWith("OPTIONS")) { + // handle CORS preflight request (Safari) + String corsHeaders = "Access-Control-Allow-Methods: GET, POST\n" + + "Access-Control-Allow-Headers: Content-Type\n"; + writeHttpHeader(bw, "text/plain", null, corsHeaders, HTTP_STATUS_OK); + bw.flush(); + return; + } else { + writeHttpHeader(bw, "application/json", HTTP_STATUS_OK); + + String profileId = null; + if (url.length() > PROFILE_UPLOAD_URL.length() + 1) { + // e.g. /brouter/profile/custom_1400767688382 + profileId = url.substring(PROFILE_UPLOAD_URL.length() + 1); + } + + ProfileUploadHandler uploadHandler = new ProfileUploadHandler(serviceContext); + uploadHandler.handlePostRequest(profileId, br, bw); + + bw.flush(); + return; + } + } else if (url.startsWith("/brouter/suspects")) { + writeHttpHeader(bw, url.endsWith(".json") ? "application/json" : "text/html", HTTP_STATUS_OK); + SuspectManager.process(url, bw); + return; + } else { + writeHttpHeader(bw, HTTP_STATUS_NOT_FOUND); + bw.flush(); + return; + } + RoutingContext rc = handler.readRoutingContext(); + List wplist = handler.readWayPointList(); + + if (wplist.size() < 10) { + SuspectManager.nearRecentWps.add(wplist); + } + for (Map.Entry e : params.entrySet()) { + if ("timode".equals(e.getKey())) { + rc.turnInstructionMode = Integer.parseInt(e.getValue()); + } else if ("heading".equals(e.getKey())) { + rc.startDirection = Integer.valueOf(Integer.parseInt(e.getValue())); + rc.forceUseStartDirection = true; + } else if (e.getKey().startsWith("profile:")) { + if (rc.keyValues == null) { + rc.keyValues = new HashMap(); + } + rc.keyValues.put(e.getKey().substring(8), e.getValue()); + } + } + cr = new RoutingEngine(null, null, serviceContext.segmentDir, wplist, rc); + cr.quite = true; + cr.doRun(maxRunningTime); + + if (cr.getErrorMessage() != null) { + writeHttpHeader(bw, HTTP_STATUS_BAD_REQUEST); + bw.write(cr.getErrorMessage()); + bw.write("\n"); + } else { + OsmTrack track = cr.getFoundTrack(); + + String headers = encodings == null || encodings.indexOf("gzip") < 0 ? null : "Content-Encoding: gzip\n"; + writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK); + if (track != null) { + if (headers != null) // compressed + { + java.io.ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); + w.write(handler.formatTrack(track)); + w.close(); + bw.flush(); + clientSocket.getOutputStream().write(baos.toByteArray()); + } else { + bw.write(handler.formatTrack(track)); + } + } + } + bw.flush(); + } catch (Throwable e) { + try { + writeHttpHeader(bw, HTTP_STATUS_INTERNAL_SERVER_ERROR); + bw.flush(); + } catch (IOException _ignore) { + } + System.out.println("RouteServer got exception (will continue): " + e); + e.printStackTrace(); + } finally { + cr = null; + if (br != null) try { + br.close(); + } catch (Exception e) { + } + if (bw != null) try { + bw.close(); + } catch (Exception e) { + } + if (clientSocket != null) try { + clientSocket.close(); + } catch (Exception e) { + } + terminated = true; + synchronized (threadPoolSync) { + threadPoolSync.notifyAll(); + } + long t = System.currentTimeMillis(); + long ms = t - starttime; + System.out.println(formattedTimeStamp(t) + sessionInfo + " ip=" + sIp + " ms=" + ms + " -> " + getline); + } + } + + + public static void main(String[] args) throws Exception { + System.out.println("BRouter " + OsmTrack.version + " / " + OsmTrack.versionDate); + if (args.length != 5 && args.length != 6) { + System.out.println("serve BRouter protocol"); + System.out.println("usage: java RouteServer [bindaddress]"); + return; + } + + ServiceContext serviceContext = new ServiceContext(); + serviceContext.segmentDir = new File(args[0]); + serviceContext.profileDir = args[1]; + System.setProperty("profileBaseDir", serviceContext.profileDir); + String dirs = args[2]; + StringTokenizer tk = new StringTokenizer(dirs, ","); + serviceContext.customProfileDir = tk.nextToken(); + serviceContext.sharedProfileDir = tk.hasMoreTokens() ? tk.nextToken() : serviceContext.customProfileDir; + + int maxthreads = Integer.parseInt(args[4]); + + ProfileCache.setSize(2 * maxthreads); + + PriorityQueue threadQueue = new PriorityQueue(); + + ServerSocket serverSocket = args.length > 5 ? new ServerSocket(Integer.parseInt(args[3]), 100, InetAddress.getByName(args[5])) : new ServerSocket(Integer.parseInt(args[3])); + + // stacksample for performance profiling + // ( caution: start stacksampler only after successfully creating the server socket + // because that thread prevents the process from terminating, so the start-attempt + // by the watchdog cron would create zombies ) + File stackLog = new File("stacks.txt"); + if (stackLog.exists()) { + StackSampler stackSampler = new StackSampler(stackLog, 1000); + stackSampler.start(); + System.out.println("*** sampling stacks into stacks.txt *** "); + } + + for (; ; ) { + Socket clientSocket = serverSocket.accept(); + RouteServer server = new RouteServer(); + server.serviceContext = serviceContext; + server.clientSocket = clientSocket; + server.starttime = System.currentTimeMillis(); + + // kill an old thread if thread limit reached + + cleanupThreadQueue(threadQueue); + + if (debug) System.out.println("threadQueue.size()=" + threadQueue.size()); + if (threadQueue.size() >= maxthreads) { + synchronized (threadPoolSync) { + // wait up to 2000ms (maybe notified earlier) + // to prevent killing short-running threads + long maxage = server.starttime - threadQueue.peek().starttime; + long maxWaitTime = 2000L - maxage; + if (debug) System.out.println("maxage=" + maxage + " maxWaitTime=" + maxWaitTime); + if (debug) { + for (RouteServer t : threadQueue) { + System.out.println("age=" + (server.starttime - t.starttime)); + } + } + if (maxWaitTime > 0) { + threadPoolSync.wait(maxWaitTime); + } + long t = System.currentTimeMillis(); + System.out.println(formattedTimeStamp(t) + " contention! ms waited " + (t - server.starttime)); + } + cleanupThreadQueue(threadQueue); + if (threadQueue.size() >= maxthreads) { + if (debug) System.out.println("stopping oldest thread..."); + // no way... stop the oldest thread + RouteServer oldest = threadQueue.poll(); + oldest.stopRouter(); + long t = System.currentTimeMillis(); + System.out.println(formattedTimeStamp(t) + " contention! ms killed " + (t - oldest.starttime)); + } + } + + threadQueue.add(server); + + server.start(); + if (debug) System.out.println("thread started..."); + } + } + + + private static HashMap getUrlParams(String url) throws UnsupportedEncodingException { + HashMap params = new HashMap(); + String decoded = URLDecoder.decode(url, "UTF-8"); + StringTokenizer tk = new StringTokenizer(decoded, "?&"); + while (tk.hasMoreTokens()) { + String t = tk.nextToken(); + StringTokenizer tk2 = new StringTokenizer(t, "="); + if (tk2.hasMoreTokens()) { + String key = tk2.nextToken(); + if (tk2.hasMoreTokens()) { + String value = tk2.nextToken(); + params.put(key, value); + } + } + } + return params; + } + + private static long getMaxRunningTime() { + long maxRunningTime = 60000; + String sMaxRunningTime = System.getProperty("maxRunningTime"); + if (sMaxRunningTime != null) { + maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000; + } + return maxRunningTime; + } + + private static void writeHttpHeader(BufferedWriter bw, String status) throws IOException { + writeHttpHeader(bw, "text/plain", status); + } + + private static void writeHttpHeader(BufferedWriter bw, String mimeType, String status) throws IOException { + writeHttpHeader(bw, mimeType, null, status); + } + + private static void writeHttpHeader(BufferedWriter bw, String mimeType, String fileName, String status) throws IOException { + writeHttpHeader(bw, mimeType, fileName, null, status); + } + + private static void writeHttpHeader(BufferedWriter bw, String mimeType, String fileName, String headers, String status) throws IOException { + // http-header + bw.write(String.format("HTTP/1.1 %s\n", status)); + bw.write("Connection: close\n"); + bw.write("Content-Type: " + mimeType + "; charset=utf-8\n"); + if (fileName != null) { + bw.write("Content-Disposition: attachment; filename=\"" + fileName + "\"\n"); + } + bw.write("Access-Control-Allow-Origin: *\n"); + if (headers != null) { + bw.write(headers); + } + bw.write("\n"); + } + + private static void cleanupThreadQueue(PriorityQueue threadQueue) { + for (; ; ) { + boolean removedItem = false; + for (RouteServer t : threadQueue) { + if (t.terminated) { + threadQueue.remove(t); + removedItem = true; + break; + } + } + if (!removedItem) { + break; + } + } + } + + @Override + public int compareTo(RouteServer t) { + return starttime < t.starttime ? -1 : (starttime > t.starttime ? 1 : 0); + } + +} diff --git a/brouter-server/src/main/java/btools/server/ServiceContext.java b/brouter-server/src/main/java/btools/server/ServiceContext.java index 1d1b950..fb3eea9 100644 --- a/brouter-server/src/main/java/btools/server/ServiceContext.java +++ b/brouter-server/src/main/java/btools/server/ServiceContext.java @@ -1,20 +1,19 @@ -package btools.server; - -import java.util.List; -import java.util.Map; -import java.io.File; - -import btools.router.OsmNodeNamed; - -/** - * Environment configuration that is initialized at server/service startup - */ -public class ServiceContext -{ - public File segmentDir; - public String profileDir; - public String customProfileDir; - public String sharedProfileDir; - public Map profileMap = null; - public List nogoList; -} +package btools.server; + +import java.util.List; +import java.util.Map; +import java.io.File; + +import btools.router.OsmNodeNamed; + +/** + * Environment configuration that is initialized at server/service startup + */ +public class ServiceContext { + public File segmentDir; + public String profileDir; + public String customProfileDir; + public String sharedProfileDir; + public Map profileMap = null; + public List nogoList; +} diff --git a/brouter-server/src/main/java/btools/server/SuspectManager.java b/brouter-server/src/main/java/btools/server/SuspectManager.java index 22ccedc..ea40886 100644 --- a/brouter-server/src/main/java/btools/server/SuspectManager.java +++ b/brouter-server/src/main/java/btools/server/SuspectManager.java @@ -15,211 +15,180 @@ import btools.mapaccess.OsmNode; import btools.router.OsmNodeNamed; import btools.router.SuspectInfo; -public class SuspectManager extends Thread -{ - private static SimpleDateFormat dfTimestampZ = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" ); +public class SuspectManager extends Thread { + private static SimpleDateFormat dfTimestampZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); static NearRecentWps nearRecentWps = new NearRecentWps(); static NearRecentWps hiddenWps = new NearRecentWps(); - private static String formatZ( Date date ) - { - synchronized( dfTimestampZ ) - { - return dfTimestampZ.format( date ); + private static String formatZ(Date date) { + synchronized (dfTimestampZ) { + return dfTimestampZ.format(date); } } - private static String formatAge( File f ) - { - return formatAge( System.currentTimeMillis() - f.lastModified() ); + private static String formatAge(File f) { + return formatAge(System.currentTimeMillis() - f.lastModified()); } - private static String formatAge( long age ) - { + private static String formatAge(long age) { long minutes = age / 60000; - if ( minutes < 60 ) - { + if (minutes < 60) { return minutes + " minutes"; } long hours = minutes / 60; - if ( hours < 24 ) - { + if (hours < 24) { return hours + " hours"; } long days = hours / 24; return days + " days"; } - private static String getLevelDecsription( int level ) - { - switch( level ) - { - case 30 : return "motorway"; - case 28 : return "trunk"; - case 26 : return "primary"; - case 24 : return "secondary"; - case 22 : return "tertiary"; - default: return "none"; + private static String getLevelDecsription(int level) { + switch (level) { + case 30: + return "motorway"; + case 28: + return "trunk"; + case 26: + return "primary"; + case 24: + return "secondary"; + case 22: + return "tertiary"; + default: + return "none"; } } - - - private static void markFalsePositive( SuspectList suspects, long id ) throws IOException - { - new File( "falsepositives/" + id ).createNewFile(); - for( int isuspect = 0; isuspect suspects.timestamp ) - { + if (hideTime < 0) { + File confirmedEntry = new File("confirmednegatives/" + id); + if (confirmedEntry.exists() && confirmedEntry.lastModified() > suspects.timestamp) { continue; // that would be under current suspects } } - dueTime = hideTime < 0 ? "(asap)" : formatAge( hideTime + 43200000 ); + dueTime = hideTime < 0 ? "(asap)" : formatAge(hideTime + 43200000); } - if ( polygon != null && !polygon.isInArea( id ) ) - { + if (polygon != null && !polygon.isInArea(id)) { continue; // not in selected polygon } - File confirmedEntry = new File( "confirmednegatives/" + id ); + File confirmedEntry = new File("confirmednegatives/" + id); String status = suspects.newOrConfirmed[isuspect] ? "new" : "archived"; - if ( confirmedEntry.exists() ) - { - status = "confirmed " + formatAge( confirmedEntry ) + " ago"; + if (confirmedEntry.exists()) { + status = "confirmed " + formatAge(confirmedEntry) + " ago"; } - - if ( dueTime != null ) - { + + if (dueTime != null) { status = "deferred"; } - if ( n > 0 ) - { - bw.write( "," ); + if (n > 0) { + bw.write(","); } - - int ilon = (int) ( id >> 32 ); - int ilat = (int) ( id & 0xffffffff ); - double dlon = ( ilon - 180000000 ) / 1000000.; - double dlat = ( ilat - 90000000 ) / 1000000.; - - String slevel = getLevelDecsription( nprio ); - bw.write( "\n{\n" ); - bw.write( " \"id\": " + n + ",\n" ); - bw.write( " \"type\": \"Feature\",\n" ); - bw.write( " \"properties\": {\n" ); - bw.write( " \"issue_id\": \"" + id + "\",\n" ); - bw.write( " \"Status\": \"" + status + "\",\n" ); - if ( dueTime != null ) - { - bw.write( " \"DueTime\": \"" + dueTime + "\",\n" ); + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + double dlon = (ilon - 180000000) / 1000000.; + double dlat = (ilat - 90000000) / 1000000.; + + String slevel = getLevelDecsription(nprio); + + bw.write("\n{\n"); + bw.write(" \"id\": " + n + ",\n"); + bw.write(" \"type\": \"Feature\",\n"); + bw.write(" \"properties\": {\n"); + bw.write(" \"issue_id\": \"" + id + "\",\n"); + bw.write(" \"Status\": \"" + status + "\",\n"); + if (dueTime != null) { + bw.write(" \"DueTime\": \"" + dueTime + "\",\n"); } - bw.write( " \"Level\": \"" + slevel + "\"\n" ); - bw.write( " },\n" ); - bw.write( " \"geometry\": {\n" ); - bw.write( " \"type\": \"Point\",\n" ); - bw.write( " \"coordinates\": [\n" ); - bw.write( " " + dlon + ",\n" ); - bw.write( " " + dlat + "\n" ); - bw.write( " ]\n" ); - bw.write( " }\n" ); - bw.write( "}" ); - + bw.write(" \"Level\": \"" + slevel + "\"\n"); + bw.write(" },\n"); + bw.write(" \"geometry\": {\n"); + bw.write(" \"type\": \"Point\",\n"); + bw.write(" \"coordinates\": [\n"); + bw.write(" " + dlon + ",\n"); + bw.write(" " + dlat + "\n"); + bw.write(" ]\n"); + bw.write(" }\n"); + bw.write("}"); + n++; - } - - bw.write( "\n ]\n" ); - bw.write( "}\n" ); + } + + bw.write("\n ]\n"); + bw.write("}\n"); bw.flush(); } - - public static void process( String url, BufferedWriter bw ) throws IOException - { - try - { - _process( url, bw ); - } - catch( IllegalArgumentException iae ) - { - try - { - bw.write( "

ERROR: " + iae.getMessage() + "

\n\n" ); - bw.write( "(press Browser-Back to continue)\n" ); + + public static void process(String url, BufferedWriter bw) throws IOException { + try { + _process(url, bw); + } catch (IllegalArgumentException iae) { + try { + bw.write("

ERROR: " + iae.getMessage() + "

\n\n"); + bw.write("(press Browser-Back to continue)\n"); bw.flush(); + } catch (IOException _ignore) { } - catch (IOException _ignore){} } } - private static void _process( String url, BufferedWriter bw ) throws IOException - { - StringTokenizer tk = new StringTokenizer( url, "/?" ); + private static void _process(String url, BufferedWriter bw) throws IOException { + StringTokenizer tk = new StringTokenizer(url, "/?"); tk.nextToken(); tk.nextToken(); long id = 0L; @@ -228,277 +197,228 @@ public class SuspectManager extends Thread String suspectFilename = "worldsuspects.txt"; String filter = null; - while ( tk.hasMoreTokens() ) - { + while (tk.hasMoreTokens()) { String c = tk.nextToken(); - if ( "all".equals( c ) || "new".equals( c ) || "confirmed".equals( c ) || "fp".equals( c ) || "deferred".equals( c ) ) - { + if ("all".equals(c) || "new".equals(c) || "confirmed".equals(c) || "fp".equals(c) || "deferred".equals(c)) { filter = c; break; } - if ( country.length() == 0 && !"world".equals(c) ) - { - if ( new File( c + "suspects.txt" ).exists() ) - { + if (country.length() == 0 && !"world".equals(c)) { + if (new File(c + "suspects.txt").exists()) { suspectFilename = c + "suspects.txt"; challenge = "/" + c; continue; } } - + country += "/" + c; } - - SuspectList suspects = getAllSuspects( suspectFilename ); + + SuspectList suspects = getAllSuspects(suspectFilename); Area polygon = null; - if ( !("/world".equals( country ) || "".equals( country ) ) ) - { - File polyFile = new File( "worldpolys" + country + ".poly" ); - if ( !polyFile.exists() ) - { - bw.write( "polygon file for country '" + country + "' not found\n" ); - bw.write( "\n" ); + if (!("/world".equals(country) || "".equals(country))) { + File polyFile = new File("worldpolys" + country + ".poly"); + if (!polyFile.exists()) { + bw.write("polygon file for country '" + country + "' not found\n"); + bw.write("\n"); bw.flush(); return; } - polygon = new Area( polyFile ); + polygon = new Area(polyFile); } - if ( url.endsWith( ".json" ) ) - { - StringTokenizer tk2 = new StringTokenizer( tk.nextToken(), "." ); - int level = Integer.parseInt( tk2.nextToken() ); - - newAndConfirmedJson( suspects, bw, filter, level, polygon ); + if (url.endsWith(".json")) { + StringTokenizer tk2 = new StringTokenizer(tk.nextToken(), "."); + int level = Integer.parseInt(tk2.nextToken()); + + newAndConfirmedJson(suspects, bw, filter, level, polygon); return; } - - bw.write( "\n" ); - bw.write( "BRouter suspect manager. Help

\n" ); - if ( filter == null ) // generate country list + bw.write("\n"); + bw.write("BRouter suspect manager. Help

\n"); + + if (filter == null) // generate country list { - bw.write( "\n" ); - File countryParent = new File( "worldpolys" + country ); + bw.write("
\n"); + File countryParent = new File("worldpolys" + country); File[] files = countryParent.listFiles(); TreeSet names = new TreeSet(); - for ( File f : files ) - { + for (File f : files) { String name = f.getName(); - if ( name.endsWith( ".poly" ) ) - { - names.add( name.substring( 0, name.length() - 5 ) ); + if (name.endsWith(".poly")) { + names.add(name.substring(0, name.length() - 5)); } } - for ( String c : names ) - { + for (String c : names) { String url2 = "/brouter/suspects" + challenge + country + "/" + c; String linkNew = ""; String linkCnf = ""; String linkAll = ""; - + String linkSub = ""; - if ( new File( countryParent, c ).exists() ) - { + if (new File(countryParent, c).exists()) { linkSub = ""; } - bw.write( "" + linkNew + linkCnf + linkAll + linkSub + "\n" ); + bw.write("" + linkNew + linkCnf + linkAll + linkSub + "\n"); } - bw.write( "
 new  confirmed  all  sub-regions 
" + c + "
" + c + "
\n" ); - bw.write( "\n" ); + bw.write("\n"); + bw.write("\n"); bw.flush(); return; } - File suspectFile = new File( "worldsuspects.txt" ); - if ( !suspectFile.exists() ) - { - bw.write( "suspect file worldsuspects.txt not found\n" ); - bw.write( "\n" ); + File suspectFile = new File("worldsuspects.txt"); + if (!suspectFile.exists()) { + bw.write("suspect file worldsuspects.txt not found\n"); + bw.write("\n"); bw.flush(); return; } boolean showWatchList = false; - if ( tk.hasMoreTokens() ) - { + if (tk.hasMoreTokens()) { String t = tk.nextToken(); - if ( "watchlist".equals( t ) ) - { + if ("watchlist".equals(t)) { showWatchList = true; - } - else - { - id = Long.parseLong( t ); + } else { + id = Long.parseLong(t); } } - if ( showWatchList ) - { - bw.write( "watchlist for " + country + "\n" ); - bw.write( "
back to country list

\n" ); - + if (showWatchList) { + bw.write("watchlist for " + country + "\n"); + bw.write("
back to country list

\n"); + long timeNow = System.currentTimeMillis(); - for( int isuspect = 0; isuspect suspects.timestamp ) - { + if (hideTime < 0) { + File confirmedEntry = new File("confirmednegatives/" + id); + if (confirmedEntry.exists() && confirmedEntry.lastModified() > suspects.timestamp) { continue; // that would be under current suspects } } - if ( polygon != null && !polygon.isInArea( id ) ) - { + if (polygon != null && !polygon.isInArea(id)) { continue; // not in selected polygon } - + String countryId = challenge + country + "/" + filter + "/" + id; - String dueTime = hideTime < 0 ? "(asap)" : formatAge( hideTime + 43200000 ); + String dueTime = hideTime < 0 ? "(asap)" : formatAge(hideTime + 43200000); String hint = "   due in " + dueTime; - int ilon = (int) ( id >> 32 ); - int ilat = (int) ( id & 0xffffffff ); - double dlon = ( ilon - 180000000 ) / 1000000.; - double dlat = ( ilat - 90000000 ) / 1000000.; + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + double dlon = (ilon - 180000000) / 1000000.; + double dlat = (ilat - 90000000) / 1000000.; String url2 = "/brouter/suspects" + countryId; - bw.write( "" + dlon + "," + dlat + "" + hint + "
\n" ); + bw.write("" + dlon + "," + dlat + "" + hint + "
\n"); } - bw.write( "\n" ); + bw.write("\n"); bw.flush(); return; } String message = null; - if ( tk.hasMoreTokens() ) - { + if (tk.hasMoreTokens()) { String command = tk.nextToken(); - if ( "falsepositive".equals( command ) ) - { - int wps = nearRecentWps.count( id ); - if ( wps < 8 ) - { + if ("falsepositive".equals(command)) { + int wps = nearRecentWps.count(id); + if (wps < 8) { message = "marking false-positive requires at least 8 recent nearby waypoints from BRouter-Web, found: " + wps + "

****** DO SOME MORE TEST-ROUTINGS IN BROUTER-WEB ******* before marking false positive"; - } - else - { - markFalsePositive( suspects, id ); + } else { + markFalsePositive(suspects, id); message = "Marked issue " + id + " as false-positive"; id = 0L; } } - if ( "confirm".equals( command ) ) - { - int wps = nearRecentWps.count( id ); - if ( wps < 2 ) - { + if ("confirm".equals(command)) { + int wps = nearRecentWps.count(id); + if (wps < 2) { message = "marking confirmed requires at least 2 recent nearby waypoints from BRouter-Web, found: " + wps - + "

****** DO AT LEAST ONE TEST-ROUTING IN BROUTER-WEB ******* before marking confirmed"; - } - else - { - new File( "confirmednegatives/" + id ).createNewFile(); + + "

****** DO AT LEAST ONE TEST-ROUTING IN BROUTER-WEB ******* before marking confirmed"; + } else { + new File("confirmednegatives/" + id).createNewFile(); } } - if ( "fixed".equals( command ) ) - { - File fixedMarker = new File( "fixedsuspects/" + id ); - if ( !fixedMarker.exists() ) - { + if ("fixed".equals(command)) { + File fixedMarker = new File("fixedsuspects/" + id); + if (!fixedMarker.exists()) { fixedMarker.createNewFile(); } int hideDays = 0; - if ( tk.hasMoreTokens() ) - { + if (tk.hasMoreTokens()) { String param = tk.nextToken(); - if ( param.startsWith( "ndays=" ) ) - { - param = param.substring( "ndays=".length() ); + if (param.startsWith("ndays=")) { + param = param.substring("ndays=".length()); } - try - { - hideDays = Integer.parseInt( param ); // hiding, not fixing + try { + hideDays = Integer.parseInt(param); // hiding, not fixing + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("not a number: " + param); } - catch( NumberFormatException nfe ) - { - throw new IllegalArgumentException( "not a number: " + param ); - } - if ( hideDays < 1 || hideDays > 999 ) - { - throw new IllegalArgumentException( "hideDays must be within 1..999" ); + if (hideDays < 1 || hideDays > 999) { + throw new IllegalArgumentException("hideDays must be within 1..999"); } message = "Hide issue " + id + " for " + hideDays + " days"; - } - else - { + } else { message = "Marked issue " + id + " as fixed"; } - if ( hideDays > 0 ) - { - OsmNodeNamed nn = new OsmNodeNamed ( new OsmNode( id ) ); + if (hideDays > 0) { + OsmNodeNamed nn = new OsmNodeNamed(new OsmNode(id)); nn.name = "" + hideDays; - hiddenWps.add( nn ); + hiddenWps.add(nn); } id = 0L; - fixedMarker.setLastModified( System.currentTimeMillis() + hideDays*86400000L ); + fixedMarker.setLastModified(System.currentTimeMillis() + hideDays * 86400000L); } } - if ( id != 0L ) - { + if (id != 0L) { String countryId = challenge + country + "/" + filter + "/" + id; - int ilon = (int) ( id >> 32 ); - int ilat = (int) ( id & 0xffffffff ); - double dlon = ( ilon - 180000000 ) / 1000000.; - double dlat = ( ilat - 90000000 ) / 1000000.; + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + double dlon = (ilon - 180000000) / 1000000.; + double dlat = (ilat - 90000000) / 1000000.; String profile = "car-eco"; - File configFile = new File( "configs/profile.cfg" ); - if ( configFile.exists() ) - { - BufferedReader br = new BufferedReader( new FileReader( configFile ) ); + File configFile = new File("configs/profile.cfg"); + if (configFile.exists()) { + BufferedReader br = new BufferedReader(new FileReader(configFile)); profile = br.readLine(); br.close(); } // get triggers - int triggers = suspects.trigger4Id( id ); + int triggers = suspects.trigger4Id(id); SuspectList daily = getDailySuspectsIfLoaded(); - if ( daily != null && daily != suspects ) - { - triggers |= daily.trigger4Id( id ); // hack, because osmoscope does not echo type of analysis + if (daily != null && daily != suspects) { + triggers |= daily.trigger4Id(id); // hack, because osmoscope does not echo type of analysis } - String triggerText = SuspectInfo.getTriggerText( triggers ); - + String triggerText = SuspectInfo.getTriggerText(triggers); String url1 = "http://brouter.de/brouter-web/#map=18/" + dlat + "/" + dlon - + "/OpenStreetMap&lonlats=" + dlon + "," + dlat + "&profile=" + profile; + + "/OpenStreetMap&lonlats=" + dlon + "," + dlat + "&profile=" + profile; // String url1 = "http://localhost:8080/brouter-web/#map=18/" + dlat + "/" // + dlon + "/Mapsforge Tile Server&lonlats=" + dlon + "," + dlat; @@ -507,157 +427,132 @@ public class SuspectManager extends Thread double slon = 0.00156; double slat = 0.001; - String url3 = "http://127.0.0.1:8111/load_and_zoom?left=" + ( dlon - slon ) - + "&bottom=" + ( dlat - slat ) + "&right=" + ( dlon + slon ) + "&top=" + ( dlat + slat ); + String url3 = "http://127.0.0.1:8111/load_and_zoom?left=" + (dlon - slon) + + "&bottom=" + (dlat - slat) + "&right=" + (dlon + slon) + "&top=" + (dlat + slat); - Date weekAgo = new Date( System.currentTimeMillis() - 604800000L ); - String url4a = "https://overpass-turbo.eu/?Q=[date:"" + formatZ( weekAgo ) + "Z"];way[highway]({{bbox}});out meta geom;&C=" - + dlat + ";" + dlon + ";18&R"; + Date weekAgo = new Date(System.currentTimeMillis() - 604800000L); + String url4a = "https://overpass-turbo.eu/?Q=[date:"" + formatZ(weekAgo) + "Z"];way[highway]({{bbox}});out meta geom;&C=" + + dlat + ";" + dlon + ";18&R"; String url4b = "https://overpass-turbo.eu/?Q=(node(around%3A1%2C%7B%7Bcenter%7D%7D)-%3E.n%3Bway(bn.n)%5Bhighway%5D%3Brel(bn.n%3A%22via%22)%5Btype%3Drestriction%5D%3B)%3Bout%20meta%3B%3E%3Bout%20skel%20qt%3B&C=" - + dlat + ";" + dlon + ";18&R"; + + dlat + ";" + dlon + ";18&R"; String url5 = "https://tyrasd.github.io/latest-changes/#16/" + dlat + "/" + dlon; String url6 = "https://apps.sentinel-hub.com/sentinel-playground/?source=S2L2A&lat=" + dlat + "&lng=" + dlon + "&zoom=15"; - if ( message != null ) - { - bw.write( "" + message + "

\n" ); + if (message != null) { + bw.write("" + message + "

\n"); } - bw.write( "Trigger: " + triggerText + "

\n" ); - bw.write( "Open in BRouter-Web

\n" ); - bw.write( "Open in OpenStreetmap

\n" ); - bw.write( "Open in JOSM (via remote control)

\n" ); - bw.write( "Overpass: minus one week    node context

\n" ); - bw.write( "Open in Latest-Changes / last week

\n" ); - bw.write( "Current Sentinel-2 imagary

\n" ); - bw.write( "
\n" ); - if ( isFixed( id, suspects.timestamp ) ) - { - bw.write( "

back to watchlist

\n" ); - } - else - { - bw.write( "mark false positive (=not an issue)

\n" ); - File confirmedEntry = new File( "confirmednegatives/" + id ); - if ( confirmedEntry.exists() ) - { + bw.write("Trigger: " + triggerText + "

\n"); + bw.write("Open in BRouter-Web

\n"); + bw.write("Open in OpenStreetmap

\n"); + bw.write("Open in JOSM (via remote control)

\n"); + bw.write("Overpass: minus one week    node context

\n"); + bw.write("Open in Latest-Changes / last week

\n"); + bw.write("Current Sentinel-2 imagary

\n"); + bw.write("
\n"); + if (isFixed(id, suspects.timestamp)) { + bw.write("

back to watchlist

\n"); + } else { + bw.write("mark false positive (=not an issue)

\n"); + File confirmedEntry = new File("confirmednegatives/" + id); + if (confirmedEntry.exists()) { String prefix = "hide for days:   "; - bw.write( prefix + "\">mark as fixed

\n" ); - bw.write( "hide for: weeks:" ); - bw.write( prefix2 + "/7\">1w" ); - bw.write( prefix2 + "/14\">2w" ); - bw.write( prefix2 + "/21\">3w" ); - bw.write( "     months:" ); - bw.write( prefix2 + "/30\">1m" ); - bw.write( prefix2 + "/61\">2m" ); - bw.write( prefix2 + "/91\">3m" ); - bw.write( prefix2 + "/122\">4m" ); - bw.write( prefix2 + "/152\">5m" ); - bw.write( prefix2 + "/183\">6m

\n" ); - bw.write( prefix2d + "

\n" ); + bw.write(prefix + "\">mark as fixed

\n"); + bw.write("hide for: weeks:"); + bw.write(prefix2 + "/7\">1w"); + bw.write(prefix2 + "/14\">2w"); + bw.write(prefix2 + "/21\">3w"); + bw.write("     months:"); + bw.write(prefix2 + "/30\">1m"); + bw.write(prefix2 + "/61\">2m"); + bw.write(prefix2 + "/91\">3m"); + bw.write(prefix2 + "/122\">4m"); + bw.write(prefix2 + "/152\">5m"); + bw.write(prefix2 + "/183\">6m

\n"); + bw.write(prefix2d + "

\n"); + } else { + bw.write("mark as a confirmed issue

\n"); } - else - { - bw.write( "mark as a confirmed issue

\n" ); - } - if ( polygon != null ) - { - bw.write( "

back to issue list

\n" ); + if (polygon != null) { + bw.write("

back to issue list

\n"); } } - } - else if ( polygon == null ) - { - bw.write( message + "
\n" ); - } - else - { - bw.write( filter + " suspect list for " + country + "\n" ); - bw.write( "
see watchlist\n" ); - bw.write( "
back to country list

\n" ); + } else if (polygon == null) { + bw.write(message + "
\n"); + } else { + bw.write(filter + " suspect list for " + country + "\n"); + bw.write("
see watchlist\n"); + bw.write("
back to country list

\n"); int maxprio = 0; { - for( int isuspect = 0; isuspect
\n" ); + int nprio = ((prio + 1) / 2) * 2; // normalize (no link prios) + if (nprio < maxprio) { + if (maxprio == 0) { + bw.write("current level: " + getLevelDecsription(maxprio) + "

\n"); } break; } - if ( polygon != null && !polygon.isInBoundingBox( id ) ) - { + if (polygon != null && !polygon.isInBoundingBox(id)) { continue; // not in selected polygon (pre-check) } - if ( new File( "falsepositives/" + id ).exists() ) - { + if (new File("falsepositives/" + id).exists()) { continue; // known false positive } - if ( isFixed( id, suspects.timestamp ) ) - { + if (isFixed(id, suspects.timestamp)) { continue; // known fixed } - if ( "new".equals( filter ) && new File( "suspectarchive/" + id ).exists() ) - { + if ("new".equals(filter) && new File("suspectarchive/" + id).exists()) { continue; // known archived } - if ( "confirmed".equals( filter ) && !new File( "confirmednegatives/" + id ).exists() ) - { + if ("confirmed".equals(filter) && !new File("confirmednegatives/" + id).exists()) { continue; // showing confirmed suspects only } - if ( polygon != null && !polygon.isInArea( id ) ) - { + if (polygon != null && !polygon.isInArea(id)) { continue; // not in selected polygon } - if ( maxprio == 0 ) - { + if (maxprio == 0) { maxprio = nprio; - bw.write( "current level: " + getLevelDecsription( maxprio ) + "

\n" ); + bw.write("current level: " + getLevelDecsription(maxprio) + "

\n"); } String countryId = challenge + country + "/" + filter + "/" + id; - File confirmedEntry = new File( "confirmednegatives/" + id ); + File confirmedEntry = new File("confirmednegatives/" + id); String hint = ""; - if ( confirmedEntry.exists() ) - { - hint = "   confirmed " + formatAge( confirmedEntry ) + " ago"; + if (confirmedEntry.exists()) { + hint = "   confirmed " + formatAge(confirmedEntry) + " ago"; } - int ilon = (int) ( id >> 32 ); - int ilat = (int) ( id & 0xffffffff ); - double dlon = ( ilon - 180000000 ) / 1000000.; - double dlat = ( ilat - 90000000 ) / 1000000.; + int ilon = (int) (id >> 32); + int ilat = (int) (id & 0xffffffff); + double dlon = (ilon - 180000000) / 1000000.; + double dlat = (ilat - 90000000) / 1000000.; String url2 = "/brouter/suspects" + countryId; - bw.write( "" + dlon + "," + dlat + "" + hint + "
\n" ); + bw.write("" + dlon + "," + dlat + "" + hint + "
\n"); } } } - bw.write( "\n" ); + bw.write("\n"); bw.flush(); return; } - - private static boolean isFixed( long id, long timestamp ) - { - File fixedEntry = new File( "fixedsuspects/" + id ); + + private static boolean isFixed(long id, long timestamp) { + File fixedEntry = new File("fixedsuspects/" + id); return fixedEntry.exists() && fixedEntry.lastModified() > timestamp; } - - - private static final class SuspectList - { + + + private static final class SuspectList { int cnt; long[] ids; int[] prios; @@ -665,9 +560,8 @@ public class SuspectManager extends Thread boolean[] newOrConfirmed; boolean[] falsePositive; long timestamp; - - SuspectList( int count, long time ) - { + + SuspectList(int count, long time) { cnt = count; ids = new long[cnt]; prios = new int[cnt]; @@ -677,12 +571,9 @@ public class SuspectManager extends Thread timestamp = time; } - int trigger4Id( long id ) - { - for( int i = 0; i allSuspectsMap = new HashMap(); - - private static SuspectList getDailySuspectsIfLoaded() throws IOException - { - synchronized( allSuspectsMap ) - { - return allSuspectsMap.get( "dailysuspects.txt" ); + private static HashMap allSuspectsMap = new HashMap(); + + private static SuspectList getDailySuspectsIfLoaded() throws IOException { + synchronized (allSuspectsMap) { + return allSuspectsMap.get("dailysuspects.txt"); } } - private static SuspectList getAllSuspects( String suspectFileName ) throws IOException - { - synchronized( allSuspectsMap ) - { - SuspectList allSuspects = allSuspectsMap.get( suspectFileName ); - File suspectFile = new File( suspectFileName ); - if ( allSuspects != null && suspectFile.lastModified() == allSuspects.timestamp ) - { + private static SuspectList getAllSuspects(String suspectFileName) throws IOException { + synchronized (allSuspectsMap) { + SuspectList allSuspects = allSuspectsMap.get(suspectFileName); + File suspectFile = new File(suspectFileName); + if (allSuspects != null && suspectFile.lastModified() == allSuspects.timestamp) { return allSuspects; } // count prios int[] prioCount = new int[100]; - BufferedReader r = new BufferedReader( new FileReader( suspectFile ) ); - for ( ;; ) - { + BufferedReader r = new BufferedReader(new FileReader(suspectFile)); + for (; ; ) { String line = r.readLine(); - if ( line == null ) break; - StringTokenizer tk2 = new StringTokenizer( line ); + if (line == null) break; + StringTokenizer tk2 = new StringTokenizer(line); tk2.nextToken(); - int prio = Integer.parseInt( tk2.nextToken() ); - int nprio = ( ( prio + 1 ) / 2 ) * 2; // normalize (no link prios) + int prio = Integer.parseInt(tk2.nextToken()); + int nprio = ((prio + 1) / 2) * 2; // normalize (no link prios) prioCount[nprio]++; } r.close(); // sum up int pointer = 0; - for( int i=99; i>=0; i-- ) - { + for (int i = 99; i >= 0; i--) { int cnt = prioCount[i]; prioCount[i] = pointer; pointer += cnt; } - - - + // sort into suspect list - allSuspects = new SuspectList( pointer, suspectFile.lastModified() ); - r = new BufferedReader( new FileReader( suspectFile ) ); - for ( ;; ) - { + allSuspects = new SuspectList(pointer, suspectFile.lastModified()); + r = new BufferedReader(new FileReader(suspectFile)); + for (; ; ) { String line = r.readLine(); - if ( line == null ) break; - StringTokenizer tk2 = new StringTokenizer( line ); - long id = Long.parseLong( tk2.nextToken() ); - int prio = Integer.parseInt( tk2.nextToken() ); - int nprio = ( ( prio + 1 ) / 2 ) * 2; // normalize (no link prios) + if (line == null) break; + StringTokenizer tk2 = new StringTokenizer(line); + long id = Long.parseLong(tk2.nextToken()); + int prio = Integer.parseInt(tk2.nextToken()); + int nprio = ((prio + 1) / 2) * 2; // normalize (no link prios) pointer = prioCount[nprio]++; allSuspects.ids[pointer] = id; allSuspects.prios[pointer] = prio; - allSuspects.triggers[pointer] = tk2.hasMoreTokens() ? Integer.parseInt( tk2.nextToken() ) : 0; - allSuspects.newOrConfirmed[pointer] = new File( "confirmednegatives/" + id ).exists() || !(new File( "suspectarchive/" + id ).exists() ); - allSuspects.falsePositive[pointer] = new File( "falsepositives/" + id ).exists(); + allSuspects.triggers[pointer] = tk2.hasMoreTokens() ? Integer.parseInt(tk2.nextToken()) : 0; + allSuspects.newOrConfirmed[pointer] = new File("confirmednegatives/" + id).exists() || !(new File("suspectarchive/" + id).exists()); + allSuspects.falsePositive[pointer] = new File("falsepositives/" + id).exists(); } r.close(); - allSuspectsMap.put( suspectFileName, allSuspects ); + allSuspectsMap.put(suspectFileName, allSuspects); return allSuspects; - } - } + } + } } diff --git a/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java b/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java index 896257a..4cc2138 100644 --- a/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java +++ b/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java @@ -3,6 +3,7 @@ package btools.server.request; import btools.router.RoutingContext; import btools.router.RoutingEngine; import btools.server.ServiceContext; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -15,8 +16,7 @@ import java.util.Map; /** * Custom profile uploads */ -public class ProfileUploadHandler -{ +public class ProfileUploadHandler { // maximum number of characters (file size limit for custom profiles) private static final int MAX_LENGTH = 100000; @@ -24,29 +24,25 @@ public class ProfileUploadHandler public static final String CUSTOM_PREFIX = "custom_"; public static final String SHARED_PREFIX = "shared_"; - private ServiceContext serviceContext; - - public ProfileUploadHandler( ServiceContext serviceContext) - { - this.serviceContext = serviceContext; - } + private ServiceContext serviceContext; - public void handlePostRequest(String profileId, BufferedReader br, BufferedWriter response) throws IOException - { + public ProfileUploadHandler(ServiceContext serviceContext) { + this.serviceContext = serviceContext; + } + + public void handlePostRequest(String profileId, BufferedReader br, BufferedWriter response) throws IOException { BufferedWriter fileWriter = null; - try - { + try { String id; - if ( profileId != null ) - { + if (profileId != null) { // update existing file when id appended - id = profileId.substring( ProfileUploadHandler.CUSTOM_PREFIX.length() ); + id = profileId.substring(ProfileUploadHandler.CUSTOM_PREFIX.length()); } else { id = "" + System.currentTimeMillis(); } - File file = new File( getOrCreateCustomProfileDir(), id + ".brf" ); - fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream( file ) ) ); + File file = new File(getOrCreateCustomProfileDir(), id + ".brf"); + fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))); //StringWriter sw = new StringWriter(); bw = new BufferedWriter(sw); // only profile text as content @@ -56,57 +52,56 @@ public class ProfileUploadHandler //System.out.println("data: |" + sw.toString() + "|"); Map responseData = new HashMap(); - responseData.put( "profileid", CUSTOM_PREFIX + id ); + responseData.put("profileid", CUSTOM_PREFIX + id); - validateProfile( id, responseData ); + validateProfile(id, responseData); - response.write( toJSON( responseData ) ); - } - finally - { - if ( fileWriter != null ) try { fileWriter.close(); } catch( Exception e ) {} + response.write(toJSON(responseData)); + } finally { + if (fileWriter != null) try { + fileWriter.close(); + } catch (Exception e) { + } } } - private File getOrCreateCustomProfileDir() - { + private File getOrCreateCustomProfileDir() { // workaround: customProfileDir relative to profileDir, because RoutingEngine doesn't know custom profiles File customProfileDir = new File(serviceContext.profileDir, serviceContext.customProfileDir); - if (!customProfileDir.exists()) - { + if (!customProfileDir.exists()) { customProfileDir.mkdir(); } return customProfileDir; } // reads HTTP POST content from input into output stream/writer - private static void readPostData( BufferedReader ir, BufferedWriter bw, String id ) throws IOException - { + private static void readPostData(BufferedReader ir, BufferedWriter bw, String id) throws IOException { // Content-Type: text/plain;charset=UTF-8 int numChars = 0; // Content-Length header is in bytes (!= characters for UTF8), // but Reader reads characters, so don't know number of characters to read - for(;;) - { + for (; ; ) { // read will block when false, occurs at end of stream rather than -1 if (!ir.ready()) { - try { Thread.sleep(1000); } catch( Exception e ) {} - if ( !ir.ready() ) break; + try { + Thread.sleep(1000); + } catch (Exception e) { + } + if (!ir.ready()) break; } int c = ir.read(); - if ( c == -1) break; - bw.write( c ); - + if (c == -1) break; + bw.write(c); + numChars++; if (numChars > MAX_LENGTH) throw new IOException("Maximum number of characters exceeded (" + MAX_LENGTH + ", " + id + ")"); } } - private String toJSON( Map data ) - { + private String toJSON(Map data) { boolean first = true; StringBuilder sb = new StringBuilder(); sb.append("{"); @@ -123,29 +118,22 @@ public class ProfileUploadHandler return sb.toString(); } - public void validateProfile(String id, Map responseData) - { + public void validateProfile(String id, Map responseData) { // validate by initializing RoutingEngine, where parsing is done, and catching exceptions // see https://github.com/abrensch/brouter/issues/14 - try - { + try { RoutingContext rc = new RoutingContext(); - rc.localFunction = serviceContext.customProfileDir + "/" + id; - new RoutingEngine( null, null, null, null, rc ); - } - catch ( Exception e ) - { + rc.localFunction = serviceContext.customProfileDir + "/" + id; + new RoutingEngine(null, null, null, null, rc); + } catch (Exception e) { String msg = e.getMessage(); - if ( msg == null ) - { + if (msg == null) { msg = ""; - } - else if ( msg.indexOf( "does not contain expressions for context" ) >= 0 ) - { + } else if (msg.indexOf("does not contain expressions for context") >= 0) { // remove absolute path in this specific exception, useful for server, but don't disclose to client - msg = msg.substring( msg.indexOf( "does not contain expressions for context" ) ); + msg = msg.substring(msg.indexOf("does not contain expressions for context")); } - responseData.put( "error", "Profile error: " + msg ); + responseData.put("error", "Profile error: " + msg); } } } diff --git a/brouter-server/src/main/java/btools/server/request/RequestHandler.java b/brouter-server/src/main/java/btools/server/request/RequestHandler.java index bfcf6c3..fb69d30 100644 --- a/brouter-server/src/main/java/btools/server/request/RequestHandler.java +++ b/brouter-server/src/main/java/btools/server/request/RequestHandler.java @@ -1,31 +1,29 @@ -package btools.server.request; - -import java.util.HashMap; -import java.util.List; - -import btools.router.OsmNodeNamed; -import btools.router.OsmTrack; -import btools.router.RoutingContext; -import btools.server.ServiceContext; - -public abstract class RequestHandler -{ - protected ServiceContext serviceContext; - protected HashMap params; - - public RequestHandler( ServiceContext serviceContext, HashMap params ) - { - this.serviceContext = serviceContext; - this.params = params; - } - - public abstract RoutingContext readRoutingContext(); - - public abstract List readWayPointList(); - - public abstract String formatTrack(OsmTrack track); - - public abstract String getMimeType(); - - public abstract String getFileName(); -} \ No newline at end of file +package btools.server.request; + +import java.util.HashMap; +import java.util.List; + +import btools.router.OsmNodeNamed; +import btools.router.OsmTrack; +import btools.router.RoutingContext; +import btools.server.ServiceContext; + +public abstract class RequestHandler { + protected ServiceContext serviceContext; + protected HashMap params; + + public RequestHandler(ServiceContext serviceContext, HashMap params) { + this.serviceContext = serviceContext; + this.params = params; + } + + public abstract RoutingContext readRoutingContext(); + + public abstract List readWayPointList(); + + public abstract String formatTrack(OsmTrack track); + + public abstract String getMimeType(); + + public abstract String getFileName(); +} diff --git a/brouter-server/src/main/java/btools/server/request/ServerHandler.java b/brouter-server/src/main/java/btools/server/request/ServerHandler.java index 983b47d..2c3897c 100644 --- a/brouter-server/src/main/java/btools/server/request/ServerHandler.java +++ b/brouter-server/src/main/java/btools/server/request/ServerHandler.java @@ -1,343 +1,293 @@ -package btools.server.request; - -import btools.mapaccess.OsmNode; -import btools.router.OsmNodeNamed; -import btools.router.OsmNogoPolygon; -import btools.router.OsmTrack; -import btools.router.RoutingContext; -import btools.server.ServiceContext; -import java.io.BufferedWriter; -import java.io.File; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * URL query parameter handler for web and standalone server. Supports all - * BRouter features without restrictions. - * - * Parameters: - * - * lonlats = lon,lat|... (unlimited list of lon,lat waypoints separated by |) - * nogos = lon,lat,radius|... (optional, radius in meters) - * profile = profile file name without .brf - * alternativeidx = [0|1|2|3] (optional, default 0) - * format = [kml|gpx|geojson] (optional, default gpx) - * trackname = name used for filename and format specific trackname (optional, default brouter) - * exportWaypoints = 1 to export them (optional, default is no export) - * pois = lon,lat,name|... (optional) - * - * Example URLs: - * {@code http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx} - * {@code http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml&trackname=Ride&pois=1.1,2.1,Barner Bar} - * - */ -public class ServerHandler extends RequestHandler { - - private RoutingContext rc; - - public ServerHandler( ServiceContext serviceContext, HashMap params ) - { - super( serviceContext, params ); - } - - @Override - public RoutingContext readRoutingContext() - { - rc = new RoutingContext(); - rc.memoryclass = 128; - - String profile = params.get( "profile" ); - // when custom profile replace prefix with directory path - if ( profile.startsWith( ProfileUploadHandler.CUSTOM_PREFIX ) ) - { - String customProfile = profile.substring( ProfileUploadHandler.CUSTOM_PREFIX.length() ); - profile = new File( serviceContext.customProfileDir, customProfile ).getPath(); - } - else if ( profile.startsWith( ProfileUploadHandler.SHARED_PREFIX ) ) - { - String customProfile = profile.substring( ProfileUploadHandler.SHARED_PREFIX.length() ); - profile = new File( serviceContext.sharedProfileDir, customProfile ).getPath(); - } - rc.localFunction = profile; - - rc.setAlternativeIdx(Integer.parseInt(params.get( "alternativeidx" ))); - - List poisList = readPoisList(); - rc.poipoints = poisList; - - List nogoList = readNogoList(); - List nogoPolygonsList = readNogoPolygons(); - - if ( nogoList != null ) - { - RoutingContext.prepareNogoPoints( nogoList ); - rc.nogopoints = nogoList; - } - - if (rc.nogopoints == null) - { - rc.nogopoints = nogoPolygonsList; - } - else if ( nogoPolygonsList != null ) - { - rc.nogopoints.addAll(nogoPolygonsList); - } - - return rc; - } - - @Override - public List readWayPointList() - { - // lon,lat|... - String lonLats = params.get( "lonlats" ); - if (lonLats == null) throw new IllegalArgumentException( "lonlats parameter not set" ); - - String[] coords = lonLats.split("\\|"); - if (coords.length < 2) - throw new IllegalArgumentException( "we need two lat/lon points at least!" ); - - List wplist = new ArrayList(); - for (int i = 0; i < coords.length; i++) - { - String[] lonLat = coords[i].split(","); - if (lonLat.length < 2) - throw new IllegalArgumentException( "we need two lat/lon points at least!" ); - wplist.add( readPosition( lonLat[0], lonLat[1], "via" + i ) ); - } - - wplist.get(0).name = "from"; - wplist.get(wplist.size()-1).name = "to"; - - return wplist; - } - - @Override - public String formatTrack(OsmTrack track) - { - String result; - // optional, may be null - String format = params.get( "format" ); - String trackName = getTrackName(); - if (trackName != null) { - track.name = trackName; - } - String exportWaypointsStr = params.get( "exportWaypoints" ); - if (exportWaypointsStr != null && Integer.parseInt(exportWaypointsStr) != 0) { - track.exportWaypoints = true; - } - - if (format == null || "gpx".equals(format)) - { - result = track.formatAsGpx(); - } - else if ("kml".equals(format)) - { - result = track.formatAsKml(); - } - else if ("geojson".equals(format)) - { - result = track.formatAsGeoJson(); - } - else if ("csv".equals(format)) - { - try - { - StringWriter sw = new StringWriter(); - BufferedWriter bw = new BufferedWriter(sw); - track.writeMessages( bw, rc ); - return sw.toString(); - } - catch (Exception ex) - { - return "Error: " + ex.getMessage(); - } - } - else { - System.out.println("unknown track format '" + format + "', using default"); - result = track.formatAsGpx(); - } - - return result; - } - - @Override - public String getMimeType() - { - // default - String result = "text/plain"; - - // optional, may be null - String format = params.get( "format" ); - if ( format != null ) - { - if ( "gpx".equals( format ) ) - { - result = "application/gpx+xml"; - } - else if ( "kml".equals( format ) ) - { - result = "application/vnd.google-earth.kml+xml"; - } - else if ( "geojson".equals( format ) ) - { - result = "application/vnd.geo+json"; - } - else if ( "csv".equals( format ) ) - { - result = "text/tab-separated-values"; - } - } - - return result; - } - - @Override - public String getFileName() - { - String fileName = null; - String format = params.get( "format" ); - String trackName = getTrackName(); - - if ( format != null ) - { - fileName = ( trackName == null ? "brouter" : trackName ) + "." + format; - } - - return fileName; - } - - private String getTrackName() - { - return params.get( "trackname" ) == null ? null : params.get( "trackname" ).replaceAll("[^a-zA-Z0-9 \\._\\-]+", ""); - } - - private static OsmNodeNamed readPosition( String vlon, String vlat, String name ) - { - if ( vlon == null ) throw new IllegalArgumentException( "lon " + name + " not found in input" ); - if ( vlat == null ) throw new IllegalArgumentException( "lat " + name + " not found in input" ); - - return readPosition(Double.parseDouble( vlon ), Double.parseDouble( vlat ), name); - } - - private static OsmNodeNamed readPosition( double lon, double lat, String name ) - { - OsmNodeNamed n = new OsmNodeNamed(); - n.name = name; - n.ilon = (int)( ( lon + 180. ) *1000000. + 0.5); - n.ilat = (int)( ( lat + 90. ) *1000000. + 0.5); - return n; - } - - private List readPoisList() - { - // lon,lat,name|... - String pois = params.get( "pois" ); - if ( pois == null ) return null; - - String[] lonLatNameList = pois.split("\\|"); - - List poisList = new ArrayList(); - for (int i = 0; i < lonLatNameList.length; i++) - { - String[] lonLatName = lonLatNameList[i].split(","); - - if (lonLatName.length != 3) - continue; - - OsmNodeNamed n = new OsmNodeNamed(); - n.ilon = (int)( ( Double.parseDouble(lonLatName[0]) + 180. ) *1000000. + 0.5); - n.ilat = (int)( ( Double.parseDouble(lonLatName[1]) + 90. ) *1000000. + 0.5); - n.name = lonLatName[2]; - poisList.add(n); - } - - return poisList; - } - - private List readNogoList() - { - // lon,lat,radius|... - String nogos = params.get( "nogos" ); - if ( nogos == null ) return null; - - String[] lonLatRadList = nogos.split("\\|"); - - List nogoList = new ArrayList(); - for (int i = 0; i < lonLatRadList.length; i++) - { - String[] lonLatRad = lonLatRadList[i].split(","); - String nogoWeight = "NaN"; - if (lonLatRad.length > 3) { - nogoWeight = lonLatRad[3]; - } - nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight)); - } - - return nogoList; - } - - private static OsmNodeNamed readNogo( String lon, String lat, String radius, String nogoWeight ) - { - double weight = "undefined".equals( nogoWeight ) ? Double.NaN : Double.parseDouble( nogoWeight ); - return readNogo(Double.parseDouble( lon ), Double.parseDouble( lat ), Integer.parseInt( radius ), weight ); - } - - private static OsmNodeNamed readNogo( double lon, double lat, int radius, double nogoWeight ) - { - OsmNodeNamed n = new OsmNodeNamed(); - n.name = "nogo" + radius; - n.ilon = (int)( ( lon + 180. ) *1000000. + 0.5); - n.ilat = (int)( ( lat + 90. ) *1000000. + 0.5); - n.isNogo = true; - n.nogoWeight = nogoWeight; - return n; - } - - private List readNogoPolygons() - { - List result = new ArrayList(); - parseNogoPolygons( params.get("polylines"), result, false ); - parseNogoPolygons( params.get("polygons"), result, true ); - return result.size() > 0 ? result : null; - } - - private static void parseNogoPolygons(String polygons, List result, boolean closed ) - { - if ( polygons != null ) - { - String[] polygonList = polygons.split("\\|"); - for (int i = 0; i < polygonList.length; i++) - { - String[] lonLatList = polygonList[i].split(","); - if ( lonLatList.length > 1 ) - { - OsmNogoPolygon polygon = new OsmNogoPolygon(closed); - int j; - for (j = 0; j < 2 * (lonLatList.length / 2) - 1;) - { - String slon = lonLatList[j++]; - String slat = lonLatList[j++]; - int lon = (int)( ( Double.parseDouble(slon) + 180. ) *1000000. + 0.5); - int lat = (int)( ( Double.parseDouble(slat) + 90. ) *1000000. + 0.5); - polygon.addVertex(lon, lat); - } - - String nogoWeight = "NaN"; - if (j < lonLatList.length) { - nogoWeight = lonLatList[j]; - } - polygon.nogoWeight = Double.parseDouble( nogoWeight ); - - if ( polygon.points.size() > 0 ) - { - polygon.calcBoundingCircle(); - result.add(polygon); - } - } - } - } - } -} +package btools.server.request; + +import btools.mapaccess.OsmNode; +import btools.router.OsmNodeNamed; +import btools.router.OsmNogoPolygon; +import btools.router.OsmTrack; +import btools.router.RoutingContext; +import btools.server.ServiceContext; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * URL query parameter handler for web and standalone server. Supports all + * BRouter features without restrictions. + *

+ * Parameters: + *

+ * lonlats = lon,lat|... (unlimited list of lon,lat waypoints separated by |) + * nogos = lon,lat,radius|... (optional, radius in meters) + * profile = profile file name without .brf + * alternativeidx = [0|1|2|3] (optional, default 0) + * format = [kml|gpx|geojson] (optional, default gpx) + * trackname = name used for filename and format specific trackname (optional, default brouter) + * exportWaypoints = 1 to export them (optional, default is no export) + * pois = lon,lat,name|... (optional) + *

+ * Example URLs: + * {@code http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx} + * {@code http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml&trackname=Ride&pois=1.1,2.1,Barner Bar} + */ +public class ServerHandler extends RequestHandler { + + private RoutingContext rc; + + public ServerHandler(ServiceContext serviceContext, HashMap params) { + super(serviceContext, params); + } + + @Override + public RoutingContext readRoutingContext() { + rc = new RoutingContext(); + rc.memoryclass = 128; + + String profile = params.get("profile"); + // when custom profile replace prefix with directory path + if (profile.startsWith(ProfileUploadHandler.CUSTOM_PREFIX)) { + String customProfile = profile.substring(ProfileUploadHandler.CUSTOM_PREFIX.length()); + profile = new File(serviceContext.customProfileDir, customProfile).getPath(); + } else if (profile.startsWith(ProfileUploadHandler.SHARED_PREFIX)) { + String customProfile = profile.substring(ProfileUploadHandler.SHARED_PREFIX.length()); + profile = new File(serviceContext.sharedProfileDir, customProfile).getPath(); + } + rc.localFunction = profile; + + rc.setAlternativeIdx(Integer.parseInt(params.get("alternativeidx"))); + + List poisList = readPoisList(); + rc.poipoints = poisList; + + List nogoList = readNogoList(); + List nogoPolygonsList = readNogoPolygons(); + + if (nogoList != null) { + RoutingContext.prepareNogoPoints(nogoList); + rc.nogopoints = nogoList; + } + + if (rc.nogopoints == null) { + rc.nogopoints = nogoPolygonsList; + } else if (nogoPolygonsList != null) { + rc.nogopoints.addAll(nogoPolygonsList); + } + + return rc; + } + + @Override + public List readWayPointList() { + // lon,lat|... + String lonLats = params.get("lonlats"); + if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set"); + + String[] coords = lonLats.split("\\|"); + if (coords.length < 2) + throw new IllegalArgumentException("we need two lat/lon points at least!"); + + List wplist = new ArrayList(); + for (int i = 0; i < coords.length; i++) { + String[] lonLat = coords[i].split(","); + if (lonLat.length < 2) + throw new IllegalArgumentException("we need two lat/lon points at least!"); + wplist.add(readPosition(lonLat[0], lonLat[1], "via" + i)); + } + + wplist.get(0).name = "from"; + wplist.get(wplist.size() - 1).name = "to"; + + return wplist; + } + + @Override + public String formatTrack(OsmTrack track) { + String result; + // optional, may be null + String format = params.get("format"); + String trackName = getTrackName(); + if (trackName != null) { + track.name = trackName; + } + String exportWaypointsStr = params.get("exportWaypoints"); + if (exportWaypointsStr != null && Integer.parseInt(exportWaypointsStr) != 0) { + track.exportWaypoints = true; + } + + if (format == null || "gpx".equals(format)) { + result = track.formatAsGpx(); + } else if ("kml".equals(format)) { + result = track.formatAsKml(); + } else if ("geojson".equals(format)) { + result = track.formatAsGeoJson(); + } else if ("csv".equals(format)) { + try { + StringWriter sw = new StringWriter(); + BufferedWriter bw = new BufferedWriter(sw); + track.writeMessages(bw, rc); + return sw.toString(); + } catch (Exception ex) { + return "Error: " + ex.getMessage(); + } + } else { + System.out.println("unknown track format '" + format + "', using default"); + result = track.formatAsGpx(); + } + + return result; + } + + @Override + public String getMimeType() { + // default + String result = "text/plain"; + + // optional, may be null + String format = params.get("format"); + if (format != null) { + if ("gpx".equals(format)) { + result = "application/gpx+xml"; + } else if ("kml".equals(format)) { + result = "application/vnd.google-earth.kml+xml"; + } else if ("geojson".equals(format)) { + result = "application/vnd.geo+json"; + } else if ("csv".equals(format)) { + result = "text/tab-separated-values"; + } + } + + return result; + } + + @Override + public String getFileName() { + String fileName = null; + String format = params.get("format"); + String trackName = getTrackName(); + + if (format != null) { + fileName = (trackName == null ? "brouter" : trackName) + "." + format; + } + + return fileName; + } + + private String getTrackName() { + return params.get("trackname") == null ? null : params.get("trackname").replaceAll("[^a-zA-Z0-9 \\._\\-]+", ""); + } + + private static OsmNodeNamed readPosition(String vlon, String vlat, String name) { + if (vlon == null) throw new IllegalArgumentException("lon " + name + " not found in input"); + if (vlat == null) throw new IllegalArgumentException("lat " + name + " not found in input"); + + return readPosition(Double.parseDouble(vlon), Double.parseDouble(vlat), name); + } + + private static OsmNodeNamed readPosition(double lon, double lat, String name) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = name; + n.ilon = (int) ((lon + 180.) * 1000000. + 0.5); + n.ilat = (int) ((lat + 90.) * 1000000. + 0.5); + return n; + } + + private List readPoisList() { + // lon,lat,name|... + String pois = params.get("pois"); + if (pois == null) return null; + + String[] lonLatNameList = pois.split("\\|"); + + List poisList = new ArrayList(); + for (int i = 0; i < lonLatNameList.length; i++) { + String[] lonLatName = lonLatNameList[i].split(","); + + if (lonLatName.length != 3) + continue; + + OsmNodeNamed n = new OsmNodeNamed(); + n.ilon = (int) ((Double.parseDouble(lonLatName[0]) + 180.) * 1000000. + 0.5); + n.ilat = (int) ((Double.parseDouble(lonLatName[1]) + 90.) * 1000000. + 0.5); + n.name = lonLatName[2]; + poisList.add(n); + } + + return poisList; + } + + private List readNogoList() { + // lon,lat,radius|... + String nogos = params.get("nogos"); + if (nogos == null) return null; + + String[] lonLatRadList = nogos.split("\\|"); + + List nogoList = new ArrayList(); + for (int i = 0; i < lonLatRadList.length; i++) { + String[] lonLatRad = lonLatRadList[i].split(","); + String nogoWeight = "NaN"; + if (lonLatRad.length > 3) { + nogoWeight = lonLatRad[3]; + } + nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight)); + } + + return nogoList; + } + + private static OsmNodeNamed readNogo(String lon, String lat, String radius, String nogoWeight) { + double weight = "undefined".equals(nogoWeight) ? Double.NaN : Double.parseDouble(nogoWeight); + return readNogo(Double.parseDouble(lon), Double.parseDouble(lat), Integer.parseInt(radius), weight); + } + + private static OsmNodeNamed readNogo(double lon, double lat, int radius, double nogoWeight) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = "nogo" + radius; + n.ilon = (int) ((lon + 180.) * 1000000. + 0.5); + n.ilat = (int) ((lat + 90.) * 1000000. + 0.5); + n.isNogo = true; + n.nogoWeight = nogoWeight; + return n; + } + + private List readNogoPolygons() { + List result = new ArrayList(); + parseNogoPolygons(params.get("polylines"), result, false); + parseNogoPolygons(params.get("polygons"), result, true); + return result.size() > 0 ? result : null; + } + + private static void parseNogoPolygons(String polygons, List result, boolean closed) { + if (polygons != null) { + String[] polygonList = polygons.split("\\|"); + for (int i = 0; i < polygonList.length; i++) { + String[] lonLatList = polygonList[i].split(","); + if (lonLatList.length > 1) { + OsmNogoPolygon polygon = new OsmNogoPolygon(closed); + int j; + for (j = 0; j < 2 * (lonLatList.length / 2) - 1; ) { + String slon = lonLatList[j++]; + String slat = lonLatList[j++]; + int lon = (int) ((Double.parseDouble(slon) + 180.) * 1000000. + 0.5); + int lat = (int) ((Double.parseDouble(slat) + 90.) * 1000000. + 0.5); + polygon.addVertex(lon, lat); + } + + String nogoWeight = "NaN"; + if (j < lonLatList.length) { + nogoWeight = lonLatList[j]; + } + polygon.nogoWeight = Double.parseDouble(nogoWeight); + + if (polygon.points.size() > 0) { + polygon.calcBoundingCircle(); + result.add(polygon); + } + } + } + } + } +} diff --git a/brouter-server/src/test/java/btools/server/IntegrityCheckTest.java b/brouter-server/src/test/java/btools/server/IntegrityCheckTest.java index 19e9237..cdae8d3 100644 --- a/brouter-server/src/test/java/btools/server/IntegrityCheckTest.java +++ b/brouter-server/src/test/java/btools/server/IntegrityCheckTest.java @@ -8,24 +8,21 @@ import org.junit.Test; import btools.mapaccess.PhysicalFile; -public class IntegrityCheckTest -{ +public class IntegrityCheckTest { private File workingDir; @Test - public void integrityTest() throws Exception - { - URL resulturl = this.getClass().getResource( "/testtrack0.gpx" ); - Assert.assertTrue( "reference result not found: ", resulturl != null ); - File resultfile = new File( resulturl.getFile() ); + public void integrityTest() throws Exception { + URL resulturl = this.getClass().getResource("/testtrack0.gpx"); + Assert.assertTrue("reference result not found: ", resulturl != null); + File resultfile = new File(resulturl.getFile()); workingDir = resultfile.getParentFile(); - File segmentDir = new File( workingDir, "/../../../../brouter-map-creator/build/resources/test/tmp/segments" ); + File segmentDir = new File(workingDir, "/../../../../brouter-map-creator/build/resources/test/tmp/segments"); File[] files = segmentDir.listFiles(); - for ( File f : files ) - { - PhysicalFile.checkFileIntegrity( f ); + for (File f : files) { + PhysicalFile.checkFileIntegrity(f); } } diff --git a/brouter-server/src/test/java/btools/server/RouterTest.java b/brouter-server/src/test/java/btools/server/RouterTest.java index 334e8bf..210e033 100644 --- a/brouter-server/src/test/java/btools/server/RouterTest.java +++ b/brouter-server/src/test/java/btools/server/RouterTest.java @@ -4,75 +4,73 @@ import java.util.*; import org.junit.Assert; import org.junit.Test; + import java.net.URL; import java.io.File; import btools.router.*; import btools.mapaccess.*; -public class RouterTest -{ +public class RouterTest { private File workingDir; @Test - public void routerTest() throws Exception - { - URL resulturl = this.getClass().getResource( "/testtrack0.gpx" ); - Assert.assertTrue( "reference result not found: ", resulturl != null ); + public void routerTest() throws Exception { + URL resulturl = this.getClass().getResource("/testtrack0.gpx"); + Assert.assertTrue("reference result not found: ", resulturl != null); File resultfile = new File(resulturl.getFile()); workingDir = resultfile.getParentFile(); String msg; // first test: route within dreiech test-map crossing tile border - - msg = calcRoute( 8.720897, 50.002515, 8.723658, 49.997510, "testtrack" ); + + msg = calcRoute(8.720897, 50.002515, 8.723658, 49.997510, "testtrack"); // error message from router? - Assert.assertTrue( "routing failed: " + msg, msg == null ); + Assert.assertTrue("routing failed: " + msg, msg == null); // if the track didn't change, we expect the first alternative also - File a1 = new File( workingDir, "testtrack1.gpx" ); - Assert.assertTrue( "result content missmatch", a1.exists() ); + File a1 = new File(workingDir, "testtrack1.gpx"); + Assert.assertTrue("result content missmatch", a1.exists()); // second test: to-point far off - msg = calcRoute( 8.720897, 50.002515, 16.723658, 49.997510, "notrack" ); + msg = calcRoute(8.720897, 50.002515, 16.723658, 49.997510, "notrack"); - Assert.assertTrue( msg, msg != null && msg.indexOf( "not found" ) >= 0 ); + Assert.assertTrue(msg, msg != null && msg.indexOf("not found") >= 0); } - private String calcRoute( double flon, double flat, double tlon, double tlat, String trackname ) throws Exception - { + private String calcRoute(double flon, double flat, double tlon, double tlat, String trackname) throws Exception { String wd = workingDir.getAbsolutePath(); List wplist = new ArrayList(); OsmNodeNamed n; n = new OsmNodeNamed(); n.name = "from"; - n.ilon = 180000000 + (int)(flon*1000000 + 0.5); - n.ilat = 90000000 + (int)(flat*1000000 + 0.5); - wplist.add( n ); + n.ilon = 180000000 + (int) (flon * 1000000 + 0.5); + n.ilat = 90000000 + (int) (flat * 1000000 + 0.5); + wplist.add(n); n = new OsmNodeNamed(); n.name = "to"; - n.ilon = 180000000 + (int)(tlon*1000000 + 0.5); - n.ilat = 90000000 + (int)(tlat*1000000 + 0.5); - wplist.add( n ); + n.ilon = 180000000 + (int) (tlon * 1000000 + 0.5); + n.ilat = 90000000 + (int) (tlat * 1000000 + 0.5); + wplist.add(n); RoutingContext rctx = new RoutingContext(); rctx.localFunction = wd + "/../../../../misc/profiles2/trekking.brf"; // c.setAlternativeIdx( 1 ); RoutingEngine re = new RoutingEngine( - wd + "/" + trackname, - wd + "/" + trackname, - new File ( wd, "/../../../../brouter-map-creator/build/resources/test/tmp/segments"), - wplist, - rctx ); + wd + "/" + trackname, + wd + "/" + trackname, + new File(wd, "/../../../../brouter-map-creator/build/resources/test/tmp/segments"), + wplist, + rctx); + + re.doRun(0); - re.doRun( 0 ); - return re.getErrorMessage(); } diff --git a/brouter-util/src/main/java/btools/util/BitCoderContext.java b/brouter-util/src/main/java/btools/util/BitCoderContext.java index 2ded5d5..7299189 100644 --- a/brouter-util/src/main/java/btools/util/BitCoderContext.java +++ b/brouter-util/src/main/java/btools/util/BitCoderContext.java @@ -1,8 +1,7 @@ package btools.util; -public class BitCoderContext -{ +public class BitCoderContext { private byte[] ab; private int idxMax; private int idx = -1; @@ -19,13 +18,11 @@ public class BitCoderContext private static final int[] bm2bits = new int[256]; - static - { + static { // fill varbits lookup table - BitCoderContext bc = new BitCoderContext( new byte[4] ); - for( int i=0; i<4096; i++ ) - { + BitCoderContext bc = new BitCoderContext(new byte[4]); + for (int i = 0; i < 4096; i++) { bc.reset(); bc.bits = 14; bc.b = 0x1000 + i; @@ -34,16 +31,14 @@ public class BitCoderContext vl_values[i] = bc.decodeVarBits2(); vl_length[i] = bc.getReadingBitPosition() - b0; } - for( int i=0; i<4096; i++ ) - { + for (int i = 0; i < 4096; i++) { bc.reset(); int b0 = bc.getWritingBitPosition(); - bc.encodeVarBits2( i ); + bc.encodeVarBits2(i); vc_values[i] = bc.b; vc_length[i] = bc.getWritingBitPosition() - b0; } - for( int i=0; i<1024; i++ ) - { + for (int i = 0; i < 1024; i++) { bc.reset(); bc.bits = 14; bc.b = 0x1000 + i; @@ -52,37 +47,31 @@ public class BitCoderContext vl_values[i] = bc.decodeVarBits2(); vl_length[i] = bc.getReadingBitPosition() - b0; } - for( int b=0; b<256; b++ ) - { + for (int b = 0; b < 256; b++) { int r = 0; - for( int i=0; i<8; i++ ) - { - if ( (b & (1< range) - { - encodeBit( false ); + while (value > range) { + encodeBit(false); value -= range + 1; range = 2 * range + 1; } - encodeBit( true ); - encodeBounded( range, value ); + encodeBit(true); + encodeBounded(range, value); } - public final void encodeVarBits( int value ) - { - if ( (value & 0xfff) == value ) - { + public final void encodeVarBits(int value) { + if ((value & 0xfff) == value) { flushBuffer(); b |= vc_values[value] << bits; - bits += vc_length[value]; - } - else - { - encodeVarBits2( value ); // slow fallback for large values + bits += vc_length[value]; + } else { + encodeVarBits2(value); // slow fallback for large values } } /** * @see #encodeVarBits */ - public final int decodeVarBits2() - { + public final int decodeVarBits2() { int range = 0; - while (!decodeBit()) - { + while (!decodeBit()) { range = 2 * range + 1; } - return range + decodeBounded( range ); + return range + decodeBounded(range); } - public final int decodeVarBits() - { + public final int decodeVarBits() { fillBuffer(); int b12 = b & 0xfff; int len = vl_length[b12]; - if ( len <= 12 ) - { + if (len <= 12) { b >>>= len; bits -= len; return vl_values[b12]; // full value lookup } - if ( len <= 23 ) // // only length lookup + if (len <= 23) // // only length lookup { int len2 = len >> 1; - b >>>= (len2+1); - int mask = 0xffffffff >>> ( 32 - len2 ); + b >>>= (len2 + 1); + int mask = 0xffffffff >>> (32 - len2); mask += b & mask; b >>>= len2; bits -= len; return mask; } - if ( (b & 0xffffff) != 0 ) - { + if ((b & 0xffffff) != 0) { // here we just know len in [25..47] // ( fillBuffer guarantees only 24 bits! ) b >>>= 12; - int len3 = 1 + (vl_length[b & 0xfff]>>1); + int len3 = 1 + (vl_length[b & 0xfff] >> 1); b >>>= len3; int len2 = 11 + len3; - bits -= len2+1; + bits -= len2 + 1; fillBuffer(); - int mask = 0xffffffff >>> ( 32 - len2 ); + int mask = 0xffffffff >>> (32 - len2); mask += b & mask; b >>>= len2; bits -= len2; @@ -179,29 +157,24 @@ public class BitCoderContext } - public final void encodeBit( boolean value ) - { - if (bits > 31) - { - ab[++idx] = (byte)(b & 0xff); + public final void encodeBit(boolean value) { + if (bits > 31) { + ab[++idx] = (byte) (b & 0xff); b >>>= 8; - bits -=8; + bits -= 8; } - if ( value ) - { + if (value) { b |= 1 << bits; } bits++; } - public final boolean decodeBit() - { - if ( bits == 0 ) - { + public final boolean decodeBit() { + if (bits == 0) { bits = 8; b = ab[++idx] & 0xff; } - boolean value = ( ( b & 1 ) != 0 ); + boolean value = ((b & 1) != 0); b >>>= 1; bits--; return value; @@ -213,19 +186,14 @@ public class BitCoderContext * this is variable length encoding, with the shorter codes * for the central value range */ - public final void encodeBounded( int max, int value ) - { + public final void encodeBounded(int max, int value) { int im = 1; // integer mask - while (im <= max) - { - if ( ( value & im ) != 0 ) - { - encodeBit( true ); + while (im <= max) { + if ((value & im) != 0) { + encodeBit(true); max -= im; - } - else - { - encodeBit( false ); + } else { + encodeBit(false); } im <<= 1; } @@ -233,20 +201,18 @@ public class BitCoderContext /** * decode an integer in the range 0..max (inclusive). + * * @see #encodeBounded */ - public final int decodeBounded( int max ) - { + public final int decodeBounded(int max) { int value = 0; int im = 1; // integer mask - while (( value | im ) <= max) - { - if ( bits == 0 ) - { + while ((value | im) <= max) { + if (bits == 0) { bits = 8; b = ab[++idx] & 0xff; } - if ( ( b & 1 ) != 0 ) + if ((b & 1) != 0) value |= im; b >>>= 1; bits--; @@ -255,67 +221,57 @@ public class BitCoderContext return value; } - public final int decodeBits( int count ) - { + public final int decodeBits(int count) { fillBuffer(); - int mask = 0xffffffff >>> ( 32 - count ); + int mask = 0xffffffff >>> (32 - count); int value = b & mask; b >>>= count; bits -= count; return value; } - public final int decodeBitsReverse( int count ) - { + public final int decodeBitsReverse(int count) { fillBuffer(); int value = 0; - while( count > 8 ) - { - value = (value << 8) | reverse_byte[ b & 0xff ]; - b >>=8; - count -=8; - bits -=8; + while (count > 8) { + value = (value << 8) | reverse_byte[b & 0xff]; + b >>= 8; + count -= 8; + bits -= 8; fillBuffer(); } - value = (value << count) | reverse_byte[ b & 0xff ] >> (8-count); + value = (value << count) | reverse_byte[b & 0xff] >> (8 - count); bits -= count; b >>= count; return value; } - private void fillBuffer() - { - while (bits < 24) - { - if ( idx++ < idxMax ) - { + private void fillBuffer() { + while (bits < 24) { + if (idx++ < idxMax) { b |= (ab[idx] & 0xff) << bits; } bits += 8; } } - private void flushBuffer() - { - while (bits > 7) - { - ab[++idx] = (byte)(b & 0xff); + private void flushBuffer() { + while (bits > 7) { + ab[++idx] = (byte) (b & 0xff); b >>>= 8; - bits -=8; + bits -= 8; } } /** - * flushes and closes the (write-mode) context - * + * flushes and closes the (write-mode) context + * * @return the encoded length in bytes */ - public final int closeAndGetEncodedLength() - { + public final int closeAndGetEncodedLength() { flushBuffer(); - if ( bits > 0 ) - { - ab[++idx] = (byte)(b & 0xff); + if (bits > 0) { + ab[++idx] = (byte) (b & 0xff); } return idx + 1; } @@ -323,51 +279,43 @@ public class BitCoderContext /** * @return the encoded length in bits */ - public final int getWritingBitPosition() - { + public final int getWritingBitPosition() { return (idx << 3) + 8 + bits; } - public final int getReadingBitPosition() - { + public final int getReadingBitPosition() { return (idx << 3) + 8 - bits; } - public final void setReadingBitPosition(int pos) - { + public final void setReadingBitPosition(int pos) { idx = pos >>> 3; bits = (idx << 3) + 8 - pos; b = ab[idx] & 0xff; - b >>>= (8-bits); + b >>>= (8 - bits); } - public static void main( String[] args ) - { + public static void main(String[] args) { byte[] ab = new byte[581969]; - BitCoderContext ctx = new BitCoderContext( ab ); - for ( int i = 0; i < 31; i++ ) - { - ctx.encodeVarBits( (1<> 1 : -(v >> 1 ); + public final int readVarLengthSigned() { + int v = readVarLengthUnsigned(); + return (v & 1) == 0 ? v >> 1 : -(v >> 1); } - public final int readVarLengthUnsigned() - { + public final int readVarLengthUnsigned() { byte b; - int v = (b=ab[aboffset++]) & 0x7f; - if ( b >= 0 ) return v; - v |= ( (b=ab[aboffset++]) & 0x7f ) << 7; - if ( b >= 0 ) return v; - v |= ( (b=ab[aboffset++]) & 0x7f ) << 14; - if ( b >= 0 ) return v; - v |= ( (b=ab[aboffset++]) & 0x7f ) << 21; - if ( b >= 0 ) return v; - v |= ( (b=ab[aboffset++]) & 0xf ) << 28; + int v = (b = ab[aboffset++]) & 0x7f; + if (b >= 0) return v; + v |= ((b = ab[aboffset++]) & 0x7f) << 7; + if (b >= 0) return v; + v |= ((b = ab[aboffset++]) & 0x7f) << 14; + if (b >= 0) return v; + v |= ((b = ab[aboffset++]) & 0x7f) << 21; + if (b >= 0) return v; + v |= ((b = ab[aboffset++]) & 0xf) << 28; return v; } - public final void readFully( byte[] ta ) - { - System.arraycopy( ab, aboffset, ta, 0, ta.length ); - aboffset += ta.length; + public final void readFully(byte[] ta) { + System.arraycopy(ab, aboffset, ta, 0, ta.length); + aboffset += ta.length; } - public final boolean hasMoreData() - { + public final boolean hasMoreData() { return aboffset < aboffsetEnd; } @Override - public String toString() - { - StringBuilder sb = new StringBuilder( "[" ); - for( int i=0; i> 24) & 0xff ); - ab[aboffset++] = (byte)( (v >> 16) & 0xff ); - ab[aboffset++] = (byte)( (v >> 8) & 0xff ); - ab[aboffset++] = (byte)( (v ) & 0xff ); + public final void writeInt(int v) { + ab[aboffset++] = (byte) ((v >> 24) & 0xff); + ab[aboffset++] = (byte) ((v >> 16) & 0xff); + ab[aboffset++] = (byte) ((v >> 8) & 0xff); + ab[aboffset++] = (byte) ((v) & 0xff); } - public final void writeLong( long v ) - { - ab[aboffset++] = (byte)( (v >> 56) & 0xff ); - ab[aboffset++] = (byte)( (v >> 48) & 0xff ); - ab[aboffset++] = (byte)( (v >> 40) & 0xff ); - ab[aboffset++] = (byte)( (v >> 32) & 0xff ); - ab[aboffset++] = (byte)( (v >> 24) & 0xff ); - ab[aboffset++] = (byte)( (v >> 16) & 0xff ); - ab[aboffset++] = (byte)( (v >> 8) & 0xff ); - ab[aboffset++] = (byte)( (v ) & 0xff ); + public final void writeLong(long v) { + ab[aboffset++] = (byte) ((v >> 56) & 0xff); + ab[aboffset++] = (byte) ((v >> 48) & 0xff); + ab[aboffset++] = (byte) ((v >> 40) & 0xff); + ab[aboffset++] = (byte) ((v >> 32) & 0xff); + ab[aboffset++] = (byte) ((v >> 24) & 0xff); + ab[aboffset++] = (byte) ((v >> 16) & 0xff); + ab[aboffset++] = (byte) ((v >> 8) & 0xff); + ab[aboffset++] = (byte) ((v) & 0xff); } - public final void writeBoolean( boolean v) - { - ab[aboffset++] = (byte)( v ? 1 : 0 ); + public final void writeBoolean(boolean v) { + ab[aboffset++] = (byte) (v ? 1 : 0); } - public final void writeByte( int v ) - { - ab[aboffset++] = (byte)( (v ) & 0xff ); + public final void writeByte(int v) { + ab[aboffset++] = (byte) ((v) & 0xff); } - public final void writeShort( int v ) - { - ab[aboffset++] = (byte)( (v >> 8) & 0xff ); - ab[aboffset++] = (byte)( (v ) & 0xff ); + public final void writeShort(int v) { + ab[aboffset++] = (byte) ((v >> 8) & 0xff); + ab[aboffset++] = (byte) ((v) & 0xff); } - public final void write( byte[] sa ) - { - System.arraycopy( sa, 0, ab, aboffset, sa.length ); + public final void write(byte[] sa) { + System.arraycopy(sa, 0, ab, aboffset, sa.length); aboffset += sa.length; } - public final void write( byte[] sa, int offset, int len ) - { - System.arraycopy( sa, offset, ab, aboffset, len ); + public final void write(byte[] sa, int offset, int len) { + System.arraycopy(sa, offset, ab, aboffset, len); aboffset += len; } - public final void writeVarBytes( byte[] sa ) - { - if ( sa == null ) - { - writeVarLengthUnsigned( 0 ); - } - else - { + public final void writeVarBytes(byte[] sa) { + if (sa == null) { + writeVarLengthUnsigned(0); + } else { int len = sa.length; - writeVarLengthUnsigned( len ); - write( sa, 0, len ); + writeVarLengthUnsigned(len); + write(sa, 0, len); } } - public final void writeModeAndDesc( boolean isReverse, byte[] sa ) - { + public final void writeModeAndDesc(boolean isReverse, byte[] sa) { int len = sa == null ? 0 : sa.length; - int sizecode = len << 1 | ( isReverse ? 1 : 0 ); - writeVarLengthUnsigned( sizecode ); - if ( len > 0 ) - { - write( sa, 0, len ); + int sizecode = len << 1 | (isReverse ? 1 : 0); + writeVarLengthUnsigned(sizecode); + if (len > 0) { + write(sa, 0, len); } } - public final byte[] toByteArray() - { + public final byte[] toByteArray() { byte[] c = new byte[aboffset]; - System.arraycopy( ab, 0, c, 0, aboffset ); + System.arraycopy(ab, 0, c, 0, aboffset); return c; } @@ -102,76 +86,66 @@ public class ByteDataWriter extends ByteDataReader * * @return the offset of the placeholder */ - public final int writeSizePlaceHolder() - { + public final int writeSizePlaceHolder() { return aboffset++; } - public final void injectSize( int sizeoffset ) - { + public final void injectSize(int sizeoffset) { int size = 0; - int datasize = aboffset-sizeoffset-1; + int datasize = aboffset - sizeoffset - 1; int v = datasize; - do - { + do { v >>= 7; size++; } - while( v != 0 ); - if ( size > 1 ) // doesn't fit -> shift the data after the placeholder + while (v != 0); + if (size > 1) // doesn't fit -> shift the data after the placeholder { - System.arraycopy( ab, sizeoffset+1, ab, sizeoffset+size, datasize ); + System.arraycopy(ab, sizeoffset + 1, ab, sizeoffset + size, datasize); } aboffset = sizeoffset; - writeVarLengthUnsigned( datasize ); + writeVarLengthUnsigned(datasize); aboffset = sizeoffset + size + datasize; } - public final void writeVarLengthSigned( int v ) - { - writeVarLengthUnsigned( v < 0 ? ( (-v) << 1 ) | 1 : v << 1 ); + public final void writeVarLengthSigned(int v) { + writeVarLengthUnsigned(v < 0 ? ((-v) << 1) | 1 : v << 1); } - public final void writeVarLengthUnsigned( int v ) - { + public final void writeVarLengthUnsigned(int v) { int i7 = v & 0x7f; - if ( ( v >>>= 7 ) == 0 ) - { - ab[aboffset++] = (byte) ( i7 ); + if ((v >>>= 7) == 0) { + ab[aboffset++] = (byte) (i7); return; } - ab[aboffset++] = (byte) ( i7 | 0x80 ); + ab[aboffset++] = (byte) (i7 | 0x80); i7 = v & 0x7f; - if ( ( v >>>= 7 ) == 0 ) - { - ab[aboffset++] = (byte) ( i7 ); + if ((v >>>= 7) == 0) { + ab[aboffset++] = (byte) (i7); return; } - ab[aboffset++] = (byte) ( i7 | 0x80 ); + ab[aboffset++] = (byte) (i7 | 0x80); i7 = v & 0x7f; - if ( ( v >>>= 7 ) == 0 ) - { - ab[aboffset++] = (byte) ( i7 ); + if ((v >>>= 7) == 0) { + ab[aboffset++] = (byte) (i7); return; } - ab[aboffset++] = (byte) ( i7 | 0x80 ); + ab[aboffset++] = (byte) (i7 | 0x80); i7 = v & 0x7f; - if ( ( v >>>= 7 ) == 0 ) - { - ab[aboffset++] = (byte) ( i7 ); + if ((v >>>= 7) == 0) { + ab[aboffset++] = (byte) (i7); return; } - ab[aboffset++] = (byte) ( i7 | 0x80 ); + ab[aboffset++] = (byte) (i7 | 0x80); - ab[aboffset++] = (byte) ( v ); + ab[aboffset++] = (byte) (v); return; } - public int size() - { + public int size() { return aboffset; } diff --git a/brouter-util/src/main/java/btools/util/CheapAngleMeter.java b/brouter-util/src/main/java/btools/util/CheapAngleMeter.java index 7fdad3b..88a4668 100644 --- a/brouter-util/src/main/java/btools/util/CheapAngleMeter.java +++ b/brouter-util/src/main/java/btools/util/CheapAngleMeter.java @@ -4,18 +4,15 @@ */ package btools.util; -public final class CheapAngleMeter -{ +public final class CheapAngleMeter { private double cosangle; - public double getCosAngle() - { + public double getCosAngle() { return cosangle; } - public double calcAngle( int lon0, int lat0, int lon1, int lat1, int lon2, int lat2 ) - { - double[] lonlat2m = CheapRuler.getLonLatToMeterScales( lat1 ); + public double calcAngle(int lon0, int lat0, int lon1, int lat1, int lon2, int lat2) { + double[] lonlat2m = CheapRuler.getLonLatToMeterScales(lat1); double lon2m = lonlat2m[0]; double lat2m = lonlat2m[1]; double dx10 = (lon1 - lon0) * lon2m; @@ -23,33 +20,30 @@ public final class CheapAngleMeter double dx21 = (lon2 - lon1) * lon2m; double dy21 = (lat2 - lat1) * lat2m; - double dd = Math.sqrt( (dx10*dx10 + dy10*dy10)*(dx21*dx21 + dy21*dy21) ); - if ( dd == 0. ) { cosangle = 1.; return 0.; } - double sinp = (dy10*dx21 - dx10*dy21)/dd; - double cosp = (dy10*dy21 + dx10*dx21)/dd; + double dd = Math.sqrt((dx10 * dx10 + dy10 * dy10) * (dx21 * dx21 + dy21 * dy21)); + if (dd == 0.) { + cosangle = 1.; + return 0.; + } + double sinp = (dy10 * dx21 - dx10 * dy21) / dd; + double cosp = (dy10 * dy21 + dx10 * dx21) / dd; cosangle = cosp; double offset = 0.; - double s2 = sinp*sinp; - if ( s2 > 0.5 ) - { - if ( sinp > 0. ) - { + double s2 = sinp * sinp; + if (s2 > 0.5) { + if (sinp > 0.) { offset = 90.; sinp = -cosp; - } - else - { + } else { offset = -90.; sinp = cosp; } - s2 = cosp*cosp; - } - else if ( cosp < 0. ) - { + s2 = cosp * cosp; + } else if (cosp < 0.) { sinp = -sinp; offset = sinp > 0. ? -180. : 180.; } - return offset + sinp * ( 57.4539 + s2 * ( 9.57565 + s2 * ( 4.30904 + s2 * 2.56491 ) ) ); + return offset + sinp * (57.4539 + s2 * (9.57565 + s2 * (4.30904 + s2 * 2.56491))); } } diff --git a/brouter-util/src/main/java/btools/util/CheapRuler.java b/brouter-util/src/main/java/btools/util/CheapRuler.java index 8daa84f..7a7dc8f 100644 --- a/brouter-util/src/main/java/btools/util/CheapRuler.java +++ b/brouter-util/src/main/java/btools/util/CheapRuler.java @@ -6,9 +6,9 @@ public final class CheapRuler { * See * https://blog.mapbox.com/fast-geodesic-approximations-with-cheap-ruler-106f229ad016 * for more details. - * + *

* Original code is at https://github.com/mapbox/cheap-ruler under ISC license. - * + *

* This is implemented as a Singleton to have a unique cache for the cosine * values across all the code. */ @@ -28,12 +28,12 @@ public final class CheapRuler { */ static { for (int i = 0; i < SCALE_CACHE_LENGTH; i++) { - SCALE_CACHE[i] = calcKxKyFromILat( i*SCALE_CACHE_INCREMENT + SCALE_CACHE_INCREMENT/2 ); + SCALE_CACHE[i] = calcKxKyFromILat(i * SCALE_CACHE_INCREMENT + SCALE_CACHE_INCREMENT / 2); } } private static double[] calcKxKyFromILat(int ilat) { - double lat = DEG_TO_RAD*(ilat*ILATLNG_TO_LATLNG - 90); + double lat = DEG_TO_RAD * (ilat * ILATLNG_TO_LATLNG - 90); double cos = Math.cos(lat); double cos2 = 2 * cos * cos - 1; double cos3 = 2 * cos * cos2 - cos; @@ -53,26 +53,26 @@ public final class CheapRuler { * * @return [lon->meter,lat->meter] */ - public static double[] getLonLatToMeterScales( int ilat ) { - return SCALE_CACHE[ ilat / SCALE_CACHE_INCREMENT ]; + public static double[] getLonLatToMeterScales(int ilat) { + return SCALE_CACHE[ilat / SCALE_CACHE_INCREMENT]; } /** * Compute the distance (in meters) between two points represented by their * (integer) latitude and longitude. * - * @param ilon1 Integer longitude for the start point. this is (longitude in degrees + 180) * 1e6. - * @param ilat1 Integer latitude for the start point, this is (latitude + 90) * 1e6. - * @param ilon2 Integer longitude for the end point, this is (longitude + 180) * 1e6. - * @param ilat2 Integer latitude for the end point, this is (latitude + 90) * 1e6. - * @return The distance between the two points, in meters. - * + * @param ilon1 Integer longitude for the start point. this is (longitude in degrees + 180) * 1e6. + * @param ilat1 Integer latitude for the start point, this is (latitude + 90) * 1e6. + * @param ilon2 Integer longitude for the end point, this is (longitude + 180) * 1e6. + * @param ilat2 Integer latitude for the end point, this is (latitude + 90) * 1e6. + * @return The distance between the two points, in meters. + *

* Note: * Integer longitude is ((longitude in degrees) + 180) * 1e6. * Integer latitude is ((latitude in degrees) + 90) * 1e6. */ public static double distance(int ilon1, int ilat1, int ilon2, int ilat2) { - double[] kxky = getLonLatToMeterScales( ( ilat1 + ilat2 ) >> 1 ); + double[] kxky = getLonLatToMeterScales((ilat1 + ilat2) >> 1); double dlon = (ilon1 - ilon2) * kxky[0]; double dlat = (ilat1 - ilat2) * kxky[1]; return Math.sqrt(dlat * dlat + dlon * dlon); // in m diff --git a/brouter-util/src/main/java/btools/util/CompactLongMap.java b/brouter-util/src/main/java/btools/util/CompactLongMap.java index 80943c5..a16e129 100644 --- a/brouter-util/src/main/java/btools/util/CompactLongMap.java +++ b/brouter-util/src/main/java/btools/util/CompactLongMap.java @@ -1,328 +1,285 @@ -package btools.util; - -import java.util.ArrayList; - -/** - * Memory efficient Map to map a long-key to an object-value - * - * Implementation is such that basically the 12 bytes - * per entry is allocated that's needed to store - * a long- and an object-value. - * This class does not implement the Map interface - * because it's not complete (remove() is not implemented, - * CompactLongMap can only grow.) - * - * @author ab - */ -public class CompactLongMap -{ - private long[][] al; - private int[] pa; - private int size = 0; - private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays - - protected V value_in; - protected V value_out; - - protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE - private static boolean earlyDuplicateCheck; - - public CompactLongMap() - { - // pointer array - pa = new int[MAXLISTS]; - - // allocate key lists - al = new long[MAXLISTS][]; - al[0] = new long[1]; // make the first array (the transient buffer) - - // same for the values - vla = new Object[MAXLISTS][]; - vla[0] = new Object[1]; - - earlyDuplicateCheck = Boolean.getBoolean( "earlyDuplicateCheck" ); - } - - - /* - * - * The Map extension: - * next 5 protected methods are needed to implement value-support - * overwrite them all to support value structures other than the - * long-values implemented here as a sample. - * - * Furthermore, put() and get() method need to be implemented - * to access the values. - * - * Note that this map does not behave exactly like java.util.Map - * - put(..) with already existing key throws exception - * - get(..) with non-existing key thros exception - * - * If you have keys that cannot easily be mapped on long's, use - * a hash-function to do the mapping. But note that, in comparison - * to java.util.HashMap, in that case the keys itself are not saved, - * only the hash-values, so you need to be sure that random duplicate - * hashs are either excluded by the structure of your data or that - * you can handle the possible IllegalArgumentException - * - */ - - private Object[][] vla; // value list array - - - public boolean put( long id, V value ) - { - try - { - value_in = value; - if ( contains( id, true ) ) - { - return true; - } - vla[0][0] = value; - _add( id ); - return false; - } - finally - { - value_in = null; - value_out = null; - } - } - - /** - * Same as put( id, value ) but duplicate check - * is skipped for performance. Be aware that you - * can get a duplicate exception later on if the - * map is restructured! - * with System parameter earlyDuplicateCheck=true you - * can enforce the early duplicate check for debugging - * - * @param id the key to insert - * @param value the value to insert object - * @exception IllegalArgumentException for duplicates if enabled - */ - public void fastPut( long id, V value ) - { - if ( earlyDuplicateCheck && contains( id ) ) - { - throw new IllegalArgumentException( "duplicate key found in early check: " + id ); - } - vla[0][0] = value; - _add( id ); - } - - /** - * Get the value for the given id - * @param id the key to query - * @return the object, or null if id not known - */ - public V get( long id ) - { - try - { - if ( contains( id, false ) ) - { - return value_out; - } - return null; - } - finally - { - value_out = null; - } - } - - - /** - * @return the number of entries in this map - */ - public int size() - { - return size; - } - - - private boolean _add( long id ) - { - if ( size == Integer.MAX_VALUE ) - { - throw new IllegalArgumentException( "cannot grow beyond size Integer.MAX_VALUE" ); - } - - // put the new entry in the first array - al[0][0] = id; - - // determine the first empty array - int bp = size++; // treat size as bitpattern - int idx = 1; - int n = 1; - - pa[0] = 1; - pa[1] = 1; - - while ( (bp&1) == 1 ) - { - bp >>= 1; - pa[idx++] = n; - n <<= 1; - } - - // create it if not existant - if ( al[idx] == null ) - { - al[idx] = new long[n]; - vla[idx] = new Object[n]; - } - - // now merge the contents of arrays 0...idx-1 into idx - while ( n > 0 ) - { - long maxId = 0; - int maxIdx = -1; - - for ( int i=0; i 0 ) - { - long currentId = al[i][p-1]; - if ( maxIdx < 0 || currentId > maxId ) - { - maxIdx = i; - maxId = currentId; - } - } - } - - // current maximum found, copy to target array - if ( n < al[idx].length && maxId == al[idx][n] ) - { - throw new IllegalArgumentException( "duplicate key found in late check: " + maxId ); - } - --n; - al[idx][n] = maxId; - vla[idx][n] = vla[maxIdx][pa[maxIdx]-1]; - - --pa[maxIdx]; - } - - // de-allocate empty arrays of a certain size (fix at 64kByte) - while ( idx-- > _maxKeepExponent ) - { - al[idx] = null; - vla[idx] = null; - } - - return false; - } - - /** - * @return true if "id" is contained in this set. - */ - public boolean contains( long id ) - { - try - { - return contains( id, false ); - } - finally - { - value_out = null; - } - } - - protected boolean contains( long id, boolean doPut ) - { - // determine the first empty array - int bp = size; // treat size as bitpattern - int idx = 1; - - while ( bp != 0 ) - { - if ( (bp&1) == 1 ) - { - // array at idx is valid, check - if ( contains( idx, id, doPut ) ) - { - return true; - } - } - idx++; - bp >>= 1; - } - return false; - } - - - // does sorted array "a" contain "id" ? - private boolean contains( int idx, long id, boolean doPut ) - { - long[] a = al[idx]; - int offset = a.length; - int n = 0; - - while ( (offset >>= 1) > 0 ) - { - int nn = n + offset; - if ( a[nn] <= id ) - { - n = nn; - } - } - if ( a[n] == id ) - { - value_out = (V)vla[idx][n]; - if ( doPut ) vla[idx][n] = value_in; - return true; - } - return false; - } - - protected void moveToFrozenArrays( long[] faid, ArrayList flv ) - { - for( int i=1; i>= 1; - } - faid[ti] = minId; - flv.add( (V)vla[minIdx][pa[minIdx]] ); - pa[minIdx]++; - - if ( ti > 0 && faid[ti-1] == minId ) - { - throw new IllegalArgumentException( "duplicate key found in late check: " + minId ); - } - } - - // free the non-frozen arrays - al = null; - vla = null; - } - -} +package btools.util; + +import java.util.ArrayList; + +/** + * Memory efficient Map to map a long-key to an object-value + *

+ * Implementation is such that basically the 12 bytes + * per entry is allocated that's needed to store + * a long- and an object-value. + * This class does not implement the Map interface + * because it's not complete (remove() is not implemented, + * CompactLongMap can only grow.) + * + * @author ab + */ +public class CompactLongMap { + private long[][] al; + private int[] pa; + private int size = 0; + private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays + + protected V value_in; + protected V value_out; + + protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE + private static boolean earlyDuplicateCheck; + + public CompactLongMap() { + // pointer array + pa = new int[MAXLISTS]; + + // allocate key lists + al = new long[MAXLISTS][]; + al[0] = new long[1]; // make the first array (the transient buffer) + + // same for the values + vla = new Object[MAXLISTS][]; + vla[0] = new Object[1]; + + earlyDuplicateCheck = Boolean.getBoolean("earlyDuplicateCheck"); + } + + + /* + * + * The Map extension: + * next 5 protected methods are needed to implement value-support + * overwrite them all to support value structures other than the + * long-values implemented here as a sample. + * + * Furthermore, put() and get() method need to be implemented + * to access the values. + * + * Note that this map does not behave exactly like java.util.Map + * - put(..) with already existing key throws exception + * - get(..) with non-existing key thros exception + * + * If you have keys that cannot easily be mapped on long's, use + * a hash-function to do the mapping. But note that, in comparison + * to java.util.HashMap, in that case the keys itself are not saved, + * only the hash-values, so you need to be sure that random duplicate + * hashs are either excluded by the structure of your data or that + * you can handle the possible IllegalArgumentException + * + */ + + private Object[][] vla; // value list array + + + public boolean put(long id, V value) { + try { + value_in = value; + if (contains(id, true)) { + return true; + } + vla[0][0] = value; + _add(id); + return false; + } finally { + value_in = null; + value_out = null; + } + } + + /** + * Same as put( id, value ) but duplicate check + * is skipped for performance. Be aware that you + * can get a duplicate exception later on if the + * map is restructured! + * with System parameter earlyDuplicateCheck=true you + * can enforce the early duplicate check for debugging + * + * @param id the key to insert + * @param value the value to insert object + * @throws IllegalArgumentException for duplicates if enabled + */ + public void fastPut(long id, V value) { + if (earlyDuplicateCheck && contains(id)) { + throw new IllegalArgumentException("duplicate key found in early check: " + id); + } + vla[0][0] = value; + _add(id); + } + + /** + * Get the value for the given id + * + * @param id the key to query + * @return the object, or null if id not known + */ + public V get(long id) { + try { + if (contains(id, false)) { + return value_out; + } + return null; + } finally { + value_out = null; + } + } + + + /** + * @return the number of entries in this map + */ + public int size() { + return size; + } + + + private boolean _add(long id) { + if (size == Integer.MAX_VALUE) { + throw new IllegalArgumentException("cannot grow beyond size Integer.MAX_VALUE"); + } + + // put the new entry in the first array + al[0][0] = id; + + // determine the first empty array + int bp = size++; // treat size as bitpattern + int idx = 1; + int n = 1; + + pa[0] = 1; + pa[1] = 1; + + while ((bp & 1) == 1) { + bp >>= 1; + pa[idx++] = n; + n <<= 1; + } + + // create it if not existant + if (al[idx] == null) { + al[idx] = new long[n]; + vla[idx] = new Object[n]; + } + + // now merge the contents of arrays 0...idx-1 into idx + while (n > 0) { + long maxId = 0; + int maxIdx = -1; + + for (int i = 0; i < idx; i++) { + int p = pa[i]; + if (p > 0) { + long currentId = al[i][p - 1]; + if (maxIdx < 0 || currentId > maxId) { + maxIdx = i; + maxId = currentId; + } + } + } + + // current maximum found, copy to target array + if (n < al[idx].length && maxId == al[idx][n]) { + throw new IllegalArgumentException("duplicate key found in late check: " + maxId); + } + --n; + al[idx][n] = maxId; + vla[idx][n] = vla[maxIdx][pa[maxIdx] - 1]; + + --pa[maxIdx]; + } + + // de-allocate empty arrays of a certain size (fix at 64kByte) + while (idx-- > _maxKeepExponent) { + al[idx] = null; + vla[idx] = null; + } + + return false; + } + + /** + * @return true if "id" is contained in this set. + */ + public boolean contains(long id) { + try { + return contains(id, false); + } finally { + value_out = null; + } + } + + protected boolean contains(long id, boolean doPut) { + // determine the first empty array + int bp = size; // treat size as bitpattern + int idx = 1; + + while (bp != 0) { + if ((bp & 1) == 1) { + // array at idx is valid, check + if (contains(idx, id, doPut)) { + return true; + } + } + idx++; + bp >>= 1; + } + return false; + } + + + // does sorted array "a" contain "id" ? + private boolean contains(int idx, long id, boolean doPut) { + long[] a = al[idx]; + int offset = a.length; + int n = 0; + + while ((offset >>= 1) > 0) { + int nn = n + offset; + if (a[nn] <= id) { + n = nn; + } + } + if (a[n] == id) { + value_out = (V) vla[idx][n]; + if (doPut) vla[idx][n] = value_in; + return true; + } + return false; + } + + protected void moveToFrozenArrays(long[] faid, ArrayList flv) { + for (int i = 1; i < MAXLISTS; i++) { + pa[i] = 0; + } + + for (int ti = 0; ti < size; ti++) // target-index + { + int bp = size; // treat size as bitpattern + int minIdx = -1; + long minId = 0; + int idx = 1; + while (bp != 0) { + if ((bp & 1) == 1) { + int p = pa[idx]; + if (p < al[idx].length) { + long currentId = al[idx][p]; + if (minIdx < 0 || currentId < minId) { + minIdx = idx; + minId = currentId; + } + } + } + idx++; + bp >>= 1; + } + faid[ti] = minId; + flv.add((V) vla[minIdx][pa[minIdx]]); + pa[minIdx]++; + + if (ti > 0 && faid[ti - 1] == minId) { + throw new IllegalArgumentException("duplicate key found in late check: " + minId); + } + } + + // free the non-frozen arrays + al = null; + vla = null; + } + +} diff --git a/brouter-util/src/main/java/btools/util/CompactLongSet.java b/brouter-util/src/main/java/btools/util/CompactLongSet.java index 24d56c1..8398fca 100644 --- a/brouter-util/src/main/java/btools/util/CompactLongSet.java +++ b/brouter-util/src/main/java/btools/util/CompactLongSet.java @@ -1,217 +1,187 @@ -package btools.util; - -/** - * Memory efficient Set for long-keys - * - * @author ab - */ -public class CompactLongSet -{ - private long[][] al; - private int[] pa; - private int size = 0; - private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays - - protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE - - public CompactLongSet() - { - // pointer array - pa = new int[MAXLISTS]; - - // allocate key lists - al = new long[MAXLISTS][]; - al[0] = new long[1]; // make the first array (the transient buffer) - } - - - /** - * @return the number of entries in this set - */ - public int size() - { - return size; - } - - /** - * add a long value to this set if not yet in. - * @param id the value to add to this set. - * @return true if "id" already contained in this set. - */ - public boolean add( long id ) - { - if ( contains( id ) ) - { - return true; - } - _add( id ); - return false; - } - - public void fastAdd( long id ) - { - _add( id ); - } - - private void _add( long id ) - { - if ( size == Integer.MAX_VALUE ) - { - throw new IllegalArgumentException( "cannot grow beyond size Integer.MAX_VALUE" ); - } - - // put the new entry in the first array - al[0][0] = id; - - // determine the first empty array - int bp = size++; // treat size as bitpattern - int idx = 1; - int n = 1; - - pa[0] = 1; - pa[1] = 1; - - while ( (bp&1) == 1 ) - { - bp >>= 1; - pa[idx++] = n; - n <<= 1; - } - - // create it if not existant - if ( al[idx] == null ) - { - al[idx] = new long[n]; - } - - // now merge the contents of arrays 0...idx-1 into idx - while ( n > 0 ) - { - long maxId = 0; - int maxIdx = -1; - - for ( int i=0; i 0 ) - { - long currentId = al[i][p-1]; - if ( maxIdx < 0 || currentId > maxId ) - { - maxIdx = i; - maxId = currentId; - } - } - } - - // current maximum found, copy to target array - if ( n < al[idx].length && maxId == al[idx][n] ) - { - throw new IllegalArgumentException( "duplicate key found in late check: " + maxId ); - } - --n; - al[idx][n] = maxId; - - --pa[maxIdx]; - } - - // de-allocate empty arrays of a certain size (fix at 64kByte) - while ( idx-- > _maxKeepExponent ) - { - al[idx] = null; - } - } - - /** - * @return true if "id" is contained in this set. - */ - public boolean contains( long id ) - { - // determine the first empty array - int bp = size; // treat size as bitpattern - int idx = 1; - - while ( bp != 0 ) - { - if ( (bp&1) == 1 ) - { - // array at idx is valid, check - if ( contains( idx, id ) ) - { - return true; - } - } - idx++; - bp >>= 1; - } - return false; - } - - - // does sorted array "a" contain "id" ? - private boolean contains( int idx, long id ) - { - long[] a = al[idx]; - int offset = a.length; - int n = 0; - - while ( (offset >>= 1) > 0 ) - { - int nn = n + offset; - if ( a[nn] <= id ) - { - n = nn; - } - } - if ( a[n] == id ) - { - return true; - } - return false; - } - - protected void moveToFrozenArray( long[] faid ) - { - for( int i=1; i>= 1; - } - faid[ti] = minId; - pa[minIdx]++; - - if ( ti > 0 && faid[ti-1] == minId ) - { - throw new IllegalArgumentException( "duplicate key found in late check: " + minId ); - } - } - - // free the non-frozen array - al = null; - } - -} +package btools.util; + +/** + * Memory efficient Set for long-keys + * + * @author ab + */ +public class CompactLongSet { + private long[][] al; + private int[] pa; + private int size = 0; + private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays + + protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE + + public CompactLongSet() { + // pointer array + pa = new int[MAXLISTS]; + + // allocate key lists + al = new long[MAXLISTS][]; + al[0] = new long[1]; // make the first array (the transient buffer) + } + + + /** + * @return the number of entries in this set + */ + public int size() { + return size; + } + + /** + * add a long value to this set if not yet in. + * + * @param id the value to add to this set. + * @return true if "id" already contained in this set. + */ + public boolean add(long id) { + if (contains(id)) { + return true; + } + _add(id); + return false; + } + + public void fastAdd(long id) { + _add(id); + } + + private void _add(long id) { + if (size == Integer.MAX_VALUE) { + throw new IllegalArgumentException("cannot grow beyond size Integer.MAX_VALUE"); + } + + // put the new entry in the first array + al[0][0] = id; + + // determine the first empty array + int bp = size++; // treat size as bitpattern + int idx = 1; + int n = 1; + + pa[0] = 1; + pa[1] = 1; + + while ((bp & 1) == 1) { + bp >>= 1; + pa[idx++] = n; + n <<= 1; + } + + // create it if not existant + if (al[idx] == null) { + al[idx] = new long[n]; + } + + // now merge the contents of arrays 0...idx-1 into idx + while (n > 0) { + long maxId = 0; + int maxIdx = -1; + + for (int i = 0; i < idx; i++) { + int p = pa[i]; + if (p > 0) { + long currentId = al[i][p - 1]; + if (maxIdx < 0 || currentId > maxId) { + maxIdx = i; + maxId = currentId; + } + } + } + + // current maximum found, copy to target array + if (n < al[idx].length && maxId == al[idx][n]) { + throw new IllegalArgumentException("duplicate key found in late check: " + maxId); + } + --n; + al[idx][n] = maxId; + + --pa[maxIdx]; + } + + // de-allocate empty arrays of a certain size (fix at 64kByte) + while (idx-- > _maxKeepExponent) { + al[idx] = null; + } + } + + /** + * @return true if "id" is contained in this set. + */ + public boolean contains(long id) { + // determine the first empty array + int bp = size; // treat size as bitpattern + int idx = 1; + + while (bp != 0) { + if ((bp & 1) == 1) { + // array at idx is valid, check + if (contains(idx, id)) { + return true; + } + } + idx++; + bp >>= 1; + } + return false; + } + + + // does sorted array "a" contain "id" ? + private boolean contains(int idx, long id) { + long[] a = al[idx]; + int offset = a.length; + int n = 0; + + while ((offset >>= 1) > 0) { + int nn = n + offset; + if (a[nn] <= id) { + n = nn; + } + } + if (a[n] == id) { + return true; + } + return false; + } + + protected void moveToFrozenArray(long[] faid) { + for (int i = 1; i < MAXLISTS; i++) { + pa[i] = 0; + } + + for (int ti = 0; ti < size; ti++) // target-index + { + int bp = size; // treat size as bitpattern + int minIdx = -1; + long minId = 0; + int idx = 1; + while (bp != 0) { + if ((bp & 1) == 1) { + int p = pa[idx]; + if (p < al[idx].length) { + long currentId = al[idx][p]; + if (minIdx < 0 || currentId < minId) { + minIdx = idx; + minId = currentId; + } + } + } + idx++; + bp >>= 1; + } + faid[ti] = minId; + pa[minIdx]++; + + if (ti > 0 && faid[ti - 1] == minId) { + throw new IllegalArgumentException("duplicate key found in late check: " + minId); + } + } + + // free the non-frozen array + al = null; + } + +} diff --git a/brouter-util/src/main/java/btools/util/Crc32.java b/brouter-util/src/main/java/btools/util/Crc32.java index c815317..bf68b3c 100644 --- a/brouter-util/src/main/java/btools/util/Crc32.java +++ b/brouter-util/src/main/java/btools/util/Crc32.java @@ -5,52 +5,49 @@ package btools.util; * * @author ab */ -public final class Crc32 -{ - public static int crc( byte[] ab, int offset, int len ) - { - int crc = 0xFFFFFFFF; +public final class Crc32 { + public static int crc(byte[] ab, int offset, int len) { + int crc = 0xFFFFFFFF; int end = offset + len; - for( int j=offset; j>> 8) ^ crctable[(crc ^ ab[j]) & 0xff]; } return crc; } private static int[] crctable = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - }; + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; } diff --git a/brouter-util/src/main/java/btools/util/DenseLongMap.java b/brouter-util/src/main/java/btools/util/DenseLongMap.java index 0e9385f..0295b62 100644 --- a/brouter-util/src/main/java/btools/util/DenseLongMap.java +++ b/brouter-util/src/main/java/btools/util/DenseLongMap.java @@ -1,233 +1,202 @@ -package btools.util; - -import java.util.ArrayList; - -/** - * Special Memory efficient Map to map a long-key to - * a "small" value (some bits only) where it is expected - * that the keys are dense, so that we can use more or less - * a simple array as the best-fit data model (except for - * the 32-bit limit of arrays!) - * - * Target application are osm-node ids which are in the - * range 0...3 billion and basically dense (=only few - * nodes deleted) - * - * @author ab - */ -public class DenseLongMap -{ - private ArrayList blocklist = new ArrayList(4096); - - private int blocksize; // bytes per bitplane in one block - private int blocksizeBits; - private long blocksizeBitsMask; - private int maxvalue = 254; // fixed due to 8 bit lookup table - private int[] bitplaneCount = new int[8]; - private long putCount = 0L; - private long getCount = 0L; - - /** - * Creates a DenseLongMap for the default block size - * ( 512 bytes per bitplane, covering a key range of 4096 keys ) - * Note that one value range is limited to 0..254 - */ - public DenseLongMap() - { - this(512); - } - - /** - * Creates a DenseLongMap for the given block size - * - * @param blocksize bytes per bit-plane - */ - public DenseLongMap( int blocksize ) - { - int bits = 4; - while( bits < 28 && (1 << bits) != blocksize ) - { - bits++; - } - if ( bits == 28 ) - { - throw new RuntimeException( "not a valid blocksize: " + blocksize + " ( expected 1 << bits with bits in (4..27) )"); - } - blocksizeBits = bits + 3; - blocksizeBitsMask = (1L << blocksizeBits ) -1; - this.blocksize = blocksize; - } - - - - public void put( long key, int value ) - { - putCount++; - - if ( value < 0 || value > maxvalue ) - { - throw new IllegalArgumentException( "value out of range (0.." + maxvalue + "): " + value ); - } - - int blockn = (int)(key >> blocksizeBits); - int offset = (int)(key & blocksizeBitsMask); - - byte[] block = blockn < blocklist.size() ? blocklist.get( blockn ) : null; - - int valuebits = 1; - if ( block == null ) - { - block = new byte[sizeForBits(valuebits)]; - bitplaneCount[0] ++; - - while (blocklist.size() < blockn+1 ) - { - blocklist.add(null); - } - blocklist.set( blockn, block ); - } - else - { - // check how many bitplanes we have from the arraysize - while( sizeForBits( valuebits) < block.length ) - { - valuebits++; - } - } - int headersize = 1 << valuebits; - - byte v = (byte)(value + 1); // 0 is reserved (=unset) - - // find the index in the lookup table or the first entry - int idx = 1; - while( idx < headersize ) - { - if ( block[idx] == 0 ) - { - block[idx] = v; // create new entry - } - if ( block[idx] == v ) - { - break; - } - idx++; - } - if ( idx == headersize ) - { - block = expandBlock( block, valuebits ); - block[idx] = v; // create new entry - blocklist.set( blockn, block ); - valuebits++; - headersize = 1 << valuebits; - } - - int bitmask = 1 << (offset & 0x7); - int invmask = bitmask ^ 0xff; - int probebit = 1; - int blockidx = (offset >> 3) + headersize; - - for( int i=0; i < valuebits; i++ ) - { - if ( ( idx & probebit ) != 0 ) - { - block[blockidx] |= bitmask; - } - else - { - block[blockidx] &= invmask; - } - probebit <<= 1; - blockidx += blocksize; - } - } - - - private int sizeForBits( int bits ) - { - // size is lookup table + datablocks - return ( 1 << bits ) + blocksize * bits; - } - - private byte[] expandBlock( byte[] block, int valuebits ) - { - bitplaneCount[valuebits] ++; - byte[] newblock = new byte[sizeForBits(valuebits+1)]; - int headersize = 1 << valuebits; - System.arraycopy(block, 0, newblock, 0, headersize ); // copy header - System.arraycopy(block, headersize, newblock, 2*headersize, block.length - headersize ); // copy data - return newblock; - } - - public int getInt( long key ) - { - // bit-stats on first get - if ( getCount++ == 0L ) - { - System.out.println( "**** DenseLongMap stats ****" ); - System.out.println( "putCount=" + putCount ); - for( int i=0; i<8; i++ ) - { - System.out.println( i + "-bitplanes=" +bitplaneCount[i] ); - } - System.out.println( "****************************" ); - } - - /* actual stats for the 30x45 raster and 512 blocksize with filtered nodes: - * - **** DenseLongMap stats **** - putCount=858518399 - 0-bitplanes=783337 - 1-bitplanes=771490 - 2-bitplanes=644578 - 3-bitplanes=210767 - 4-bitplanes=439 - 5-bitplanes=0 - 6-bitplanes=0 - 7-bitplanes=0 - * - * This is a total of 1,2 GB - * (1.234.232.832+7.381.126+15.666.740 for body/header/object-overhead ) - */ - - if ( key < 0 ) - { - return -1; - } - int blockn = (int)(key >> blocksizeBits); - int offset = (int)(key & blocksizeBitsMask); - - byte[] block = blockn < blocklist.size() ? blocklist.get( blockn ) : null; - - if ( block == null ) - { - return -1; - } - - // check how many bitplanes we have from the arrayzize - int valuebits = 1; - while( sizeForBits( valuebits) < block.length ) - { - valuebits++; - } - int headersize = 1 << valuebits; - - int bitmask = 1 << (offset & 7); - int probebit = 1; - int blockidx = (offset >> 3) + headersize; - int idx = 0; // 0 is reserved (=unset) - - for( int i=0; i < valuebits; i++ ) - { - if ( ( block[blockidx] & bitmask ) != 0 ) - { - idx |= probebit; - } - probebit <<= 1; - blockidx += blocksize; - } - - // lookup that value in the lookup header - return ((256 + block[idx]) & 0xff ) -1; - } - -} +package btools.util; + +import java.util.ArrayList; + +/** + * Special Memory efficient Map to map a long-key to + * a "small" value (some bits only) where it is expected + * that the keys are dense, so that we can use more or less + * a simple array as the best-fit data model (except for + * the 32-bit limit of arrays!) + *

+ * Target application are osm-node ids which are in the + * range 0...3 billion and basically dense (=only few + * nodes deleted) + * + * @author ab + */ +public class DenseLongMap { + private ArrayList blocklist = new ArrayList(4096); + + private int blocksize; // bytes per bitplane in one block + private int blocksizeBits; + private long blocksizeBitsMask; + private int maxvalue = 254; // fixed due to 8 bit lookup table + private int[] bitplaneCount = new int[8]; + private long putCount = 0L; + private long getCount = 0L; + + /** + * Creates a DenseLongMap for the default block size + * ( 512 bytes per bitplane, covering a key range of 4096 keys ) + * Note that one value range is limited to 0..254 + */ + public DenseLongMap() { + this(512); + } + + /** + * Creates a DenseLongMap for the given block size + * + * @param blocksize bytes per bit-plane + */ + public DenseLongMap(int blocksize) { + int bits = 4; + while (bits < 28 && (1 << bits) != blocksize) { + bits++; + } + if (bits == 28) { + throw new RuntimeException("not a valid blocksize: " + blocksize + " ( expected 1 << bits with bits in (4..27) )"); + } + blocksizeBits = bits + 3; + blocksizeBitsMask = (1L << blocksizeBits) - 1; + this.blocksize = blocksize; + } + + + public void put(long key, int value) { + putCount++; + + if (value < 0 || value > maxvalue) { + throw new IllegalArgumentException("value out of range (0.." + maxvalue + "): " + value); + } + + int blockn = (int) (key >> blocksizeBits); + int offset = (int) (key & blocksizeBitsMask); + + byte[] block = blockn < blocklist.size() ? blocklist.get(blockn) : null; + + int valuebits = 1; + if (block == null) { + block = new byte[sizeForBits(valuebits)]; + bitplaneCount[0]++; + + while (blocklist.size() < blockn + 1) { + blocklist.add(null); + } + blocklist.set(blockn, block); + } else { + // check how many bitplanes we have from the arraysize + while (sizeForBits(valuebits) < block.length) { + valuebits++; + } + } + int headersize = 1 << valuebits; + + byte v = (byte) (value + 1); // 0 is reserved (=unset) + + // find the index in the lookup table or the first entry + int idx = 1; + while (idx < headersize) { + if (block[idx] == 0) { + block[idx] = v; // create new entry + } + if (block[idx] == v) { + break; + } + idx++; + } + if (idx == headersize) { + block = expandBlock(block, valuebits); + block[idx] = v; // create new entry + blocklist.set(blockn, block); + valuebits++; + headersize = 1 << valuebits; + } + + int bitmask = 1 << (offset & 0x7); + int invmask = bitmask ^ 0xff; + int probebit = 1; + int blockidx = (offset >> 3) + headersize; + + for (int i = 0; i < valuebits; i++) { + if ((idx & probebit) != 0) { + block[blockidx] |= bitmask; + } else { + block[blockidx] &= invmask; + } + probebit <<= 1; + blockidx += blocksize; + } + } + + + private int sizeForBits(int bits) { + // size is lookup table + datablocks + return (1 << bits) + blocksize * bits; + } + + private byte[] expandBlock(byte[] block, int valuebits) { + bitplaneCount[valuebits]++; + byte[] newblock = new byte[sizeForBits(valuebits + 1)]; + int headersize = 1 << valuebits; + System.arraycopy(block, 0, newblock, 0, headersize); // copy header + System.arraycopy(block, headersize, newblock, 2 * headersize, block.length - headersize); // copy data + return newblock; + } + + public int getInt(long key) { + // bit-stats on first get + if (getCount++ == 0L) { + System.out.println("**** DenseLongMap stats ****"); + System.out.println("putCount=" + putCount); + for (int i = 0; i < 8; i++) { + System.out.println(i + "-bitplanes=" + bitplaneCount[i]); + } + System.out.println("****************************"); + } + + /* actual stats for the 30x45 raster and 512 blocksize with filtered nodes: + * + **** DenseLongMap stats **** + putCount=858518399 + 0-bitplanes=783337 + 1-bitplanes=771490 + 2-bitplanes=644578 + 3-bitplanes=210767 + 4-bitplanes=439 + 5-bitplanes=0 + 6-bitplanes=0 + 7-bitplanes=0 + * + * This is a total of 1,2 GB + * (1.234.232.832+7.381.126+15.666.740 for body/header/object-overhead ) + */ + + if (key < 0) { + return -1; + } + int blockn = (int) (key >> blocksizeBits); + int offset = (int) (key & blocksizeBitsMask); + + byte[] block = blockn < blocklist.size() ? blocklist.get(blockn) : null; + + if (block == null) { + return -1; + } + + // check how many bitplanes we have from the arrayzize + int valuebits = 1; + while (sizeForBits(valuebits) < block.length) { + valuebits++; + } + int headersize = 1 << valuebits; + + int bitmask = 1 << (offset & 7); + int probebit = 1; + int blockidx = (offset >> 3) + headersize; + int idx = 0; // 0 is reserved (=unset) + + for (int i = 0; i < valuebits; i++) { + if ((block[blockidx] & bitmask) != 0) { + idx |= probebit; + } + probebit <<= 1; + blockidx += blocksize; + } + + // lookup that value in the lookup header + return ((256 + block[idx]) & 0xff) - 1; + } + +} diff --git a/brouter-util/src/main/java/btools/util/DiffCoderDataInputStream.java b/brouter-util/src/main/java/btools/util/DiffCoderDataInputStream.java index 9fba290..3e5442a 100644 --- a/brouter-util/src/main/java/btools/util/DiffCoderDataInputStream.java +++ b/brouter-util/src/main/java/btools/util/DiffCoderDataInputStream.java @@ -10,40 +10,34 @@ import java.io.IOException; import java.io.InputStream; -public final class DiffCoderDataInputStream extends DataInputStream -{ +public final class DiffCoderDataInputStream extends DataInputStream { private long[] lastValues = new long[10]; - - public DiffCoderDataInputStream( InputStream is ) - { - super( is ); - } - - public long readDiffed( int idx ) throws IOException - { - long d = readSigned(); - long v = lastValues[idx] + d; - lastValues[idx] = v; - return v; + + public DiffCoderDataInputStream(InputStream is) { + super(is); } - public long readSigned() throws IOException - { - long v = readUnsigned(); - return ( v & 1 ) == 0 ? v >> 1 : -(v >> 1 ); + public long readDiffed(int idx) throws IOException { + long d = readSigned(); + long v = lastValues[idx] + d; + lastValues[idx] = v; + return v; } - public long readUnsigned() throws IOException - { - long v = 0; - int shift = 0; - for(;;) - { - long i7 = readByte() & 0xff; - v |= (( i7 & 0x7f ) << shift); - if ( ( i7 & 0x80 ) == 0 ) break; - shift += 7; - } - return v; + public long readSigned() throws IOException { + long v = readUnsigned(); + return (v & 1) == 0 ? v >> 1 : -(v >> 1); + } + + public long readUnsigned() throws IOException { + long v = 0; + int shift = 0; + for (; ; ) { + long i7 = readByte() & 0xff; + v |= ((i7 & 0x7f) << shift); + if ((i7 & 0x80) == 0) break; + shift += 7; + } + return v; } } diff --git a/brouter-util/src/main/java/btools/util/DiffCoderDataOutputStream.java b/brouter-util/src/main/java/btools/util/DiffCoderDataOutputStream.java index b3e8e75..3c1ccc9 100644 --- a/brouter-util/src/main/java/btools/util/DiffCoderDataOutputStream.java +++ b/brouter-util/src/main/java/btools/util/DiffCoderDataOutputStream.java @@ -10,36 +10,30 @@ import java.io.IOException; import java.io.OutputStream; -public final class DiffCoderDataOutputStream extends DataOutputStream -{ +public final class DiffCoderDataOutputStream extends DataOutputStream { private long[] lastValues = new long[10]; - - public DiffCoderDataOutputStream( OutputStream os ) - { - super( os ); - } - - public void writeDiffed( long v, int idx ) throws IOException - { - long d = v - lastValues[idx]; - lastValues[idx] = v; - writeSigned( d ); + + public DiffCoderDataOutputStream(OutputStream os) { + super(os); } - public void writeSigned( long v ) throws IOException - { - writeUnsigned( v < 0 ? ( (-v) << 1 ) | 1 : v << 1 ); + public void writeDiffed(long v, int idx) throws IOException { + long d = v - lastValues[idx]; + lastValues[idx] = v; + writeSigned(d); } - public void writeUnsigned( long v ) throws IOException - { - do - { - long i7 = v & 0x7f; - v >>= 7; - if ( v != 0 ) i7 |= 0x80; - writeByte( (byte)( i7 & 0xff ) ); - } - while( v != 0 ); + public void writeSigned(long v) throws IOException { + writeUnsigned(v < 0 ? ((-v) << 1) | 1 : v << 1); + } + + public void writeUnsigned(long v) throws IOException { + do { + long i7 = v & 0x7f; + v >>= 7; + if (v != 0) i7 |= 0x80; + writeByte((byte) (i7 & 0xff)); + } + while (v != 0); } } diff --git a/brouter-util/src/main/java/btools/util/FastMath.java b/brouter-util/src/main/java/btools/util/FastMath.java index 4caf9d0..05d4431 100644 --- a/brouter-util/src/main/java/btools/util/FastMath.java +++ b/brouter-util/src/main/java/btools/util/FastMath.java @@ -5,20 +5,17 @@ package btools.util; * * @author ab */ -public class FastMath -{ +public class FastMath { /** * Approximation to Math.exp for small negative arguments */ - public static double exp( double e ) - { + public static double exp(double e) { double x = e; double f = 1.; - while( e < -1. ) - { + while (e < -1.) { e += 1.; f *= 0.367879; } - return f*( 1. + x*( 1. + x * ( 0.5 + x * ( 0.166667 + 0.0416667 * x) ) ) ); + return f * (1. + x * (1. + x * (0.5 + x * (0.166667 + 0.0416667 * x)))); } } diff --git a/brouter-util/src/main/java/btools/util/FrozenLongMap.java b/brouter-util/src/main/java/btools/util/FrozenLongMap.java index 168a948..8048297 100644 --- a/brouter-util/src/main/java/btools/util/FrozenLongMap.java +++ b/brouter-util/src/main/java/btools/util/FrozenLongMap.java @@ -1,143 +1,120 @@ -package btools.util; - -import java.util.ArrayList; -import java.util.List; - -/** - * Frozen instance of Memory efficient Map - * - * This one is readily sorted into a singe array for faster access - * - * @author ab - */ -public class FrozenLongMap extends CompactLongMap -{ - private long[] faid; - private ArrayList flv; - private int size = 0; - private int p2size; // next power of 2 of size - - public FrozenLongMap( CompactLongMap map ) - { - size = map.size(); - - faid = new long[size]; - flv = new ArrayList(size); - - map.moveToFrozenArrays( faid, flv ); - - p2size = 0x40000000; - while( p2size > size ) p2size >>= 1; - } - - @Override - public boolean put( long id, V value ) - { - try - { - value_in = value; - if ( contains( id, true ) ) - { - return true; - } - throw new RuntimeException( "cannot only put on existing key in FrozenLongIntMap" ); - } - finally - { - value_in = null; - value_out = null; - } - } - - @Override - public void fastPut( long id, V value ) - { - throw new RuntimeException( "cannot put on FrozenLongIntMap" ); - } - - /** - * @return the number of entries in this set - */ - @Override - public int size() - { - return size; - } - - - - /** - * @return true if "id" is contained in this set. - */ - @Override - protected boolean contains( long id, boolean doPut ) - { - if ( size == 0 ) - { - return false; - } - long[] a = faid; - int offset = p2size; - int n = 0; - - while ( offset> 0 ) - { - int nn = n + offset; - if ( nn < size && a[nn] <= id ) - { - n = nn; - } - offset >>= 1; - } - if ( a[n] == id ) - { - value_out = flv.get(n); - if ( doPut ) - { - flv.set( n, value_in ); - } - return true; - } - return false; - } - - /** - * @return the value for "id", or null if key unknown - */ - @Override - public V get( long id ) - { - if ( size == 0 ) - { - return null; - } - long[] a = faid; - int offset = p2size; - int n = 0; - - while ( offset> 0 ) - { - int nn = n + offset; - if ( nn < size && a[nn] <= id ) - { - n = nn; - } - offset >>= 1; - } - if ( a[n] == id ) - { - return flv.get(n); - } - return null; - } - - public List getValueList() - { - return flv; - } - - public long[] getKeyArray() - { - return faid; - } -} +package btools.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * Frozen instance of Memory efficient Map + *

+ * This one is readily sorted into a singe array for faster access + * + * @author ab + */ +public class FrozenLongMap extends CompactLongMap { + private long[] faid; + private ArrayList flv; + private int size = 0; + private int p2size; // next power of 2 of size + + public FrozenLongMap(CompactLongMap map) { + size = map.size(); + + faid = new long[size]; + flv = new ArrayList(size); + + map.moveToFrozenArrays(faid, flv); + + p2size = 0x40000000; + while (p2size > size) p2size >>= 1; + } + + @Override + public boolean put(long id, V value) { + try { + value_in = value; + if (contains(id, true)) { + return true; + } + throw new RuntimeException("cannot only put on existing key in FrozenLongIntMap"); + } finally { + value_in = null; + value_out = null; + } + } + + @Override + public void fastPut(long id, V value) { + throw new RuntimeException("cannot put on FrozenLongIntMap"); + } + + /** + * @return the number of entries in this set + */ + @Override + public int size() { + return size; + } + + + /** + * @return true if "id" is contained in this set. + */ + @Override + protected boolean contains(long id, boolean doPut) { + if (size == 0) { + return false; + } + long[] a = faid; + int offset = p2size; + int n = 0; + + while (offset > 0) { + int nn = n + offset; + if (nn < size && a[nn] <= id) { + n = nn; + } + offset >>= 1; + } + if (a[n] == id) { + value_out = flv.get(n); + if (doPut) { + flv.set(n, value_in); + } + return true; + } + return false; + } + + /** + * @return the value for "id", or null if key unknown + */ + @Override + public V get(long id) { + if (size == 0) { + return null; + } + long[] a = faid; + int offset = p2size; + int n = 0; + + while (offset > 0) { + int nn = n + offset; + if (nn < size && a[nn] <= id) { + n = nn; + } + offset >>= 1; + } + if (a[n] == id) { + return flv.get(n); + } + return null; + } + + public List getValueList() { + return flv; + } + + public long[] getKeyArray() { + return faid; + } +} diff --git a/brouter-util/src/main/java/btools/util/FrozenLongSet.java b/brouter-util/src/main/java/btools/util/FrozenLongSet.java index f6ef6b3..2d97b8a 100644 --- a/brouter-util/src/main/java/btools/util/FrozenLongSet.java +++ b/brouter-util/src/main/java/btools/util/FrozenLongSet.java @@ -1,81 +1,70 @@ -package btools.util; - -/** - * Frozen instance of Memory efficient Set - * - * This one is readily sorted into a singe array for faster access - * - * @author ab - */ -public class FrozenLongSet extends CompactLongSet -{ - private long[] faid; - private int size = 0; - private int p2size; // next power of 2 of size - - public FrozenLongSet( CompactLongSet set ) - { - size = set.size(); - - faid = new long[size]; - - set.moveToFrozenArray( faid ); - - p2size = 0x40000000; - while( p2size > size ) p2size >>= 1; - } - - @Override - public boolean add( long id ) - { - throw new RuntimeException( "cannot add on FrozenLongSet" ); - } - - @Override - public void fastAdd( long id ) - { - throw new RuntimeException( "cannot add on FrozenLongSet" ); - } - - /** - * @return the number of entries in this set - */ - @Override - public int size() - { - return size; - } - - - - /** - * @return true if "id" is contained in this set. - */ - @Override - public boolean contains( long id ) - { - if ( size == 0 ) - { - return false; - } - long[] a = faid; - int offset = p2size; - int n = 0; - - while ( offset> 0 ) - { - int nn = n + offset; - if ( nn < size && a[nn] <= id ) - { - n = nn; - } - offset >>= 1; - } - if ( a[n] == id ) - { - return true; - } - return false; - } - -} +package btools.util; + +/** + * Frozen instance of Memory efficient Set + *

+ * This one is readily sorted into a singe array for faster access + * + * @author ab + */ +public class FrozenLongSet extends CompactLongSet { + private long[] faid; + private int size = 0; + private int p2size; // next power of 2 of size + + public FrozenLongSet(CompactLongSet set) { + size = set.size(); + + faid = new long[size]; + + set.moveToFrozenArray(faid); + + p2size = 0x40000000; + while (p2size > size) p2size >>= 1; + } + + @Override + public boolean add(long id) { + throw new RuntimeException("cannot add on FrozenLongSet"); + } + + @Override + public void fastAdd(long id) { + throw new RuntimeException("cannot add on FrozenLongSet"); + } + + /** + * @return the number of entries in this set + */ + @Override + public int size() { + return size; + } + + + /** + * @return true if "id" is contained in this set. + */ + @Override + public boolean contains(long id) { + if (size == 0) { + return false; + } + long[] a = faid; + int offset = p2size; + int n = 0; + + while (offset > 0) { + int nn = n + offset; + if (nn < size && a[nn] <= id) { + n = nn; + } + offset >>= 1; + } + if (a[n] == id) { + return true; + } + return false; + } + +} diff --git a/brouter-util/src/main/java/btools/util/IByteArrayUnifier.java b/brouter-util/src/main/java/btools/util/IByteArrayUnifier.java index f441a36..bff2e76 100644 --- a/brouter-util/src/main/java/btools/util/IByteArrayUnifier.java +++ b/brouter-util/src/main/java/btools/util/IByteArrayUnifier.java @@ -1,6 +1,5 @@ package btools.util; -public interface IByteArrayUnifier -{ - byte[] unify( byte[] ab, int offset, int len ); +public interface IByteArrayUnifier { + byte[] unify(byte[] ab, int offset, int len); } diff --git a/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java b/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java index 850504b..e25244c 100644 --- a/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java +++ b/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java @@ -1,53 +1,44 @@ -package btools.util; - -import java.util.List; -import java.util.ArrayList; - -/** - * Behaves like an Array of list - * with lazy list-allocation at getList - * - * @author ab - */ -public class LazyArrayOfLists -{ - private ArrayList> lists; - - public LazyArrayOfLists( int size ) - { - lists = new ArrayList>( size ); - for ( int i = 0; i< size; i++ ) - { - lists.add( null ); - } - } - - public List getList( int idx ) - { - ArrayList list = lists.get( idx ); - if ( list == null ) - { - list = new ArrayList(); - lists.set( idx, list ); - } - return list; - } - - public int getSize( int idx ) - { - List list = lists.get( idx ); - return list == null ? 0 : list.size(); - } - - public void trimAll() - { - for ( int idx = 0; idx< lists.size(); idx++ ) - { - ArrayList list = lists.get( idx ); - if ( list != null ) - { - list.trimToSize(); - } - } - } -} +package btools.util; + +import java.util.List; +import java.util.ArrayList; + +/** + * Behaves like an Array of list + * with lazy list-allocation at getList + * + * @author ab + */ +public class LazyArrayOfLists { + private ArrayList> lists; + + public LazyArrayOfLists(int size) { + lists = new ArrayList>(size); + for (int i = 0; i < size; i++) { + lists.add(null); + } + } + + public List getList(int idx) { + ArrayList list = lists.get(idx); + if (list == null) { + list = new ArrayList(); + lists.set(idx, list); + } + return list; + } + + public int getSize(int idx) { + List list = lists.get(idx); + return list == null ? 0 : list.size(); + } + + public void trimAll() { + for (int idx = 0; idx < lists.size(); idx++) { + ArrayList list = lists.get(idx); + if (list != null) { + list.trimToSize(); + } + } + } +} diff --git a/brouter-util/src/main/java/btools/util/LongList.java b/brouter-util/src/main/java/btools/util/LongList.java index 2da9914..da25e26 100644 --- a/brouter-util/src/main/java/btools/util/LongList.java +++ b/brouter-util/src/main/java/btools/util/LongList.java @@ -1,43 +1,36 @@ -package btools.util; - -/** - * dynamic list of primitive longs - * - * @author ab - */ -public class LongList -{ - private long[] a; - private int size; - - public LongList( int capacity ) - { - a = capacity < 4 ? new long[4] : new long[capacity]; - } - - public void add( long value ) - { - if ( size == a.length ) - { - long[] aa = new long[2*size]; - System.arraycopy( a, 0, aa, 0, size ); - a = aa; - } - a[size++] = value; - } - - public long get( int idx ) - { - if ( idx >= size ) - { - throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx ); - } - return a[idx]; - } - - public int size() - { - return size; - } - -} +package btools.util; + +/** + * dynamic list of primitive longs + * + * @author ab + */ +public class LongList { + private long[] a; + private int size; + + public LongList(int capacity) { + a = capacity < 4 ? new long[4] : new long[capacity]; + } + + public void add(long value) { + if (size == a.length) { + long[] aa = new long[2 * size]; + System.arraycopy(a, 0, aa, 0, size); + a = aa; + } + a[size++] = value; + } + + public long get(int idx) { + if (idx >= size) { + throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx); + } + return a[idx]; + } + + public int size() { + return size; + } + +} diff --git a/brouter-util/src/main/java/btools/util/LruMap.java b/brouter-util/src/main/java/btools/util/LruMap.java index 441ecad..958ea0a 100644 --- a/brouter-util/src/main/java/btools/util/LruMap.java +++ b/brouter-util/src/main/java/btools/util/LruMap.java @@ -5,114 +5,93 @@ package btools.util; * * @author ab */ -public final class LruMap -{ +public final class LruMap { private int hashbins; private int maxsize; private int size; - + private LruMapNode lru; private LruMapNode mru; private LruMapNode[] binArray; - public LruMap( int bins, int size) - { + public LruMap(int bins, int size) { hashbins = bins; maxsize = size; binArray = new LruMapNode[hashbins]; } - - public LruMapNode get( LruMapNode key ) - { - int bin = ( key.hash & 0xfffffff ) % hashbins; - + + public LruMapNode get(LruMapNode key) { + int bin = (key.hash & 0xfffffff) % hashbins; + LruMapNode e = binArray[bin]; - while ( e != null ) - { - if ( key.equals( e ) ) - { + while (e != null) { + if (key.equals(e)) { return e; } e = e.nextInBin; } return null; } - + // put e to the mru end of the queue - public void touch( LruMapNode e ) - { + public void touch(LruMapNode e) { LruMapNode n = e.next; LruMapNode p = e.previous; - - if ( n == null ) - { + + if (n == null) { return; // already at mru } n.previous = p; - if ( p != null ) - { + if (p != null) { p.next = n; - } - else - { + } else { lru = n; } - + mru.next = e; - e.previous = mru; + e.previous = mru; e.next = null; mru = e; } - - public LruMapNode removeLru() - { - if ( size < maxsize ) - { + + public LruMapNode removeLru() { + if (size < maxsize) { return null; } size--; // unlink the lru from it's bin-queue - int bin = ( lru.hashCode() & 0xfffffff ) % hashbins; + int bin = (lru.hashCode() & 0xfffffff) % hashbins; LruMapNode e = binArray[bin]; - if ( e == lru ) - { + if (e == lru) { binArray[bin] = lru.nextInBin; - } - else - { - while( e != null ) - { + } else { + while (e != null) { LruMapNode prev = e; e = e.nextInBin; - if ( e == lru ) - { + if (e == lru) { prev.nextInBin = lru.nextInBin; break; } } } - + LruMapNode res = lru; lru = lru.next; lru.previous = null; return res; } - public void put( LruMapNode val ) - { - int bin = ( val.hashCode() & 0xfffffff ) % hashbins; + public void put(LruMapNode val) { + int bin = (val.hashCode() & 0xfffffff) % hashbins; val.nextInBin = binArray[bin]; binArray[bin] = val; - - val.previous = mru; + + val.previous = mru; val.next = null; - if ( mru == null ) - { + if (mru == null) { lru = val; - } - else - { + } else { mru.next = val; } mru = val; diff --git a/brouter-util/src/main/java/btools/util/LruMapNode.java b/brouter-util/src/main/java/btools/util/LruMapNode.java index 31b0af2..e726686 100644 --- a/brouter-util/src/main/java/btools/util/LruMapNode.java +++ b/brouter-util/src/main/java/btools/util/LruMapNode.java @@ -1,10 +1,9 @@ package btools.util; -public abstract class LruMapNode -{ +public abstract class LruMapNode { LruMapNode nextInBin; // next entry for hash-bin LruMapNode next; // next in lru sequence (towards mru) LruMapNode previous; // previous in lru sequence (towards lru) - + public int hash; } diff --git a/brouter-util/src/main/java/btools/util/MixCoderDataInputStream.java b/brouter-util/src/main/java/btools/util/MixCoderDataInputStream.java index ea2f705..31acac9 100644 --- a/brouter-util/src/main/java/btools/util/MixCoderDataInputStream.java +++ b/brouter-util/src/main/java/btools/util/MixCoderDataInputStream.java @@ -10,8 +10,7 @@ import java.io.IOException; import java.io.InputStream; -public final class MixCoderDataInputStream extends DataInputStream -{ +public final class MixCoderDataInputStream extends DataInputStream { private int lastValue; private int repCount; private int diffshift; @@ -22,15 +21,12 @@ public final class MixCoderDataInputStream extends DataInputStream private static final int[] vl_values = BitCoderContext.vl_values; private static final int[] vl_length = BitCoderContext.vl_length; - public MixCoderDataInputStream( InputStream is ) - { - super( is ); + public MixCoderDataInputStream(InputStream is) { + super(is); } - public int readMixed() throws IOException - { - if ( repCount == 0 ) - { + public int readMixed() throws IOException { + if (repCount == 0) { boolean negative = decodeBit(); int d = decodeVarBits() + diffshift; repCount = decodeVarBits() + 1; @@ -41,37 +37,32 @@ public final class MixCoderDataInputStream extends DataInputStream return lastValue; } - public final boolean decodeBit() throws IOException - { + public final boolean decodeBit() throws IOException { fillBuffer(); - boolean value = ( ( b & 1 ) != 0 ); + boolean value = ((b & 1) != 0); b >>>= 1; bits--; return value; } - public final int decodeVarBits2() throws IOException - { + public final int decodeVarBits2() throws IOException { int range = 0; - while (!decodeBit()) - { + while (!decodeBit()) { range = 2 * range + 1; } - return range + decodeBounded( range ); + return range + decodeBounded(range); } /** * decode an integer in the range 0..max (inclusive). + * * @see #encodeBounded */ - public final int decodeBounded( int max ) throws IOException - { + public final int decodeBounded(int max) throws IOException { int value = 0; int im = 1; // integer mask - while (( value | im ) <= max) - { - if ( decodeBit() ) - { + while ((value | im) <= max) { + if (decodeBit()) { value |= im; } im <<= 1; @@ -84,38 +75,35 @@ public final class MixCoderDataInputStream extends DataInputStream * @see #encodeVarBits */ - public final int decodeVarBits() throws IOException - { + public final int decodeVarBits() throws IOException { fillBuffer(); int b12 = b & 0xfff; int len = vl_length[b12]; - if ( len <= 12 ) - { + if (len <= 12) { b >>>= len; bits -= len; return vl_values[b12]; // full value lookup } - if ( len <= 23 ) // // only length lookup + if (len <= 23) // // only length lookup { int len2 = len >> 1; - b >>>= (len2+1); - int mask = 0xffffffff >>> ( 32 - len2 ); + b >>>= (len2 + 1); + int mask = 0xffffffff >>> (32 - len2); mask += b & mask; b >>>= len2; bits -= len; return mask; } - if ( (b & 0xffffff) != 0 ) - { + if ((b & 0xffffff) != 0) { // here we just know len in [25..47] // ( fillBuffer guarantees only 24 bits! ) b >>>= 12; - int len3 = 1 + (vl_length[b & 0xfff]>>1); + int len3 = 1 + (vl_length[b & 0xfff] >> 1); b >>>= len3; int len2 = 11 + len3; - bits -= len2+1; + bits -= len2 + 1; fillBuffer(); - int mask = 0xffffffff >>> ( 32 - len2 ); + int mask = 0xffffffff >>> (32 - len2); mask += b & mask; b >>>= len2; bits -= len2; @@ -124,14 +112,11 @@ public final class MixCoderDataInputStream extends DataInputStream return decodeVarBits2(); // no chance, use the slow one } - private void fillBuffer() throws IOException - { - while (bits < 24) - { + private void fillBuffer() throws IOException { + while (bits < 24) { int nextByte = read(); - - if ( nextByte != -1 ) - { + + if (nextByte != -1) { b |= (nextByte & 0xff) << bits; } bits += 8; diff --git a/brouter-util/src/main/java/btools/util/MixCoderDataOutputStream.java b/brouter-util/src/main/java/btools/util/MixCoderDataOutputStream.java index ad6d81a..99f8aec 100644 --- a/brouter-util/src/main/java/btools/util/MixCoderDataOutputStream.java +++ b/brouter-util/src/main/java/btools/util/MixCoderDataOutputStream.java @@ -10,8 +10,7 @@ import java.io.IOException; import java.io.OutputStream; -public final class MixCoderDataOutputStream extends DataOutputStream -{ +public final class MixCoderDataOutputStream extends DataOutputStream { private int lastValue; private int lastLastValue; private int repCount; @@ -23,28 +22,24 @@ public final class MixCoderDataOutputStream extends DataOutputStream public static int[] diffs = new int[100]; public static int[] counts = new int[100]; - public MixCoderDataOutputStream( OutputStream os ) - { - super( os ); + public MixCoderDataOutputStream(OutputStream os) { + super(os); } - public void writeMixed( int v ) throws IOException - { - if ( v != lastValue && repCount > 0 ) - { + public void writeMixed(int v) throws IOException { + if (v != lastValue && repCount > 0) { int d = lastValue - lastLastValue; lastLastValue = lastValue; - - encodeBit( d < 0 ); - if ( d < 0 ) - { + + encodeBit(d < 0); + if (d < 0) { d = -d; } - encodeVarBits( d-diffshift ); - encodeVarBits( repCount-1 ); - - if ( d < 100 ) diffs[d]++; - if ( repCount < 100 ) counts[repCount]++; + encodeVarBits(d - diffshift); + encodeVarBits(repCount - 1); + + if (d < 100) diffs[d]++; + if (repCount < 100) counts[repCount]++; diffshift = 1; repCount = 0; @@ -54,59 +49,48 @@ public final class MixCoderDataOutputStream extends DataOutputStream } @Override - public void flush() throws IOException - { + public void flush() throws IOException { int v = lastValue; - writeMixed( v+1 ); + writeMixed(v + 1); lastValue = v; repCount = 0; - if ( bm > 1 ) - { - writeByte( (byte)b ); // flush bit-coding + if (bm > 1) { + writeByte((byte) b); // flush bit-coding } } - public final void encodeBit( boolean value ) throws IOException - { - if ( bm == 0x100 ) - { - writeByte( (byte)b ); + public final void encodeBit(boolean value) throws IOException { + if (bm == 0x100) { + writeByte((byte) b); bm = 1; b = 0; } - if ( value ) - { + if (value) { b |= bm; } bm <<= 1; } - public final void encodeVarBits( int value ) throws IOException - { + public final void encodeVarBits(int value) throws IOException { int range = 0; - while (value > range) - { - encodeBit( false ); + while (value > range) { + encodeBit(false); value -= range + 1; range = 2 * range + 1; } - encodeBit( true ); - encodeBounded( range, value ); + encodeBit(true); + encodeBounded(range, value); } - public final void encodeBounded( int max, int value ) throws IOException - { + public final void encodeBounded(int max, int value) throws IOException { int im = 1; // integer mask - while (im <= max) - { - if ( bm == 0x100 ) - { - writeByte( (byte)b ); + while (im <= max) { + if (bm == 0x100) { + writeByte((byte) b); bm = 1; b = 0; } - if ( ( value & im ) != 0 ) - { + if ((value & im) != 0) { b |= bm; max -= im; } @@ -115,9 +99,8 @@ public final class MixCoderDataOutputStream extends DataOutputStream } } - public static void stats() - { - for(int i=1; i<100;i++) System.out.println( "diff[" + i + "] = " + diffs[i] ); - for(int i=1; i<100;i++) System.out.println( "counts[" + i + "] = " + counts[i] ); + public static void stats() { + for (int i = 1; i < 100; i++) System.out.println("diff[" + i + "] = " + diffs[i]); + for (int i = 1; i < 100; i++) System.out.println("counts[" + i + "] = " + counts[i]); } } diff --git a/brouter-util/src/main/java/btools/util/ProgressListener.java b/brouter-util/src/main/java/btools/util/ProgressListener.java index 4a6d473..88179ee 100644 --- a/brouter-util/src/main/java/btools/util/ProgressListener.java +++ b/brouter-util/src/main/java/btools/util/ProgressListener.java @@ -1,9 +1,8 @@ package btools.util; -public interface ProgressListener -{ +public interface ProgressListener { public void updateProgress(String task, int progress); - + public boolean isCanceled(); } diff --git a/brouter-util/src/main/java/btools/util/ReducedMedianFilter.java b/brouter-util/src/main/java/btools/util/ReducedMedianFilter.java index 7de4f08..2c1f698 100644 --- a/brouter-util/src/main/java/btools/util/ReducedMedianFilter.java +++ b/brouter-util/src/main/java/btools/util/ReducedMedianFilter.java @@ -3,31 +3,24 @@ package btools.util; /** * a median filter with additional edge reduction */ -public final class ReducedMedianFilter -{ +public final class ReducedMedianFilter { private int nsamples; private double[] weights; private int[] values; - public ReducedMedianFilter( int size ) - { + public ReducedMedianFilter(int size) { weights = new double[size]; values = new int[size]; } - public void reset() - { + public void reset() { nsamples = 0; } - public void addSample( double weight, int value ) - { - if ( weight > 0. ) - { - for( int i=0; i 0.) { + for (int i = 0; i < nsamples; i++) { + if (values[i] == value) { weights[i] += weight; return; } @@ -38,63 +31,52 @@ public final class ReducedMedianFilter } } - public double calcEdgeReducedMedian( double fraction ) - { - removeEdgeWeight( (1. - fraction)/2., true ); - removeEdgeWeight( (1. - fraction)/2., false ); + public double calcEdgeReducedMedian(double fraction) { + removeEdgeWeight((1. - fraction) / 2., true); + removeEdgeWeight((1. - fraction) / 2., false); double totalWeight = 0.; double totalValue = 0.; - for( int i=0; i 0. ) - { - // first pass to find minmax value - double totalWeight = 0.; - int minmax = 0; - for( int i=0; i 0. ) - { - int v = values[i]; - if ( totalWeight == 0. || ( high ? v > minmax : v < minmax ) ) - { - minmax = v; - } - totalWeight += w; - } - } - if ( totalWeight < excessWeight ) throw new IllegalArgumentException( "ups, not enough weight to remove" ); + private void removeEdgeWeight(double excessWeight, boolean high) { + while (excessWeight > 0.) { + // first pass to find minmax value + double totalWeight = 0.; + int minmax = 0; + for (int i = 0; i < nsamples; i++) { + double w = weights[i]; + if (w > 0.) { + int v = values[i]; + if (totalWeight == 0. || (high ? v > minmax : v < minmax)) { + minmax = v; + } + totalWeight += w; + } + } - // second pass to remove - for( int i=0; i 0. ) - { - if ( excessWeight > weights[i] ) - { - excessWeight -= weights[i]; - weights[i] = 0.; - } - else - { - weights[i] -= excessWeight; - excessWeight = 0.; - } - } - } - } + if (totalWeight < excessWeight) + throw new IllegalArgumentException("ups, not enough weight to remove"); + + // second pass to remove + for (int i = 0; i < nsamples; i++) { + if (values[i] == minmax && weights[i] > 0.) { + if (excessWeight > weights[i]) { + excessWeight -= weights[i]; + weights[i] = 0.; + } else { + weights[i] -= excessWeight; + excessWeight = 0.; + } + } + } + } } } diff --git a/brouter-util/src/main/java/btools/util/SortedHeap.java b/brouter-util/src/main/java/btools/util/SortedHeap.java index e551e7d..564750f 100644 --- a/brouter-util/src/main/java/btools/util/SortedHeap.java +++ b/brouter-util/src/main/java/btools/util/SortedHeap.java @@ -7,27 +7,23 @@ import java.util.Random; * * @author ab */ -public final class SortedHeap -{ +public final class SortedHeap { private int size; private int peaksize; private SortedBin first; private SortedBin second; private SortedBin firstNonEmpty; - public SortedHeap() - { + public SortedHeap() { clear(); } /** * @return the lowest key value, or null if none */ - public V popLowestKeyValue() - { + public V popLowestKeyValue() { SortedBin bin = firstNonEmpty; - if ( firstNonEmpty == null ) - { + if (firstNonEmpty == null) { return null; } size--; @@ -35,8 +31,7 @@ public final class SortedHeap return (V) minBin.dropLowest(); } - private static final class SortedBin - { + private static final class SortedBin { SortedHeap parent; SortedBin next; SortedBin nextNonEmpty; @@ -46,8 +41,7 @@ public final class SortedHeap int lv; // low value int lp; // low pointer - SortedBin( int binsize, SortedHeap parent ) - { + SortedBin(int binsize, SortedHeap parent) { this.binsize = binsize; this.parent = parent; al = new int[binsize]; @@ -55,24 +49,18 @@ public final class SortedHeap lp = binsize; } - SortedBin next() - { - if ( next == null ) - { - next = new SortedBin( binsize << 1, parent ); + SortedBin next() { + if (next == null) { + next = new SortedBin(binsize << 1, parent); } return next; } - Object dropLowest() - { + Object dropLowest() { int lpOld = lp; - if ( ++lp == binsize ) - { + if (++lp == binsize) { unlink(); - } - else - { + } else { lv = al[lp]; } Object res = vla[lpOld]; @@ -80,19 +68,15 @@ public final class SortedHeap return res; } - void unlink() - { + void unlink() { SortedBin neBin = parent.firstNonEmpty; - if ( neBin == this ) - { + if (neBin == this) { parent.firstNonEmpty = nextNonEmpty; return; } - for(;;) - { + for (; ; ) { SortedBin next = neBin.nextNonEmpty; - if ( next == this ) - { + if (next == this) { neBin.nextNonEmpty = nextNonEmpty; return; } @@ -100,30 +84,25 @@ public final class SortedHeap } } - void add( int key, Object value ) - { + void add(int key, Object value) { int p = lp; - for(;;) - { - if ( p == binsize || key < al[p] ) - { - al[p-1] = key; - vla[p-1] = value; + for (; ; ) { + if (p == binsize || key < al[p]) { + al[p - 1] = key; + vla[p - 1] = value; lv = al[--lp]; return; } - al[p-1] = al[p]; - vla[p-1] = vla[p]; + al[p - 1] = al[p]; + vla[p - 1] = vla[p]; p++; } } // unrolled version of above for binsize = 4 - void add4( int key, Object value ) - { + void add4(int key, Object value) { int p = lp--; - if ( p == 4 || key < al[p] ) - { + if (p == 4 || key < al[p]) { lv = al[p - 1] = key; vla[p - 1] = value; return; @@ -132,8 +111,7 @@ public final class SortedHeap vla[p - 1] = vla[p]; p++; - if ( p == 4 || key < al[p] ) - { + if (p == 4 || key < al[p]) { al[p - 1] = key; vla[p - 1] = value; return; @@ -142,8 +120,7 @@ public final class SortedHeap vla[p - 1] = vla[p]; p++; - if ( p == 4 || key < al[p] ) - { + if (p == 4 || key < al[p]) { al[p - 1] = key; vla[p - 1] = value; return; @@ -156,72 +133,71 @@ public final class SortedHeap } // unrolled loop for performance sake - SortedBin getMinBin() - { + SortedBin getMinBin() { SortedBin minBin = this; SortedBin bin = this; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; - if ( (bin = bin.nextNonEmpty) == null ) return minBin; - if ( bin.lv < minBin.lv ) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; + if ((bin = bin.nextNonEmpty) == null) return minBin; + if (bin.lv < minBin.lv) minBin = bin; return minBin; } } @@ -229,31 +205,26 @@ public final class SortedHeap /** * add a key value pair to the heap * - * @param key the key to insert - * @param value the value to insert object + * @param key the key to insert + * @param value the value to insert object */ - public void add( int key, V value ) - { + public void add(int key, V value) { size++; - if ( first.lp == 0 && second.lp == 0) // both full ? + if (first.lp == 0 && second.lp == 0) // both full ? { sortUp(); } - if ( first.lp > 0 ) - { - first.add4( key, value ); - if ( firstNonEmpty != first ) - { + if (first.lp > 0) { + first.add4(key, value); + if (firstNonEmpty != first) { first.nextNonEmpty = firstNonEmpty; firstNonEmpty = first; } - } - else // second bin not full + } else // second bin not full { - second.add4( key, value ); - if ( first.nextNonEmpty != second ) - { + second.add4(key, value); + if (first.nextNonEmpty != second) { second.nextNonEmpty = first.nextNonEmpty; first.nextNonEmpty = second; } @@ -261,10 +232,8 @@ public final class SortedHeap } - private void sortUp() - { - if ( size > peaksize ) - { + private void sortUp() { + if (size > peaksize) { peaksize = size; } @@ -272,75 +241,66 @@ public final class SortedHeap int cnt = 8; // value count of first 2 bins is always 8 SortedBin tbin = second; // target bin SortedBin lastNonEmpty = second; - do - { + do { tbin = tbin.next(); int nentries = tbin.binsize - tbin.lp; - if ( nentries > 0 ) - { + if (nentries > 0) { cnt += nentries; lastNonEmpty = tbin; } } - while( cnt > tbin.binsize ); + while (cnt > tbin.binsize); int[] al_t = tbin.al; Object[] vla_t = tbin.vla; - int tp = tbin.binsize-cnt; // target pointer + int tp = tbin.binsize - cnt; // target pointer // unlink any higher, non-empty arrays SortedBin otherNonEmpty = lastNonEmpty.nextNonEmpty; lastNonEmpty.nextNonEmpty = null; // now merge the content of these non-empty bins into the target bin - while( firstNonEmpty != null ) - { + while (firstNonEmpty != null) { // copy current minimum to target array SortedBin minBin = firstNonEmpty.getMinBin(); al_t[tp] = minBin.lv; vla_t[tp++] = minBin.dropLowest(); } - tp = tbin.binsize-cnt; + tp = tbin.binsize - cnt; tbin.lp = tp; // new target low pointer tbin.lv = tbin.al[tp]; tbin.nextNonEmpty = otherNonEmpty; firstNonEmpty = tbin; } - public void clear() - { + public void clear() { size = 0; - first = new SortedBin( 4, this ); - second = new SortedBin( 4, this ); + first = new SortedBin(4, this); + second = new SortedBin(4, this); firstNonEmpty = null; } - public int getSize() - { + public int getSize() { return size; } - public int getPeakSize() - { + public int getPeakSize() { return peaksize; } - public int getExtract( Object[] targetArray ) - { + public int getExtract(Object[] targetArray) { int tsize = targetArray.length; int div = size / tsize + 1; int tp = 0; int lpi = 0; SortedBin bin = firstNonEmpty; - while( bin != null ) - { + while (bin != null) { lpi += bin.lp; Object[] vlai = bin.vla; int n = bin.binsize; - while (lpi < n) - { + while (lpi < n) { targetArray[tp++] = vlai[lpi]; lpi += div; } diff --git a/brouter-util/src/main/java/btools/util/StackSampler.java b/brouter-util/src/main/java/btools/util/StackSampler.java index 86e0f2e..6b2e304 100644 --- a/brouter-util/src/main/java/btools/util/StackSampler.java +++ b/brouter-util/src/main/java/btools/util/StackSampler.java @@ -11,137 +11,108 @@ import java.util.Locale; import java.util.Map; import java.util.Random; -public class StackSampler extends Thread -{ - private DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss,SSS", new Locale( "en", "US" ) ); +public class StackSampler extends Thread { + private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS", new Locale("en", "US")); private BufferedWriter bw; private Random rand = new Random(); private int interval; private int flushCnt = 0; - + private volatile boolean stopped; - public StackSampler( File logfile, int interval ) - { + public StackSampler(File logfile, int interval) { this.interval = interval; - try - { - bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( logfile, true ) ) ); - } - catch (Exception e) - { - printError( "StackSampler: " + e.getMessage() ); + try { + bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logfile, true))); + } catch (Exception e) { + printError("StackSampler: " + e.getMessage()); } } - protected void printError( String msg ) - { - System.out.println( msg ); + protected void printError(String msg) { + System.out.println(msg); } @Override - public void run() - { - while( !stopped ) - { + public void run() { + while (!stopped) { dumpThreads(); } - if ( bw != null ) - { - try - { + if (bw != null) { + try { bw.close(); + } catch (Exception e) { } - catch( Exception e ) {} } } - public void dumpThreads() - { - try - { - int wait1 = rand.nextInt( interval ); + public void dumpThreads() { + try { + int wait1 = rand.nextInt(interval); int wait2 = interval - wait1; - Thread.sleep( wait1 ); - StringBuilder sb = new StringBuilder( df.format( new Date() ) + " THREADDUMP\n" ); + Thread.sleep(wait1); + StringBuilder sb = new StringBuilder(df.format(new Date()) + " THREADDUMP\n"); Map allThreads = Thread.getAllStackTraces(); - for ( Map.Entry e : allThreads.entrySet() ) - { + for (Map.Entry e : allThreads.entrySet()) { Thread t = e.getKey(); - if ( t == Thread.currentThread() ) - { + if (t == Thread.currentThread()) { continue; // not me } StackTraceElement[] stack = e.getValue(); - if ( !matchesFilter( stack ) ) - { + if (!matchesFilter(stack)) { continue; } - sb.append( " (ID=" ).append( t.getId() ).append( " \"" ).append( t.getName() ).append( "\" " ).append( t.getState() ).append( "\n" ); - for ( StackTraceElement line : stack ) - { - sb.append( " " ).append( line.toString() ).append( "\n" ); + sb.append(" (ID=").append(t.getId()).append(" \"").append(t.getName()).append("\" ").append(t.getState()).append("\n"); + for (StackTraceElement line : stack) { + sb.append(" ").append(line.toString()).append("\n"); } - sb.append( "\n" ); + sb.append("\n"); } - bw.write( sb.toString() ); - if ( flushCnt++ >= 0 ) - { + bw.write(sb.toString()); + if (flushCnt++ >= 0) { flushCnt = 0; bw.flush(); } - Thread.sleep( wait2 ); - } - catch (Exception e) - { + Thread.sleep(wait2); + } catch (Exception e) { // ignore } } - public void close() - { + public void close() { stopped = true; interrupt(); } - private boolean matchesFilter( StackTraceElement[] stack ) - { + private boolean matchesFilter(StackTraceElement[] stack) { boolean positiveMatch = false; - for ( StackTraceElement e : stack ) - { + for (StackTraceElement e : stack) { String s = e.toString(); - if ( s.indexOf( "btools" ) >= 0 ) - { + if (s.indexOf("btools") >= 0) { positiveMatch = true; } - if ( s.indexOf( "Thread.sleep" ) >= 0 || s.indexOf( "PlainSocketImpl.socketAccept" ) >= 0 ) - { + if (s.indexOf("Thread.sleep") >= 0 || s.indexOf("PlainSocketImpl.socketAccept") >= 0) { return false; } } return positiveMatch; } - public static void main( String[] args ) throws Exception - { - System.out.println( "StackSampler..." ); - Class clazz = Class.forName( args[0] ); + public static void main(String[] args) throws Exception { + System.out.println("StackSampler..."); + Class clazz = Class.forName(args[0]); String[] args2 = new String[args.length - 1]; - for ( int i = 1; i < args.length; i++ ) - { + for (int i = 1; i < args.length; i++) { args2[i - 1] = args[i]; } - StackSampler t = new StackSampler( new File( "stacks.log" ), 1000 ); + StackSampler t = new StackSampler(new File("stacks.log"), 1000); t.start(); - try - { - clazz.getMethod( "main", String[].class ).invoke( null, new Object[]{ args2 } ); - } - finally - { + try { + clazz.getMethod("main", String[].class).invoke(null, new Object[]{args2}); + } finally { t.close(); } } diff --git a/brouter-util/src/main/java/btools/util/StringUtils.java b/brouter-util/src/main/java/btools/util/StringUtils.java index 3e8c7f8..708ba36 100644 --- a/brouter-util/src/main/java/btools/util/StringUtils.java +++ b/brouter-util/src/main/java/btools/util/StringUtils.java @@ -3,53 +3,44 @@ package btools.util; /** * Some methods for String handling */ -public class StringUtils -{ - private static char[] xmlChr = new char[] { '&', '<', '>', '\'', '"', '\t', '\n', '\r' }; - private static String[] xmlEsc = new String[]{ "&", "<", ">", "'", """, " ", " ", " " }; +public class StringUtils { + private static char[] xmlChr = new char[]{'&', '<', '>', '\'', '"', '\t', '\n', '\r'}; + private static String[] xmlEsc = new String[]{"&", "<", ">", "'", """, " ", " ", " "}; - private static char[] jsnChr = new char[] { '\'', '"', '\\', '/' }; - private static String[] jsnEsc = new String[]{ "\\'", "\\\"", "\\\\", "\\/" }; + private static char[] jsnChr = new char[]{'\'', '"', '\\', '/'}; + private static String[] jsnEsc = new String[]{"\\'", "\\\"", "\\\\", "\\/"}; /** * Escape a literal to put into a json document */ - public static String escapeJson( String s ) - { - return escape( s, jsnChr, jsnEsc ); + public static String escapeJson(String s) { + return escape(s, jsnChr, jsnEsc); } /** * Escape a literal to put into a xml document */ - public static String escapeXml10( String s ) - { - return escape( s, xmlChr, xmlEsc ); + public static String escapeXml10(String s) { + return escape(s, xmlChr, xmlEsc); } - private static String escape( String s, char[] chr, String[] esc ) - { + private static String escape(String s, char[] chr, String[] esc) { StringBuilder sb = null; - for( int i=0; i>= 1; - pa[idx++] = n; - n <<= 1; - } - - // create it if not existant - if ( al[idx] == null ) - { - al[idx] = new long[n]; - vla[idx] = new byte[n]; - } - - // now merge the contents of arrays 0...idx-1 into idx - while ( n > 0 ) - { - long maxId = 0; - int maxIdx = -1; - - for ( int i=0; i 0 ) - { - long currentId = al[i][p-1]; - if ( maxIdx < 0 || currentId > maxId ) - { - maxIdx = i; - maxId = currentId; - } - } - } - - // current maximum found, copy to target array - if ( n < al[idx].length && maxId == al[idx][n] ) - { - throw new IllegalArgumentException( "duplicate key found in late check: " + maxId ); - } - --n; - al[idx][n] = maxId; - vla[idx][n] = vla[maxIdx][pa[maxIdx]-1]; - - --pa[maxIdx]; - } - - // de-allocate empty arrays of a certain size (fix at 64kByte) - while ( idx-- > _maxKeepExponent ) - { - al[idx] = null; - vla[idx] = null; - } - - return false; - } - - - private boolean contains( long id, byte[] rv ) - { - // determine the first empty array - int bp = size; // treat size as bitpattern - int idx = 1; - - while ( bp != 0 ) - { - if ( (bp&1) == 1 ) - { - // array at idx is valid, check - if ( contains( idx, id, rv ) ) - { - return true; - } - } - idx++; - bp >>= 1; - } - return false; - } - - - // does sorted array "a" contain "id" ? - private boolean contains( int idx, long id, byte[] rv ) - { - long[] a = al[idx]; - int offset = a.length; - int n = 0; - - while ( (offset >>= 1) > 0 ) - { - int nn = n + offset; - if ( a[nn] <= id ) - { - n = nn; - } - } - if ( a[n] == id ) - { - if ( rv != null ) - { - fillReturnValue( rv, idx, n ); - } - return true; - } - return false; - } - -} +package btools.util; + +/** + * TinyDenseLongMap implements the DenseLongMap interface + * but actually is made for a medium count of non-dense keys + *

+ * It's used as a replacement for DenseLongMap where we + * have limited memory and far less keys than maykey + * + * @author ab + */ +public class TinyDenseLongMap extends DenseLongMap { + private long[][] al; + private int[] pa; + private int size = 0; + private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays + + protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE + + public TinyDenseLongMap() { + super(); + + // pointer array + pa = new int[MAXLISTS]; + + // allocate key lists + al = new long[MAXLISTS][]; + al[0] = new long[1]; // make the first array (the transient buffer) + + // same for the values + vla = new byte[MAXLISTS][]; + vla[0] = new byte[1]; + } + + + private byte[][] vla; // value list array + + private void fillReturnValue(byte[] rv, int idx, int p) { + rv[0] = vla[idx][p]; + if (rv.length == 2) { + vla[idx][p] = rv[1]; + } + } + + @Override + public void put(long id, int value) { + byte[] rv = new byte[2]; + rv[1] = (byte) value; + if (contains(id, rv)) { + return; + } + + vla[0][0] = (byte) value; + _add(id); + } + + + /** + * Get the byte for the given id + * + * @param id the key to query + * @return the object + * @throws IllegalArgumentException if id is unknown + */ + @Override + public int getInt(long id) { + byte[] rv = new byte[1]; + if (contains(id, rv)) { + return rv[0]; + } + return -1; + } + + + private boolean _add(long id) { + if (size == Integer.MAX_VALUE) { + throw new IllegalArgumentException("cannot grow beyond size Integer.MAX_VALUE"); + } + + // put the new entry in the first array + al[0][0] = id; + + // determine the first empty array + int bp = size++; // treat size as bitpattern + int idx = 1; + int n = 1; + + pa[0] = 1; + pa[1] = 1; + + while ((bp & 1) == 1) { + bp >>= 1; + pa[idx++] = n; + n <<= 1; + } + + // create it if not existant + if (al[idx] == null) { + al[idx] = new long[n]; + vla[idx] = new byte[n]; + } + + // now merge the contents of arrays 0...idx-1 into idx + while (n > 0) { + long maxId = 0; + int maxIdx = -1; + + for (int i = 0; i < idx; i++) { + int p = pa[i]; + if (p > 0) { + long currentId = al[i][p - 1]; + if (maxIdx < 0 || currentId > maxId) { + maxIdx = i; + maxId = currentId; + } + } + } + + // current maximum found, copy to target array + if (n < al[idx].length && maxId == al[idx][n]) { + throw new IllegalArgumentException("duplicate key found in late check: " + maxId); + } + --n; + al[idx][n] = maxId; + vla[idx][n] = vla[maxIdx][pa[maxIdx] - 1]; + + --pa[maxIdx]; + } + + // de-allocate empty arrays of a certain size (fix at 64kByte) + while (idx-- > _maxKeepExponent) { + al[idx] = null; + vla[idx] = null; + } + + return false; + } + + + private boolean contains(long id, byte[] rv) { + // determine the first empty array + int bp = size; // treat size as bitpattern + int idx = 1; + + while (bp != 0) { + if ((bp & 1) == 1) { + // array at idx is valid, check + if (contains(idx, id, rv)) { + return true; + } + } + idx++; + bp >>= 1; + } + return false; + } + + + // does sorted array "a" contain "id" ? + private boolean contains(int idx, long id, byte[] rv) { + long[] a = al[idx]; + int offset = a.length; + int n = 0; + + while ((offset >>= 1) > 0) { + int nn = n + offset; + if (a[nn] <= id) { + n = nn; + } + } + if (a[n] == id) { + if (rv != null) { + fillReturnValue(rv, idx, n); + } + return true; + } + return false; + } + +} diff --git a/brouter-util/src/test/java/btools/util/BitCoderContextTest.java b/brouter-util/src/test/java/btools/util/BitCoderContextTest.java index 084a29c..619f96a 100644 --- a/brouter-util/src/test/java/btools/util/BitCoderContextTest.java +++ b/brouter-util/src/test/java/btools/util/BitCoderContextTest.java @@ -3,61 +3,49 @@ package btools.util; import org.junit.Assert; import org.junit.Test; -public class BitCoderContextTest -{ +public class BitCoderContextTest { @Test - public void varBitsEncodeDecodeTest() - { + public void varBitsEncodeDecodeTest() { byte[] ab = new byte[581969]; - BitCoderContext ctx = new BitCoderContext( ab ); - for ( int i = 0; i < 31; i++ ) - { - ctx.encodeVarBits( (1<" + val ); + for (int max = 1; max < 1000; max++) { + for (int val = 0; val <= max; val++) { + int valDecoded = ctx.decodeBounded(max); + if (valDecoded != val) { + Assert.fail("mismatch at max=" + max + " " + valDecoded + "<>" + val); } } } diff --git a/brouter-util/src/test/java/btools/util/ByteDataIOTest.java b/brouter-util/src/test/java/btools/util/ByteDataIOTest.java index b6f1474..82fee2f 100644 --- a/brouter-util/src/test/java/btools/util/ByteDataIOTest.java +++ b/brouter-util/src/test/java/btools/util/ByteDataIOTest.java @@ -6,23 +6,19 @@ import java.util.HashSet; import org.junit.Assert; import org.junit.Test; -public class ByteDataIOTest -{ +public class ByteDataIOTest { @Test - public void varLengthEncodeDecodeTest() - { - byte[] ab = new byte[4000]; - ByteDataWriter w = new ByteDataWriter( ab ); - for( int i=0; i<1000; i++ ) - { - w.writeVarLengthUnsigned( i ); + public void varLengthEncodeDecodeTest() { + byte[] ab = new byte[4000]; + ByteDataWriter w = new ByteDataWriter(ab); + for (int i = 0; i < 1000; i++) { + w.writeVarLengthUnsigned(i); } - ByteDataReader r = new ByteDataReader( ab ); - - for( int i=0; i<1000; i++ ) - { + ByteDataReader r = new ByteDataReader(ab); + + for (int i = 0; i < 1000; i++) { int value = r.readVarLengthUnsigned(); - Assert.assertTrue( "value mismatch", value == i ); + Assert.assertTrue("value mismatch", value == i); } } } diff --git a/brouter-util/src/test/java/btools/util/CheapAngleMeterTest.java b/brouter-util/src/test/java/btools/util/CheapAngleMeterTest.java index a086cc7..e5b7a3b 100644 --- a/brouter-util/src/test/java/btools/util/CheapAngleMeterTest.java +++ b/brouter-util/src/test/java/btools/util/CheapAngleMeterTest.java @@ -10,11 +10,11 @@ import org.junit.Test; public class CheapAngleMeterTest { 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 @@ -79,40 +79,38 @@ public class CheapAngleMeterTest { @Test public void testCalcAngle2() { CheapAngleMeter am = new CheapAngleMeter(); - int lon1 = 8500000; + int lon1 = 8500000; int lat1 = 49500000; - - double[] lonlat2m = CheapRuler.getLonLatToMeterScales( lat1 ); + + double[] lonlat2m = CheapRuler.getLonLatToMeterScales(lat1); double lon2m = lonlat2m[0]; double lat2m = lonlat2m[1]; - for ( double afrom = -175.; afrom < 180.; afrom += 10. ) - { - double sf = Math.sin( afrom * Math.PI / 180. ); - double cf = Math.cos( afrom * Math.PI / 180. ); - - int lon0 = (int)(0.5+lon1 - cf*150./lon2m ); - int lat0 = (int)(0.5+lat1 - sf*150./lat2m ); - - for ( double ato = -177.; ato < 180.; ato += 10. ) - { - double st = Math.sin( ato * Math.PI / 180. ); - double ct = Math.cos( ato * Math.PI / 180. ); - - int lon2 = (int)(0.5+lon1 + ct*250./lon2m); - int lat2 = (int)(0.5+lat1 + st*250./lat2m); - + for (double afrom = -175.; afrom < 180.; afrom += 10.) { + double sf = Math.sin(afrom * Math.PI / 180.); + double cf = Math.cos(afrom * Math.PI / 180.); + + int lon0 = (int) (0.5 + lon1 - cf * 150. / lon2m); + int lat0 = (int) (0.5 + lat1 - sf * 150. / lat2m); + + for (double ato = -177.; ato < 180.; ato += 10.) { + double st = Math.sin(ato * Math.PI / 180.); + double ct = Math.cos(ato * Math.PI / 180.); + + int lon2 = (int) (0.5 + lon1 + ct * 250. / lon2m); + int lat2 = (int) (0.5 + lat1 + st * 250. / lat2m); + double a1 = afrom - ato; - if ( a1 > 180. ) a1 -= 360.; - if ( a1 < -180. ) a1 += 360.; - double a2 = am.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 ); - double c1 = Math.cos( a1 * Math.PI / 180. ); + if (a1 > 180.) a1 -= 360.; + if (a1 < -180.) a1 += 360.; + double a2 = am.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2); + double c1 = Math.cos(a1 * Math.PI / 180.); double c2 = am.getCosAngle(); - assertEquals( "angle mismatch for afrom=" + afrom + " ato=" + ato, a1, a2, 0.2 ); - assertEquals( "cosinus mismatch for afrom=" + afrom + " ato=" + ato, c1, c2, 0.001 ); + assertEquals("angle mismatch for afrom=" + afrom + " ato=" + ato, a1, a2, 0.2); + assertEquals("cosinus mismatch for afrom=" + afrom + " ato=" + ato, c1, c2, 0.001); } } - } + } } diff --git a/brouter-util/src/test/java/btools/util/CompactMapTest.java b/brouter-util/src/test/java/btools/util/CompactMapTest.java index 4149690..8303875 100644 --- a/brouter-util/src/test/java/btools/util/CompactMapTest.java +++ b/brouter-util/src/test/java/btools/util/CompactMapTest.java @@ -6,62 +6,54 @@ import java.util.HashMap; import org.junit.Assert; import org.junit.Test; -public class CompactMapTest -{ +public class CompactMapTest { @Test - public void hashMapComparisonTest() - { - hashMapComparison( 0, 1 ); - hashMapComparison( 1, 1 ); - hashMapComparison( 2, 2 ); - hashMapComparison( 3, 3 ); - hashMapComparison( 4, 4 ); - hashMapComparison( 5, 5 ); - hashMapComparison( 7, 10 ); - hashMapComparison( 8, 10 ); - hashMapComparison( 10000, 20000 ); + public void hashMapComparisonTest() { + hashMapComparison(0, 1); + hashMapComparison(1, 1); + hashMapComparison(2, 2); + hashMapComparison(3, 3); + hashMapComparison(4, 4); + hashMapComparison(5, 5); + hashMapComparison(7, 10); + hashMapComparison(8, 10); + hashMapComparison(10000, 20000); } - private void hashMapComparison( int mapsize, int trycount ) - { - Random rand = new Random( 12345 ); - HashMap hmap = new HashMap(); + private void hashMapComparison(int mapsize, int trycount) { + Random rand = new Random(12345); + HashMap hmap = new HashMap(); CompactLongMap cmap_slow = new CompactLongMap(); CompactLongMap cmap_fast = new CompactLongMap(); - for( int i=0; i( cmap_slow ); - cmap_fast = new FrozenLongMap( cmap_fast ); + for (int i = 0; i < trycount * 2; i++) { + if (i == trycount) { + cmap_slow = new FrozenLongMap(cmap_slow); + cmap_fast = new FrozenLongMap(cmap_fast); } - long k = mapsize < 10 ? i : rand.nextInt( 20000 ); - Long KK = new Long( k ); - String s = hmap.get( KK ); + long k = mapsize < 10 ? i : rand.nextInt(20000); + Long KK = new Long(k); + String s = hmap.get(KK); - boolean contained = hmap.containsKey( KK ); - Assert.assertTrue( "containsKey missmatch (slow)", contained == cmap_slow.contains( k ) ); - Assert.assertTrue( "containsKey missmatch (fast)", contained == cmap_fast.contains( k ) ); - - if ( contained ) - { - Assert.assertEquals( "object missmatch (fast)", s, cmap_fast.get( k ) ); - Assert.assertEquals( "object missmatch (slow)", s, cmap_slow.get( k ) ); + boolean contained = hmap.containsKey(KK); + Assert.assertTrue("containsKey missmatch (slow)", contained == cmap_slow.contains(k)); + Assert.assertTrue("containsKey missmatch (fast)", contained == cmap_fast.contains(k)); + + if (contained) { + Assert.assertEquals("object missmatch (fast)", s, cmap_fast.get(k)); + Assert.assertEquals("object missmatch (slow)", s, cmap_slow.get(k)); } } } diff --git a/brouter-util/src/test/java/btools/util/CompactSetTest.java b/brouter-util/src/test/java/btools/util/CompactSetTest.java index 30987ec..68026ad 100644 --- a/brouter-util/src/test/java/btools/util/CompactSetTest.java +++ b/brouter-util/src/test/java/btools/util/CompactSetTest.java @@ -6,55 +6,48 @@ import java.util.HashSet; import org.junit.Assert; import org.junit.Test; -public class CompactSetTest -{ +public class CompactSetTest { @Test - public void hashSetComparisonTest() - { - hashSetComparison( 0, 1 ); - hashSetComparison( 1, 1 ); - hashSetComparison( 2, 2 ); - hashSetComparison( 3, 3 ); - hashSetComparison( 4, 4 ); - hashSetComparison( 5, 5 ); - hashSetComparison( 7, 10 ); - hashSetComparison( 8, 10 ); - hashSetComparison( 10000, 20000 ); + public void hashSetComparisonTest() { + hashSetComparison(0, 1); + hashSetComparison(1, 1); + hashSetComparison(2, 2); + hashSetComparison(3, 3); + hashSetComparison(4, 4); + hashSetComparison(5, 5); + hashSetComparison(7, 10); + hashSetComparison(8, 10); + hashSetComparison(10000, 20000); } - private void hashSetComparison( int setsize, int trycount ) - { - Random rand = new Random( 12345 ); + private void hashSetComparison(int setsize, int trycount) { + Random rand = new Random(12345); HashSet hset = new HashSet(); CompactLongSet cset_slow = new CompactLongSet(); CompactLongSet cset_fast = new CompactLongSet(); - for( int i=0; i hmap = new HashMap(); - DenseLongMap dmap = new DenseLongMap( 512 ); + private void hashMapComparison(int mapsize, int trycount, long keyrange) { + Random rand = new Random(12345); + HashMap hmap = new HashMap(); + DenseLongMap dmap = new DenseLongMap(512); - for( int i=0; i (keyrange / 8) * 7) { + Assert.fail("memory usage too high: " + memusage + " for keyrange " + keyrange); } // need to use the map again for valid memory measure - Assert.assertTrue( "out of range test", dmap.getInt(-1) == -1 ); + Assert.assertTrue("out of range test", dmap.getInt(-1) == -1); } } diff --git a/brouter-util/src/test/java/btools/util/MixCoderTest.java b/brouter-util/src/test/java/btools/util/MixCoderTest.java index b2cdb2c..87c4c83 100644 --- a/brouter-util/src/test/java/btools/util/MixCoderTest.java +++ b/brouter-util/src/test/java/btools/util/MixCoderTest.java @@ -6,57 +6,44 @@ import java.io.*; import org.junit.Assert; import org.junit.Test; -public class MixCoderTest -{ +public class MixCoderTest { @Test - public void mixEncodeDecodeTest() throws IOException - { + public void mixEncodeDecodeTest() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - MixCoderDataOutputStream mco = new MixCoderDataOutputStream( baos ); + MixCoderDataOutputStream mco = new MixCoderDataOutputStream(baos); MixCoderDataInputStream mci = null; - for(;;) - { - Random rnd = new Random( 1234 ); - for( int i=0; i<1500; i++ ) - { - checkEncodeDecode( rnd.nextInt( 3800 ), mco, mci ); + for (; ; ) { + Random rnd = new Random(1234); + for (int i = 0; i < 1500; i++) { + checkEncodeDecode(rnd.nextInt(3800), mco, mci); } - for( int i=0; i<1500; i++ ) - { - checkEncodeDecode( rnd.nextInt( 35 ), mco, mci ); + for (int i = 0; i < 1500; i++) { + checkEncodeDecode(rnd.nextInt(35), mco, mci); } - for( int i=0; i<1500; i++ ) - { - checkEncodeDecode( 0, mco, mci ); + for (int i = 0; i < 1500; i++) { + checkEncodeDecode(0, mco, mci); } - for( int i=0; i<1500; i++ ) - { - checkEncodeDecode( 1000, mco, mci ); + for (int i = 0; i < 1500; i++) { + checkEncodeDecode(1000, mco, mci); } - if ( mco != null ) - { + if (mco != null) { mco.close(); mco = null; - mci = new MixCoderDataInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); - } - else break; + mci = new MixCoderDataInputStream(new ByteArrayInputStream(baos.toByteArray())); + } else break; } } - private void checkEncodeDecode( int v, MixCoderDataOutputStream mco, MixCoderDataInputStream mci ) throws IOException - { - if ( mco != null ) - { - mco.writeMixed( v ); + private void checkEncodeDecode(int v, MixCoderDataOutputStream mco, MixCoderDataInputStream mci) throws IOException { + if (mco != null) { + mco.writeMixed(v); } - if ( mci != null ) - { + if (mci != null) { long vv = mci.readMixed(); - if ( vv != v ) - { - Assert.assertTrue( "value mismatch: v=" + v + " vv=" + vv, false ); + if (vv != v) { + Assert.assertTrue("value mismatch: v=" + v + " vv=" + vv, false); } } } diff --git a/brouter-util/src/test/java/btools/util/ReducedMedianFilterTest.java b/brouter-util/src/test/java/btools/util/ReducedMedianFilterTest.java index c8ca7e9..062073a 100644 --- a/brouter-util/src/test/java/btools/util/ReducedMedianFilterTest.java +++ b/brouter-util/src/test/java/btools/util/ReducedMedianFilterTest.java @@ -6,41 +6,38 @@ import java.io.*; import org.junit.Assert; import org.junit.Test; -public class ReducedMedianFilterTest -{ +public class ReducedMedianFilterTest { @Test - public void reducedMedianFilterTest() throws IOException - { - ReducedMedianFilter f = new ReducedMedianFilter( 10 ); + public void reducedMedianFilterTest() throws IOException { + ReducedMedianFilter f = new ReducedMedianFilter(10); f.reset(); - f.addSample( .2, 10 ); - f.addSample( .2, 10 ); - f.addSample( .2, 10 ); - f.addSample( .2, 15 ); - f.addSample( .2, 20 ); - - double m = f.calcEdgeReducedMedian( 0.5 ); - Assert.assertTrue( "median1 mismatch m=" + m + " expected 11.5", doubleEquals( m, 11.5 ) ); + f.addSample(.2, 10); + f.addSample(.2, 10); + f.addSample(.2, 10); + f.addSample(.2, 15); + f.addSample(.2, 20); + + double m = f.calcEdgeReducedMedian(0.5); + Assert.assertTrue("median1 mismatch m=" + m + " expected 11.5", doubleEquals(m, 11.5)); f.reset(); - f.addSample( .2, 10 ); - f.addSample( .2, 10 ); - f.addSample( .2, 10 ); - f.addSample( .2, 10 ); - f.addSample( .2, 20 ); + f.addSample(.2, 10); + f.addSample(.2, 10); + f.addSample(.2, 10); + f.addSample(.2, 10); + f.addSample(.2, 20); - m = f.calcEdgeReducedMedian( 1. ); - Assert.assertTrue( "median1 mismatch m=" + m + " expected 12", doubleEquals( m, 12. ) ); + m = f.calcEdgeReducedMedian(1.); + Assert.assertTrue("median1 mismatch m=" + m + " expected 12", doubleEquals(m, 12.)); f.reset(); - f.addSample( .5, -10 ); - f.addSample( .5, 10 ); - m = f.calcEdgeReducedMedian( 0.5 ); - Assert.assertTrue( "median2 mismatch m=" + m + " expected 0", doubleEquals( m, 0. ) ); + f.addSample(.5, -10); + f.addSample(.5, 10); + m = f.calcEdgeReducedMedian(0.5); + Assert.assertTrue("median2 mismatch m=" + m + " expected 0", doubleEquals(m, 0.)); } - private boolean doubleEquals( double d1, double d2 ) - { + private boolean doubleEquals(double d1, double d2) { double d = d1 - d2; return d < 1e-9 && d > -1e-9; } diff --git a/brouter-util/src/test/java/btools/util/SortedHeapTest.java b/brouter-util/src/test/java/btools/util/SortedHeapTest.java index ed289e5..d2538d7 100644 --- a/brouter-util/src/test/java/btools/util/SortedHeapTest.java +++ b/brouter-util/src/test/java/btools/util/SortedHeapTest.java @@ -7,59 +7,52 @@ import java.util.HashSet; import org.junit.Assert; import org.junit.Test; -public class SortedHeapTest -{ +public class SortedHeapTest { @Test - public void sortedHeapTest1() - { - SortedHeap sh = new SortedHeap(); - Random rnd = new Random(); - for( int i = 0; i< 100000; i++ ) - { - int val = rnd.nextInt( 1000000 ); - sh.add( val, "" + val ); - val = rnd.nextInt( 1000000 ); - sh.add( val, "" + val ); - sh.popLowestKeyValue(); - } + public void sortedHeapTest1() { + SortedHeap sh = new SortedHeap(); + Random rnd = new Random(); + for (int i = 0; i < 100000; i++) { + int val = rnd.nextInt(1000000); + sh.add(val, "" + val); + val = rnd.nextInt(1000000); + sh.add(val, "" + val); + sh.popLowestKeyValue(); + } - int cnt = 0; - int lastval = 0; - for(;;) - { - String s = sh.popLowestKeyValue(); - if ( s == null ) break; - cnt ++; - int val = Integer.parseInt( s ); - Assert.assertTrue( "sorting test", val >= lastval ); - lastval = val; - } - Assert.assertTrue( "total count test", cnt == 100000 ); + int cnt = 0; + int lastval = 0; + for (; ; ) { + String s = sh.popLowestKeyValue(); + if (s == null) break; + cnt++; + int val = Integer.parseInt(s); + Assert.assertTrue("sorting test", val >= lastval); + lastval = val; + } + Assert.assertTrue("total count test", cnt == 100000); } @Test - public void sortedHeapTest2() - { - SortedHeap sh = new SortedHeap(); - Random rnd = new Random(); - for( int i = 0; i< 100000; i++ ) - { - sh.add( i, "" + i ); - } + public void sortedHeapTest2() { + SortedHeap sh = new SortedHeap(); + Random rnd = new Random(); + for (int i = 0; i < 100000; i++) { + sh.add(i, "" + i); + } - int cnt = 0; - int expected = 0; - for(;;) - { - String s = sh.popLowestKeyValue(); - if ( s == null ) break; - cnt ++; - int val = Integer.parseInt( s ); - Assert.assertTrue( "sequence test", val == expected ); - expected++; - } - Assert.assertTrue( "total count test", cnt == 100000 ); + int cnt = 0; + int expected = 0; + for (; ; ) { + String s = sh.popLowestKeyValue(); + if (s == null) break; + cnt++; + int val = Integer.parseInt(s); + Assert.assertTrue("sequence test", val == expected); + expected++; + } + Assert.assertTrue("total count test", cnt == 100000); } } diff --git a/brouter-util/src/test/java/btools/util/StringUtilsTest.java b/brouter-util/src/test/java/btools/util/StringUtilsTest.java index 118116f..828ffb3 100644 --- a/brouter-util/src/test/java/btools/util/StringUtilsTest.java +++ b/brouter-util/src/test/java/btools/util/StringUtilsTest.java @@ -3,27 +3,22 @@ package btools.util; import org.junit.Assert; import org.junit.Test; -public class StringUtilsTest -{ - private static String[] raw = new String[] { "hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", "\"hi\" 'there'" }; - private static String[] xml = new String[] { "hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", ""hi" 'there'" }; - private static String[] jsn = new String[] { "hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", "\\\"hi\\\" \\'there\\'" }; +public class StringUtilsTest { + private static String[] raw = new String[]{"hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", "\"hi\" 'there'"}; + private static String[] xml = new String[]{"hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", ""hi" 'there'"}; + private static String[] jsn = new String[]{"hallo", "is 1<2 ?", "or 4>5 ?", "or 1<>2 ?", "\\\"hi\\\" \\'there\\'"}; @Test - public void xmlEncodingTest() - { - for( int i=0; i + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index c1d43b6..0755c89 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -5,11 +5,4 @@ "https://checkstyle.org/dtds/suppressions_1_2.dtd"> - - - - - - - diff --git a/misc/pbfparser/BPbfBlobDecoder.java b/misc/pbfparser/BPbfBlobDecoder.java index 06ba406..8929673 100644 --- a/misc/pbfparser/BPbfBlobDecoder.java +++ b/misc/pbfparser/BPbfBlobDecoder.java @@ -1,8 +1,10 @@ package btools.mapcreator; import com.google.protobuf.InvalidProtocolBufferException; + import org.openstreetmap.osmosis.osmbinary.Fileformat; import org.openstreetmap.osmosis.osmbinary.Osmformat; + import btools.util.LongList; import java.io.IOException; @@ -15,277 +17,240 @@ import java.util.zip.Inflater; * Converts PBF block data into decoded entities ready to be passed into an Osmosis pipeline. This * class is designed to be passed into a pool of worker threads to allow multi-threaded decoding. *

+ * * @author Brett Henderson */ -public class BPbfBlobDecoder -{ - private String blobType; - private byte[] rawBlob; +public class BPbfBlobDecoder { + private String blobType; + private byte[] rawBlob; - private OsmParser parser; + private OsmParser parser; - /** - * Creates a new instance. - *

- * @param blobType The type of blob. - * @param rawBlob The raw data of the blob. - * @param listener The listener for receiving decoding results. - */ - public BPbfBlobDecoder( String blobType, byte[] rawBlob, OsmParser parser ) - { - this.blobType = blobType; - this.rawBlob = rawBlob; - this.parser = parser; + /** + * Creates a new instance. + *

+ * + * @param blobType The type of blob. + * @param rawBlob The raw data of the blob. + * @param listener The listener for receiving decoding results. + */ + public BPbfBlobDecoder(String blobType, byte[] rawBlob, OsmParser parser) { + this.blobType = blobType; + this.rawBlob = rawBlob; + this.parser = parser; + } + + public void process() throws Exception { + if ("OSMHeader".equals(blobType)) { + processOsmHeader(readBlobContent()); + + } else if ("OSMData".equals(blobType)) { + processOsmPrimitives(readBlobContent()); + + } else { + System.out.println("Skipping unrecognised blob type " + blobType); } + } - public void process() throws Exception - { - if ("OSMHeader".equals(blobType)) - { - processOsmHeader(readBlobContent()); + private byte[] readBlobContent() throws IOException { + Fileformat.Blob blob = Fileformat.Blob.parseFrom(rawBlob); + byte[] blobData; - } else if ("OSMData".equals(blobType)) - { - processOsmPrimitives(readBlobContent()); - - } else - { - System.out.println("Skipping unrecognised blob type " + blobType); - } - } - - private byte[] readBlobContent() throws IOException - { - Fileformat.Blob blob = Fileformat.Blob.parseFrom(rawBlob); - byte[] blobData; - - if (blob.hasRaw()) - { - blobData = blob.getRaw().toByteArray(); - } else if (blob.hasZlibData()) - { - Inflater inflater = new Inflater(); - inflater.setInput(blob.getZlibData().toByteArray()); - blobData = new byte[blob.getRawSize()]; - try - { - inflater.inflate(blobData); - } catch (DataFormatException e) - { - throw new RuntimeException("Unable to decompress PBF blob.", e); - } - if (!inflater.finished()) - { - throw new RuntimeException("PBF blob contains incomplete compressed data."); - } - } else - { - throw new RuntimeException("PBF blob uses unsupported compression, only raw or zlib may be used."); - } - - return blobData; - } - - private void processOsmHeader( byte[] data ) throws InvalidProtocolBufferException - { - Osmformat.HeaderBlock header = Osmformat.HeaderBlock.parseFrom(data); - - // Build the list of active and unsupported features in the file. - List supportedFeatures = Arrays.asList("OsmSchema-V0.6", "DenseNodes"); - List activeFeatures = new ArrayList(); - List unsupportedFeatures = new ArrayList(); - for (String feature : header.getRequiredFeaturesList()) - { - if (supportedFeatures.contains(feature)) - { - activeFeatures.add(feature); - } else - { - unsupportedFeatures.add(feature); - } - } - - // We can't continue if there are any unsupported features. We wait - // until now so that we can display all unsupported features instead of - // just the first one we encounter. - if (unsupportedFeatures.size() > 0) - { - throw new RuntimeException("PBF file contains unsupported features " + unsupportedFeatures); - } - - } - - private Map buildTags( List keys, List values, BPbfFieldDecoder fieldDecoder ) - { - - Iterator keyIterator = keys.iterator(); - Iterator valueIterator = values.iterator(); - if (keyIterator.hasNext()) - { - Map tags = new HashMap(); - while (keyIterator.hasNext()) - { - String key = fieldDecoder.decodeString(keyIterator.next()); - String value = fieldDecoder.decodeString(valueIterator.next()); - tags.put(key, value); - } - return tags; - } - return null; - } - - private void processNodes( List nodes, BPbfFieldDecoder fieldDecoder ) - { - for (Osmformat.Node node : nodes) - { - Map tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder); - - parser.addNode( node.getId(), tags, fieldDecoder.decodeLatitude(node - .getLat()), fieldDecoder.decodeLatitude(node.getLon())); - } - } - - private void processNodes( Osmformat.DenseNodes nodes, BPbfFieldDecoder fieldDecoder ) - { - List idList = nodes.getIdList(); - List latList = nodes.getLatList(); - List lonList = nodes.getLonList(); - - Iterator keysValuesIterator = nodes.getKeysValsList().iterator(); - - long nodeId = 0; - long latitude = 0; - long longitude = 0; - - for (int i = 0; i < idList.size(); i++) - { - // Delta decode node fields. - nodeId += idList.get(i); - latitude += latList.get(i); - longitude += lonList.get(i); - - // Build the tags. The key and value string indexes are sequential - // in the same PBF array. Each set of tags is delimited by an index - // with a value of 0. - Map tags = null; - while (keysValuesIterator.hasNext()) - { - int keyIndex = keysValuesIterator.next(); - if (keyIndex == 0) - { - break; - } - int valueIndex = keysValuesIterator.next(); - - if (tags == null) - { - tags = new HashMap(); - } - - tags.put(fieldDecoder.decodeString(keyIndex), fieldDecoder.decodeString(valueIndex)); - } - - parser.addNode( nodeId, tags, ((double) latitude) / 10000000, ((double) longitude) / 10000000); - } - } - - private void processWays( List ways, BPbfFieldDecoder fieldDecoder ) - { - for (Osmformat.Way way : ways) - { - Map tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder); - - // Build up the list of way nodes for the way. The node ids are - // delta encoded meaning that each id is stored as a delta against - // the previous one. - long nodeId = 0; - LongList wayNodes = new LongList( 16 ); - for (long nodeIdOffset : way.getRefsList()) - { - nodeId += nodeIdOffset; - wayNodes.add(nodeId); - } - - parser.addWay( way.getId(), tags, wayNodes ); - } - } - - private LongList fromWid; - private LongList toWid; - private LongList viaNid; - - private LongList addLong( LongList ll, long l ) - { - if ( ll == null ) - { - ll = new LongList( 1 ); + if (blob.hasRaw()) { + blobData = blob.getRaw().toByteArray(); + } else if (blob.hasZlibData()) { + Inflater inflater = new Inflater(); + inflater.setInput(blob.getZlibData().toByteArray()); + blobData = new byte[blob.getRawSize()]; + try { + inflater.inflate(blobData); + } catch (DataFormatException e) { + throw new RuntimeException("Unable to decompress PBF blob.", e); } - ll.add( l ); - return ll; + if (!inflater.finished()) { + throw new RuntimeException("PBF blob contains incomplete compressed data."); + } + } else { + throw new RuntimeException("PBF blob uses unsupported compression, only raw or zlib may be used."); } - private LongList buildRelationMembers( - List memberIds, List memberRoles, List memberTypes, - BPbfFieldDecoder fieldDecoder ) - { - LongList wayIds = new LongList( 16 ); + return blobData; + } - fromWid = toWid = viaNid = null; + private void processOsmHeader(byte[] data) throws InvalidProtocolBufferException { + Osmformat.HeaderBlock header = Osmformat.HeaderBlock.parseFrom(data); - Iterator memberIdIterator = memberIds.iterator(); - Iterator memberRoleIterator = memberRoles.iterator(); - Iterator memberTypeIterator = memberTypes.iterator(); + // Build the list of active and unsupported features in the file. + List supportedFeatures = Arrays.asList("OsmSchema-V0.6", "DenseNodes"); + List activeFeatures = new ArrayList(); + List unsupportedFeatures = new ArrayList(); + for (String feature : header.getRequiredFeaturesList()) { + if (supportedFeatures.contains(feature)) { + activeFeatures.add(feature); + } else { + unsupportedFeatures.add(feature); + } + } - // Build up the list of relation members for the way. The member ids are - // delta encoded meaning that each id is stored as a delta against - // the previous one. - long refId = 0; - while (memberIdIterator.hasNext()) - { - Osmformat.Relation.MemberType memberType = memberTypeIterator.next(); - refId += memberIdIterator.next(); + // We can't continue if there are any unsupported features. We wait + // until now so that we can display all unsupported features instead of + // just the first one we encounter. + if (unsupportedFeatures.size() > 0) { + throw new RuntimeException("PBF file contains unsupported features " + unsupportedFeatures); + } - String role = fieldDecoder.decodeString( memberRoleIterator.next() ); + } - if ( memberType == Osmformat.Relation.MemberType.WAY ) // currently just waymembers - { - wayIds.add( refId ); - if ( "from".equals( role ) ) fromWid = addLong( fromWid, refId ); - if ( "to".equals( role ) ) toWid = addLong( toWid, refId ); - } - if ( memberType == Osmformat.Relation.MemberType.NODE ) // currently just waymembers - { - if ( "via".equals( role ) ) viaNid = addLong( viaNid, refId ); - } + private Map buildTags(List keys, List values, BPbfFieldDecoder fieldDecoder) { + + Iterator keyIterator = keys.iterator(); + Iterator valueIterator = values.iterator(); + if (keyIterator.hasNext()) { + Map tags = new HashMap(); + while (keyIterator.hasNext()) { + String key = fieldDecoder.decodeString(keyIterator.next()); + String value = fieldDecoder.decodeString(valueIterator.next()); + tags.put(key, value); + } + return tags; + } + return null; + } + + private void processNodes(List nodes, BPbfFieldDecoder fieldDecoder) { + for (Osmformat.Node node : nodes) { + Map tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder); + + parser.addNode(node.getId(), tags, fieldDecoder.decodeLatitude(node + .getLat()), fieldDecoder.decodeLatitude(node.getLon())); + } + } + + private void processNodes(Osmformat.DenseNodes nodes, BPbfFieldDecoder fieldDecoder) { + List idList = nodes.getIdList(); + List latList = nodes.getLatList(); + List lonList = nodes.getLonList(); + + Iterator keysValuesIterator = nodes.getKeysValsList().iterator(); + + long nodeId = 0; + long latitude = 0; + long longitude = 0; + + for (int i = 0; i < idList.size(); i++) { + // Delta decode node fields. + nodeId += idList.get(i); + latitude += latList.get(i); + longitude += lonList.get(i); + + // Build the tags. The key and value string indexes are sequential + // in the same PBF array. Each set of tags is delimited by an index + // with a value of 0. + Map tags = null; + while (keysValuesIterator.hasNext()) { + int keyIndex = keysValuesIterator.next(); + if (keyIndex == 0) { + break; } - return wayIds; - } + int valueIndex = keysValuesIterator.next(); - private void processRelations( List relations, BPbfFieldDecoder fieldDecoder ) - { - for (Osmformat.Relation relation : relations) - { - Map tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder); - - LongList wayIds = buildRelationMembers( relation.getMemidsList(), relation.getRolesSidList(), - relation.getTypesList(), fieldDecoder); - - parser.addRelation( relation.getId(), tags, wayIds, fromWid, toWid, viaNid ); + if (tags == null) { + tags = new HashMap(); } - } - private void processOsmPrimitives( byte[] data ) throws InvalidProtocolBufferException - { - Osmformat.PrimitiveBlock block = Osmformat.PrimitiveBlock.parseFrom(data); - BPbfFieldDecoder fieldDecoder = new BPbfFieldDecoder(block); + tags.put(fieldDecoder.decodeString(keyIndex), fieldDecoder.decodeString(valueIndex)); + } - for (Osmformat.PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) - { - processNodes(primitiveGroup.getDense(), fieldDecoder); - processNodes(primitiveGroup.getNodesList(), fieldDecoder); - processWays(primitiveGroup.getWaysList(), fieldDecoder); - processRelations(primitiveGroup.getRelationsList(), fieldDecoder); - } + parser.addNode(nodeId, tags, ((double) latitude) / 10000000, ((double) longitude) / 10000000); } + } + + private void processWays(List ways, BPbfFieldDecoder fieldDecoder) { + for (Osmformat.Way way : ways) { + Map tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder); + + // Build up the list of way nodes for the way. The node ids are + // delta encoded meaning that each id is stored as a delta against + // the previous one. + long nodeId = 0; + LongList wayNodes = new LongList(16); + for (long nodeIdOffset : way.getRefsList()) { + nodeId += nodeIdOffset; + wayNodes.add(nodeId); + } + + parser.addWay(way.getId(), tags, wayNodes); + } + } + + private LongList fromWid; + private LongList toWid; + private LongList viaNid; + + private LongList addLong(LongList ll, long l) { + if (ll == null) { + ll = new LongList(1); + } + ll.add(l); + return ll; + } + + private LongList buildRelationMembers( + List memberIds, List memberRoles, List memberTypes, + BPbfFieldDecoder fieldDecoder) { + LongList wayIds = new LongList(16); + + fromWid = toWid = viaNid = null; + + Iterator memberIdIterator = memberIds.iterator(); + Iterator memberRoleIterator = memberRoles.iterator(); + Iterator memberTypeIterator = memberTypes.iterator(); + + // Build up the list of relation members for the way. The member ids are + // delta encoded meaning that each id is stored as a delta against + // the previous one. + long refId = 0; + while (memberIdIterator.hasNext()) { + Osmformat.Relation.MemberType memberType = memberTypeIterator.next(); + refId += memberIdIterator.next(); + + String role = fieldDecoder.decodeString(memberRoleIterator.next()); + + if (memberType == Osmformat.Relation.MemberType.WAY) // currently just waymembers + { + wayIds.add(refId); + if ("from".equals(role)) fromWid = addLong(fromWid, refId); + if ("to".equals(role)) toWid = addLong(toWid, refId); + } + if (memberType == Osmformat.Relation.MemberType.NODE) // currently just waymembers + { + if ("via".equals(role)) viaNid = addLong(viaNid, refId); + } + } + return wayIds; + } + + private void processRelations(List relations, BPbfFieldDecoder fieldDecoder) { + for (Osmformat.Relation relation : relations) { + Map tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder); + + LongList wayIds = buildRelationMembers(relation.getMemidsList(), relation.getRolesSidList(), + relation.getTypesList(), fieldDecoder); + + parser.addRelation(relation.getId(), tags, wayIds, fromWid, toWid, viaNid); + } + } + + private void processOsmPrimitives(byte[] data) throws InvalidProtocolBufferException { + Osmformat.PrimitiveBlock block = Osmformat.PrimitiveBlock.parseFrom(data); + BPbfFieldDecoder fieldDecoder = new BPbfFieldDecoder(block); + + for (Osmformat.PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) { + processNodes(primitiveGroup.getDense(), fieldDecoder); + processNodes(primitiveGroup.getNodesList(), fieldDecoder); + processWays(primitiveGroup.getWaysList(), fieldDecoder); + processRelations(primitiveGroup.getRelationsList(), fieldDecoder); + } + } } diff --git a/misc/pbfparser/BPbfFieldDecoder.java b/misc/pbfparser/BPbfFieldDecoder.java index be27ea1..e1c5a8b 100644 --- a/misc/pbfparser/BPbfFieldDecoder.java +++ b/misc/pbfparser/BPbfFieldDecoder.java @@ -7,79 +7,78 @@ import org.openstreetmap.osmosis.osmbinary.Osmformat; /** * Manages decoding of the lower level PBF data structures. *

+ * * @author Brett Henderson *

*/ -public class BPbfFieldDecoder -{ - private static final double COORDINATE_SCALING_FACTOR = 0.000000001; - private String[] strings; - private int coordGranularity; - private long coordLatitudeOffset; - private long coordLongitudeOffset; - private int dateGranularity; +public class BPbfFieldDecoder { + private static final double COORDINATE_SCALING_FACTOR = 0.000000001; + private String[] strings; + private int coordGranularity; + private long coordLatitudeOffset; + private long coordLongitudeOffset; + private int dateGranularity; - /** - * Creates a new instance. - *

- * @param primitiveBlock The primitive block containing the fields to be decoded. - */ - public BPbfFieldDecoder( Osmformat.PrimitiveBlock primitiveBlock ) - { - this.coordGranularity = primitiveBlock.getGranularity(); - this.coordLatitudeOffset = primitiveBlock.getLatOffset(); - this.coordLongitudeOffset = primitiveBlock.getLonOffset(); - this.dateGranularity = primitiveBlock.getDateGranularity(); + /** + * Creates a new instance. + *

+ * + * @param primitiveBlock The primitive block containing the fields to be decoded. + */ + public BPbfFieldDecoder(Osmformat.PrimitiveBlock primitiveBlock) { + this.coordGranularity = primitiveBlock.getGranularity(); + this.coordLatitudeOffset = primitiveBlock.getLatOffset(); + this.coordLongitudeOffset = primitiveBlock.getLonOffset(); + this.dateGranularity = primitiveBlock.getDateGranularity(); - Osmformat.StringTable stringTable = primitiveBlock.getStringtable(); - strings = new String[stringTable.getSCount()]; - for (int i = 0; i < strings.length; i++) - { - strings[i] = stringTable.getS(i).toStringUtf8(); - } + Osmformat.StringTable stringTable = primitiveBlock.getStringtable(); + strings = new String[stringTable.getSCount()]; + for (int i = 0; i < strings.length; i++) { + strings[i] = stringTable.getS(i).toStringUtf8(); } + } - /** - * Decodes a raw latitude value into degrees. - *

- * @param rawLatitude The PBF encoded value. - * @return The latitude in degrees. - */ - public double decodeLatitude( long rawLatitude ) - { - return COORDINATE_SCALING_FACTOR * (coordLatitudeOffset + (coordGranularity * rawLatitude)); - } + /** + * Decodes a raw latitude value into degrees. + *

+ * + * @param rawLatitude The PBF encoded value. + * @return The latitude in degrees. + */ + public double decodeLatitude(long rawLatitude) { + return COORDINATE_SCALING_FACTOR * (coordLatitudeOffset + (coordGranularity * rawLatitude)); + } - /** - * Decodes a raw longitude value into degrees. - *

- * @param rawLongitude The PBF encoded value. - * @return The longitude in degrees. - */ - public double decodeLongitude( long rawLongitude ) - { - return COORDINATE_SCALING_FACTOR * (coordLongitudeOffset + (coordGranularity * rawLongitude)); - } + /** + * Decodes a raw longitude value into degrees. + *

+ * + * @param rawLongitude The PBF encoded value. + * @return The longitude in degrees. + */ + public double decodeLongitude(long rawLongitude) { + return COORDINATE_SCALING_FACTOR * (coordLongitudeOffset + (coordGranularity * rawLongitude)); + } - /** - * Decodes a raw timestamp value into a Date. - *

- * @param rawTimestamp The PBF encoded timestamp. - * @return The timestamp as a Date. - */ - public Date decodeTimestamp( long rawTimestamp ) - { - return new Date(dateGranularity * rawTimestamp); - } + /** + * Decodes a raw timestamp value into a Date. + *

+ * + * @param rawTimestamp The PBF encoded timestamp. + * @return The timestamp as a Date. + */ + public Date decodeTimestamp(long rawTimestamp) { + return new Date(dateGranularity * rawTimestamp); + } - /** - * Decodes a raw string into a String. - *

- * @param rawString The PBF encoding string. - * @return The string as a String. - */ - public String decodeString( int rawString ) - { - return strings[rawString]; - } + /** + * Decodes a raw string into a String. + *

+ * + * @param rawString The PBF encoding string. + * @return The string as a String. + */ + public String decodeString(int rawString) { + return strings[rawString]; + } } diff --git a/misc/pbfparser/OsmParser.java b/misc/pbfparser/OsmParser.java index 9c5ee42..a1850e7 100644 --- a/misc/pbfparser/OsmParser.java +++ b/misc/pbfparser/OsmParser.java @@ -3,6 +3,7 @@ package btools.mapcreator; import java.io.*; import java.util.*; import java.util.zip.*; + import btools.util.*; import org.openstreetmap.osmosis.osmbinary.Fileformat; @@ -12,24 +13,22 @@ import org.openstreetmap.osmosis.osmbinary.Fileformat; * * @author ab */ -public class OsmParser extends MapCreatorBase -{ +public class OsmParser extends MapCreatorBase { private BufferedReader _br; private NodeListener nListener; private WayListener wListener; private RelationListener rListener; - public void readMap( File mapFile, - NodeListener nListener, - WayListener wListener, - RelationListener rListener ) throws Exception - { + public void readMap(File mapFile, + NodeListener nListener, + WayListener wListener, + RelationListener rListener) throws Exception { this.nListener = nListener; this.wListener = wListener; this.rListener = rListener; - System.out.println( "*** PBF Parsing: " + mapFile ); + System.out.println("*** PBF Parsing: " + mapFile); // once more for testing int rawBlobCount = 0; @@ -37,48 +36,38 @@ public class OsmParser extends MapCreatorBase long bytesRead = 0L; // wait for file to become available - while( !mapFile.exists() ) - { - System.out.println( "--- waiting for " + mapFile + " to become available" ); - Thread.sleep( 10000 ); + while (!mapFile.exists()) { + System.out.println("--- waiting for " + mapFile + " to become available"); + Thread.sleep(10000); } long currentSize = mapFile.length(); long currentSizeTime = System.currentTimeMillis(); - DataInputStream dis = new DataInputStream( new BufferedInputStream ( new FileInputStream( mapFile ) ) ); + DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(mapFile))); - for(;;) - { + for (; ; ) { // continue reading if either more then a 100 MB unread, or the current-size is known for more then 2 Minutes - while ( currentSize-bytesRead < 100000000L ) - { + while (currentSize - bytesRead < 100000000L) { long newSize = mapFile.length(); - if ( newSize != currentSize ) - { + if (newSize != currentSize) { currentSize = newSize; currentSizeTime = System.currentTimeMillis(); - } - else if ( System.currentTimeMillis() - currentSizeTime > 120000 ) - { + } else if (System.currentTimeMillis() - currentSizeTime > 120000) { break; } - if ( currentSize-bytesRead < 100000000L ) - { - System.out.println( "--- waiting for more data, currentSize=" + currentSize + " bytesRead=" + bytesRead ); - Thread.sleep( 10000 ); + if (currentSize - bytesRead < 100000000L) { + System.out.println("--- waiting for more data, currentSize=" + currentSize + " bytesRead=" + bytesRead); + Thread.sleep(10000); } } int headerLength; - try - { + try { headerLength = dis.readInt(); bytesRead += 4; - } - catch (EOFException e) - { + } catch (EOFException e) { break; } @@ -91,74 +80,57 @@ public class OsmParser extends MapCreatorBase dis.readFully(blobData); bytesRead += blobData.length; - new BPbfBlobDecoder( blobHeader.getType(), blobData, this ).process(); + new BPbfBlobDecoder(blobHeader.getType(), blobData, this).process(); rawBlobCount++; } dis.close(); - System.out.println( "read raw blobs: " + rawBlobCount ); + System.out.println("read raw blobs: " + rawBlobCount); } - - public void addNode( long nid, Map tags, double lat, double lon ) - { - NodeData n = new NodeData( nid, lon, lat ); - n.setTags( (HashMap)tags ); - try - { - nListener.nextNode( n ); + public void addNode(long nid, Map tags, double lat, double lon) { + NodeData n = new NodeData(nid, lon, lat); + n.setTags((HashMap) tags); + try { + nListener.nextNode(n); + } catch (Exception e) { + throw new RuntimeException("error writing node: " + e); } - catch( Exception e ) - { - throw new RuntimeException( "error writing node: " + e ); - } } - public void addWay( long wid, Map tags, LongList nodes ) - { - WayData w = new WayData( wid, nodes ); - w.setTags( (HashMap)tags ); + public void addWay(long wid, Map tags, LongList nodes) { + WayData w = new WayData(wid, nodes); + w.setTags((HashMap) tags); - try - { - wListener.nextWay( w ); + try { + wListener.nextWay(w); + } catch (Exception e) { + throw new RuntimeException("error writing way: " + e); } - catch( Exception e ) - { - throw new RuntimeException( "error writing way: " + e ); - } } - public void addRelation( long rid, Map tags, LongList wayIds, LongList fromWid, LongList toWid, LongList viaNid ) - { - RelationData r = new RelationData( rid, wayIds ); - r.setTags( (HashMap)tags ); + public void addRelation(long rid, Map tags, LongList wayIds, LongList fromWid, LongList toWid, LongList viaNid) { + RelationData r = new RelationData(rid, wayIds); + r.setTags((HashMap) tags); - try - { - rListener.nextRelation( r ); - if ( fromWid == null || toWid == null || viaNid == null || viaNid.size() != 1 ) - { + try { + rListener.nextRelation(r); + if (fromWid == null || toWid == null || viaNid == null || viaNid.size() != 1) { // dummy-TR for each viaNid - for( int vi = 0; vi < ( viaNid == null ? 0 : viaNid.size() ); vi++ ) - { - rListener.nextRestriction( r, 0L, 0L, viaNid.get( vi ) ); + for (int vi = 0; vi < (viaNid == null ? 0 : viaNid.size()); vi++) { + rListener.nextRestriction(r, 0L, 0L, viaNid.get(vi)); } return; } - for( int fi = 0; fi < fromWid.size(); fi++ ) - { - for( int ti = 0; ti < toWid.size(); ti++ ) - { - rListener.nextRestriction( r, fromWid.get( fi ), toWid.get( ti ), viaNid.get( 0 ) ); + for (int fi = 0; fi < fromWid.size(); fi++) { + for (int ti = 0; ti < toWid.size(); ti++) { + rListener.nextRestriction(r, fromWid.get(fi), toWid.get(ti), viaNid.get(0)); } } + } catch (Exception e) { + throw new RuntimeException("error writing relation", e); } - catch( Exception e ) - { - throw new RuntimeException( "error writing relation", e ); - } } } diff --git a/misc/scripts/mapcreation/BRouterTests.java b/misc/scripts/mapcreation/BRouterTests.java index 3148929..72eff46 100644 --- a/misc/scripts/mapcreation/BRouterTests.java +++ b/misc/scripts/mapcreation/BRouterTests.java @@ -1,28 +1,24 @@ import java.io.*; -public class BRouterTests -{ - public static void main( String[] args ) throws Exception - { - BufferedReader br = new BufferedReader( new FileReader( args[0] ) ); - +public class BRouterTests { + public static void main(String[] args) throws Exception { + BufferedReader br = new BufferedReader(new FileReader(args[0])); + String lastname = "ups"; - for(;;) - { - + for (; ; ) { + String line = br.readLine(); - if ( line == null ) break; + if (line == null) break; line = line.trim(); - if ( line.length() == 0 ) continue; - if ( !Character.isDigit( line.charAt( 0 ) ) ) - { + if (line.length() == 0) continue; + if (!Character.isDigit(line.charAt(0))) { lastname = line; continue; } - System.out.println( "/java/bin/java -Xmx32m -jar brouter.jar segments " + line + " /var/www/brouter/profiles2/trekking.brf" ); - System.out.println( "mv mytrack0.gpx gpx/" + lastname + ".gpx" ); - System.out.println( "mv mylog0.csv csv/" + lastname + ".csv" ); + System.out.println("/java/bin/java -Xmx32m -jar brouter.jar segments " + line + " /var/www/brouter/profiles2/trekking.brf"); + System.out.println("mv mytrack0.gpx gpx/" + lastname + ".gpx"); + System.out.println("mv mylog0.csv csv/" + lastname + ".csv"); } } }