removed memory-route + experimental codec (see experimental branch)
This commit is contained in:
parent
664925133a
commit
dfdfaedb5e
58 changed files with 0 additions and 7303 deletions
|
@ -1,154 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
|
|
||||||
public final class BitReadBuffer
|
|
||||||
{
|
|
||||||
private byte[] ab;
|
|
||||||
private int idxMax;
|
|
||||||
private int idx = -1;
|
|
||||||
private int bits; // bits left in buffer
|
|
||||||
private long b;
|
|
||||||
|
|
||||||
public BitReadBuffer( byte[] ab )
|
|
||||||
{
|
|
||||||
this.ab = ab;
|
|
||||||
idxMax = ab.length-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean decodeBit()
|
|
||||||
{
|
|
||||||
fillBuffer();
|
|
||||||
boolean value = ( ( b & 1L ) != 0 );
|
|
||||||
b >>>= 1;
|
|
||||||
bits--;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long decodeBits( int count )
|
|
||||||
{
|
|
||||||
if ( count == 0 )
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fillBuffer();
|
|
||||||
long mask = -1L >>> ( 64 - count );
|
|
||||||
long value = b & mask;
|
|
||||||
b >>>= count;
|
|
||||||
bits -= count;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* decode an integer in the range 0..max (inclusive).
|
|
||||||
*/
|
|
||||||
public long decodeBounded( long max )
|
|
||||||
{
|
|
||||||
long value = 0;
|
|
||||||
long im = 1; // integer mask
|
|
||||||
fillBuffer();
|
|
||||||
while (( value | im ) <= max)
|
|
||||||
{
|
|
||||||
if ( ( b & 1 ) != 0 )
|
|
||||||
value |= im;
|
|
||||||
b >>>= 1;
|
|
||||||
bits--;
|
|
||||||
im <<= 1;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* decode a small number with a variable bit length
|
|
||||||
* (poor mans huffman tree)
|
|
||||||
* {@code 1 -> 0}
|
|
||||||
* {@code 01 -> 1} + following 1-bit word ( 1..2 )
|
|
||||||
* {@code 001 -> 3} + following 2-bit word ( 3..6 )
|
|
||||||
* {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc.
|
|
||||||
*/
|
|
||||||
public int decodeInt()
|
|
||||||
{
|
|
||||||
long range = 1;
|
|
||||||
int cnt = 1;
|
|
||||||
fillBuffer();
|
|
||||||
while ((b & range) == 0)
|
|
||||||
{
|
|
||||||
range = (range << 1) | 1;
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
b >>>= cnt;
|
|
||||||
bits -= cnt;
|
|
||||||
return (int)((range >>> 1) + ( cnt > 1 ? decodeBits( cnt-1 ) : 0 ));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* double-log variant of decodeVarBits better suited for
|
|
||||||
* distributions with a big-number tail
|
|
||||||
*/
|
|
||||||
public long decodeLong()
|
|
||||||
{
|
|
||||||
int n = decodeInt();
|
|
||||||
return (1L << n) + decodeBits( n ) - 1L;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] decodeSortedArray()
|
|
||||||
{
|
|
||||||
int size = decodeInt();
|
|
||||||
long[] values = new long[size];
|
|
||||||
if ( size == 0 )
|
|
||||||
{
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
int offset = 0;
|
|
||||||
long value = 0;
|
|
||||||
int bits = decodeInt();
|
|
||||||
int[] sizestack = new int[bits];
|
|
||||||
int stackpointer = 0;
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
while( size > 1 && bits > 0 )
|
|
||||||
{
|
|
||||||
int size2 = (int)decodeBounded( size );
|
|
||||||
sizestack[stackpointer++] = size2;
|
|
||||||
size -= size2;
|
|
||||||
value <<= 1;
|
|
||||||
bits--;
|
|
||||||
}
|
|
||||||
if ( size == 1 )
|
|
||||||
{
|
|
||||||
values[offset++] = (value << bits) | decodeBits( bits );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (size-- > 0)
|
|
||||||
{
|
|
||||||
values[offset++] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( stackpointer == 0 )
|
|
||||||
{
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
while ( ( value & 1L ) == 1L )
|
|
||||||
{
|
|
||||||
value >>= 1;
|
|
||||||
bits++;
|
|
||||||
}
|
|
||||||
value |= 1L;
|
|
||||||
size = sizestack[--stackpointer];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void fillBuffer()
|
|
||||||
{
|
|
||||||
while (bits <= 56)
|
|
||||||
{
|
|
||||||
if ( idx < idxMax )
|
|
||||||
{
|
|
||||||
b |= (ab[++idx] & 0xffL) << bits;
|
|
||||||
}
|
|
||||||
bits += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoder for unicode characters, using simple (1st order) huffmann
|
|
||||||
*/
|
|
||||||
public final class CharDecoder extends HuffmannTreeDecoder<Character>
|
|
||||||
{
|
|
||||||
private long[] alphabet;
|
|
||||||
private int range;
|
|
||||||
private char[] buffer = new char[64];
|
|
||||||
|
|
||||||
public CharDecoder( BitReadBuffer brb )
|
|
||||||
{
|
|
||||||
super( brb );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object decodeTree()
|
|
||||||
{
|
|
||||||
alphabet = brb.decodeSortedArray();
|
|
||||||
range = alphabet.length - 1;
|
|
||||||
System.out.println( "decoded alphabet of length " + alphabet.length + " idx3 = " + alphabet[3] );
|
|
||||||
return super.decodeTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Character decodeItem()
|
|
||||||
{
|
|
||||||
int idx = (int)brb.decodeBounded( range );
|
|
||||||
long lc = alphabet[idx];
|
|
||||||
System.out.println( "decoded item: c=" + ((char)lc) + " idx=" + idx );
|
|
||||||
return Character.valueOf( (char)lc );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String decodeString()
|
|
||||||
{
|
|
||||||
int n = brb.decodeInt();
|
|
||||||
char[] b = n <= buffer.length ? buffer : new char[n];
|
|
||||||
for( int i=0; i<n; i++ )
|
|
||||||
{
|
|
||||||
b[i] = decode().charValue();
|
|
||||||
}
|
|
||||||
return new String( b, 0, n );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoder for static huffmann coded data
|
|
||||||
*/
|
|
||||||
public abstract class HuffmannTreeDecoder<V>
|
|
||||||
{
|
|
||||||
private Object tree;
|
|
||||||
protected BitReadBuffer brb;
|
|
||||||
|
|
||||||
protected HuffmannTreeDecoder( BitReadBuffer brb )
|
|
||||||
{
|
|
||||||
this.brb = brb;
|
|
||||||
tree = decodeTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
public V decode()
|
|
||||||
{
|
|
||||||
Object node = tree;
|
|
||||||
while (node instanceof TreeNode)
|
|
||||||
{
|
|
||||||
TreeNode tn = (TreeNode) node;
|
|
||||||
node = brb.decodeBit() ? tn.child2 : tn.child1;
|
|
||||||
}
|
|
||||||
if ( node == null )
|
|
||||||
{
|
|
||||||
return decodeItem(); // inline item
|
|
||||||
}
|
|
||||||
return (V) node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected Object decodeTree()
|
|
||||||
{
|
|
||||||
boolean isNode = brb.decodeBit();
|
|
||||||
if ( isNode )
|
|
||||||
{
|
|
||||||
TreeNode node = new TreeNode();
|
|
||||||
node.child1 = decodeTree();
|
|
||||||
node.child2 = decodeTree();
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
boolean isInlinePrefix = brb.decodeBit();
|
|
||||||
if ( isInlinePrefix )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return decodeItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static final class TreeNode
|
|
||||||
{
|
|
||||||
public Object child1;
|
|
||||||
public Object child2;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract V decodeItem();
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.zip.Inflater;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manage the mapping between locale and native node indexes
|
|
||||||
*/
|
|
||||||
public class LocaleIndexMapping
|
|
||||||
{
|
|
||||||
private int[] refZoomDelta;
|
|
||||||
private int[] refNativeIndex;
|
|
||||||
private OsmTile[] tileForZoomDelta;
|
|
||||||
|
|
||||||
public LocaleIndexMapping( OsmTile tile, BitReadBuffer brb ) throws Exception
|
|
||||||
{
|
|
||||||
// prepare the locale index array
|
|
||||||
int localeNodeCount = brb.decodeInt();
|
|
||||||
refZoomDelta = new int[localeNodeCount];
|
|
||||||
refNativeIndex = new int[localeNodeCount];
|
|
||||||
|
|
||||||
tileForZoomDelta = new OsmTile[tile.zoom + 1];
|
|
||||||
for( OsmTile t = tile; t != null; t = t.parent )
|
|
||||||
{
|
|
||||||
tileForZoomDelta[tile.zoom-t.zoom] = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode the down-zoom refs
|
|
||||||
for( int zoomDelta=tile.zoom; zoomDelta > 0; zoomDelta-- )
|
|
||||||
{
|
|
||||||
long[] localeIndexes = brb.decodeSortedArray();
|
|
||||||
long[] nativeIndexes = brb.decodeSortedArray();
|
|
||||||
|
|
||||||
for( int i=0; i<localeIndexes.length; i++ )
|
|
||||||
{
|
|
||||||
int idx = (int)localeIndexes[i];
|
|
||||||
refZoomDelta[idx] = zoomDelta;
|
|
||||||
refNativeIndex[idx] = (int)nativeIndexes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare locale->native mapping for zoomDelta=0
|
|
||||||
int localeIdx = 0;
|
|
||||||
int nodecount = tile.nodePositions.length;
|
|
||||||
for( int i=0; i<nodecount; i++)
|
|
||||||
{
|
|
||||||
while( refZoomDelta[localeIdx] != 0 )
|
|
||||||
{
|
|
||||||
localeIdx++;
|
|
||||||
}
|
|
||||||
refNativeIndex[localeIdx++] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmNode nodeForLocaleIndex( int localeIndex )
|
|
||||||
{
|
|
||||||
int zoomDelta = refZoomDelta[localeIndex];
|
|
||||||
int nativeIndex = refNativeIndex[localeIndex];
|
|
||||||
return tileForZoomDelta[zoomDelta].nodes.get( nativeIndex );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmWay getWay( int zoomDelta, int nativeIndex )
|
|
||||||
{
|
|
||||||
return tileForZoomDelta[zoomDelta].ways.get( nativeIndex );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
public class NodeTreeElement
|
|
||||||
{
|
|
||||||
public int offset;
|
|
||||||
|
|
||||||
public int nnodes;
|
|
||||||
|
|
||||||
public NodeTreeElement child0;
|
|
||||||
|
|
||||||
public NodeTreeElement child1;
|
|
||||||
|
|
||||||
public List<OsmNode> nodes;
|
|
||||||
|
|
||||||
public static NodeTreeElement createNodeTree( long[] values, int offset, int subsize, long nextbit, long mask )
|
|
||||||
{
|
|
||||||
if ( nextbit == 0 )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( subsize < 1 )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
long data = mask & values[offset];
|
|
||||||
mask |= nextbit;
|
|
||||||
|
|
||||||
// count 0-bit-fraction
|
|
||||||
int i = offset;
|
|
||||||
int end = subsize + offset;
|
|
||||||
for ( ; i < end; i++ )
|
|
||||||
{
|
|
||||||
if ( ( values[i] & mask ) != data )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int size1 = i - offset;
|
|
||||||
int size2 = subsize - size1;
|
|
||||||
|
|
||||||
System.out.println( "createNodeTree: offset=" + offset + " subsize=" + subsize + " size1=" + size1 + " size2=" + size2 );
|
|
||||||
|
|
||||||
NodeTreeElement nte = new NodeTreeElement();
|
|
||||||
nte.offset = offset;
|
|
||||||
nte.nnodes = subsize;
|
|
||||||
|
|
||||||
nte.child0 = createNodeTree( values, offset, size1, nextbit >> 1, mask );
|
|
||||||
nte.child1 = createNodeTree( values, i, size2, nextbit >> 1, mask );
|
|
||||||
|
|
||||||
return nte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return " child0=" + (child0 != null ) + " child1=" + (child1 != null );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
public class OsmNode extends OsmObject
|
|
||||||
{
|
|
||||||
public int ilon;
|
|
||||||
public int ilat;
|
|
||||||
|
|
||||||
public boolean inBBox( int z, int x, int y )
|
|
||||||
{
|
|
||||||
int shift = 28-z;
|
|
||||||
int x0 = x << shift;
|
|
||||||
int x1 = (x+1) << shift;
|
|
||||||
int y0 = y << shift;
|
|
||||||
int y1 = (y+1) << shift;
|
|
||||||
boolean outofbox = x1 < ilon || x0 >= ilon || y1 < ilat || y0 >= ilat;
|
|
||||||
return !outofbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double gudermannian(double y)
|
|
||||||
{
|
|
||||||
return Math.atan(Math.sinh(y)) * (180. / Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLon()
|
|
||||||
{
|
|
||||||
return (((double)ilon)/( 1L << 27 ) - 1.)*180.;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLat()
|
|
||||||
{
|
|
||||||
double y = (1. - ((double)ilat)/( 1L << 27 ))*Math.PI;
|
|
||||||
return gudermannian(y);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class of Nodes, Ways and Relations
|
|
||||||
*/
|
|
||||||
public class OsmObject
|
|
||||||
{
|
|
||||||
public int id;
|
|
||||||
public Map<String,String> tags;
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class OsmRelation extends OsmObject
|
|
||||||
{
|
|
||||||
public List<OsmRelationMember> members;
|
|
||||||
|
|
||||||
// bounding box
|
|
||||||
int minx;
|
|
||||||
int miny;
|
|
||||||
int maxx;
|
|
||||||
int maxy;
|
|
||||||
|
|
||||||
public void calcBBox()
|
|
||||||
{
|
|
||||||
for( int i=0; i<members.size(); i++ )
|
|
||||||
{
|
|
||||||
OsmWay w = members.get(i).way;
|
|
||||||
if ( i == 0 )
|
|
||||||
{
|
|
||||||
minx = w.minx;
|
|
||||||
maxx = w.maxx;
|
|
||||||
miny = w.miny;
|
|
||||||
maxy = w.maxy;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( w.minx < minx ) minx = w.minx;
|
|
||||||
if ( w.maxx > maxx ) maxx = w.maxx;
|
|
||||||
if ( w.miny < miny ) miny = w.miny;
|
|
||||||
if ( w.maxy > maxy ) maxy = w.maxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inBBox( int z, int x, int y )
|
|
||||||
{
|
|
||||||
int shift = 28-z;
|
|
||||||
int x0 = x << shift;
|
|
||||||
int x1 = (x+1) << shift;
|
|
||||||
int y0 = y << shift;
|
|
||||||
int y1 = (y+1) << shift;
|
|
||||||
boolean outofbox = x1 < minx || x0 >= maxx || y1 < miny || y0 >= maxy;
|
|
||||||
return !outofbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class OsmRelationMember extends OsmObject
|
|
||||||
{
|
|
||||||
public OsmWay way;
|
|
||||||
public String role;
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for waydata on the preprocessor level
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class OsmTile
|
|
||||||
{
|
|
||||||
public OsmTile parent;
|
|
||||||
public long sourceId;
|
|
||||||
|
|
||||||
public int zoom;
|
|
||||||
public int x;
|
|
||||||
public int y;
|
|
||||||
|
|
||||||
private static List<OsmNode> emptyNodes = Collections.EMPTY_LIST;
|
|
||||||
private static List<OsmWay> emptyWays = Collections.EMPTY_LIST;
|
|
||||||
private static List<OsmRelation> emptyRelations = Collections.EMPTY_LIST;
|
|
||||||
|
|
||||||
public List<OsmNode> nodes = emptyNodes;
|
|
||||||
public List<OsmWay> ways = emptyWays;
|
|
||||||
public List<OsmRelation> relations = emptyRelations;
|
|
||||||
|
|
||||||
public long[] nodePositions;
|
|
||||||
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "z=" + zoom+ " x=" + x + " y=" + y + " nodes=" + nodes.size() + " ways=" + ways.size() + " rels=" + relations.size();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class OsmWay extends OsmObject
|
|
||||||
{
|
|
||||||
public List<OsmNode> nodes;
|
|
||||||
|
|
||||||
// bounding box
|
|
||||||
int minx;
|
|
||||||
int miny;
|
|
||||||
int maxx;
|
|
||||||
int maxy;
|
|
||||||
|
|
||||||
public void calcBBox()
|
|
||||||
{
|
|
||||||
for( int i=0; i<nodes.size(); i++ )
|
|
||||||
{
|
|
||||||
OsmNode n = nodes.get(i);
|
|
||||||
if ( i == 0 )
|
|
||||||
{
|
|
||||||
minx = maxx = n.ilon;
|
|
||||||
miny = maxy = n.ilat;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( n.ilon < minx ) minx = n.ilon;
|
|
||||||
if ( n.ilon > maxx ) maxx = n.ilon;
|
|
||||||
if ( n.ilat < miny ) miny = n.ilat;
|
|
||||||
if ( n.ilat > maxy ) maxy = n.ilat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inBBox( int z, int x, int y )
|
|
||||||
{
|
|
||||||
int shift = 28-z;
|
|
||||||
int x0 = x << shift;
|
|
||||||
int x1 = (x+1) << shift;
|
|
||||||
int y0 = y << shift;
|
|
||||||
int y1 = (y+1) << shift;
|
|
||||||
boolean outofbox = x1 < minx || x0 >= maxx || y1 < miny || y0 >= maxy;
|
|
||||||
return !outofbox;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoder for a set of tags
|
|
||||||
*
|
|
||||||
* Only tagsets detected at least twice
|
|
||||||
* have their own huffmann-codes, those
|
|
||||||
* detected only once are coded inline
|
|
||||||
*/
|
|
||||||
public final class TagSetDecoder extends HuffmannTreeDecoder<int[]>
|
|
||||||
{
|
|
||||||
public TagSetDecoder( BitReadBuffer brb )
|
|
||||||
{
|
|
||||||
super( brb );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int[] decodeItem()
|
|
||||||
{
|
|
||||||
int tagcount = brb.decodeInt();
|
|
||||||
int[] data = new int[tagcount];
|
|
||||||
int lastIdx = -1;
|
|
||||||
for( int i=0; i<tagcount; i++ )
|
|
||||||
{
|
|
||||||
int idx = lastIdx + 1 + brb.decodeInt();
|
|
||||||
data[i] = idx;
|
|
||||||
lastIdx = idx;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoder for the list of tags and their value-trees
|
|
||||||
*/
|
|
||||||
public class TagValueDecoder
|
|
||||||
{
|
|
||||||
private int nextStringStart = 0;
|
|
||||||
private byte[] textHeader;
|
|
||||||
private ArrayList<String> stringList;
|
|
||||||
private ArrayList<Tag> taglist;
|
|
||||||
private int roleIdx;
|
|
||||||
|
|
||||||
private String decodeString( BitReadBuffer brb )
|
|
||||||
{
|
|
||||||
boolean newIdx = brb.decodeBit();
|
|
||||||
if ( newIdx )
|
|
||||||
{
|
|
||||||
int slen = brb.decodeInt();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String s = new String( textHeader, nextStringStart, slen, "UTF8" );
|
|
||||||
nextStringStart += slen;
|
|
||||||
stringList.add( s );
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
catch( UnsupportedEncodingException uee )
|
|
||||||
{
|
|
||||||
throw new RuntimeException( uee );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int idx = (int)brb.decodeBounded( stringList.size()-1 );
|
|
||||||
return stringList.get( idx );
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Tag extends HuffmannTreeDecoder<String>
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
|
|
||||||
Tag( BitReadBuffer brb, String tagName )
|
|
||||||
{
|
|
||||||
super( brb );
|
|
||||||
name = tagName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String decodeItem()
|
|
||||||
{
|
|
||||||
return decodeString( brb );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagValueDecoder( BitReadBuffer brb, byte[] textHeader )
|
|
||||||
{
|
|
||||||
this.textHeader = textHeader;
|
|
||||||
stringList = new ArrayList<String>();
|
|
||||||
|
|
||||||
int ntags = brb.decodeInt();
|
|
||||||
taglist = new ArrayList<Tag>();
|
|
||||||
for( int i=0; i<ntags; i++ )
|
|
||||||
{
|
|
||||||
String tagName = decodeString( brb );
|
|
||||||
taglist.add( new Tag( brb, tagName ) );
|
|
||||||
if ( "role".equals( tagName ) )
|
|
||||||
{
|
|
||||||
roleIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTagName( int idx )
|
|
||||||
{
|
|
||||||
return taglist.get(idx).name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String decodeValue( int tagIdx )
|
|
||||||
{
|
|
||||||
return taglist.get( tagIdx ).decode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String decodeRole()
|
|
||||||
{
|
|
||||||
return taglist.get( roleIdx ).decode();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,207 +0,0 @@
|
||||||
package btools.mapdecoder;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.zip.Inflater;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TileEncoder decodes a compressed osm tile
|
|
||||||
*/
|
|
||||||
public class TileDecoder
|
|
||||||
{
|
|
||||||
private TagSetDecoder tagSetDecoder;
|
|
||||||
private TagValueDecoder tagValueDecoder;
|
|
||||||
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
OsmTile t = new TileDecoder().process( new File( args[0] ), null, Integer.parseInt( args[1] ), Integer.parseInt( args[2] ), Integer.parseInt( args[3] ) );
|
|
||||||
while( t != null )
|
|
||||||
{
|
|
||||||
System.out.println( "decoded: " + t );
|
|
||||||
t = t.parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmTile process( File tileDir, OsmTile template, int zoom, int x, int y ) throws Exception
|
|
||||||
{
|
|
||||||
long sourceId = tileDir.getAbsolutePath().hashCode();
|
|
||||||
|
|
||||||
// look for a match in the template
|
|
||||||
for( OsmTile tile = template; tile != null; tile = tile.parent )
|
|
||||||
{
|
|
||||||
if ( tile.zoom == zoom && tile.x == x && tile.y == y && tile.sourceId == sourceId )
|
|
||||||
{
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OsmTile td = new OsmTile();
|
|
||||||
td.sourceId = sourceId;
|
|
||||||
td.zoom = zoom;
|
|
||||||
td.x = x;
|
|
||||||
td.y = y;
|
|
||||||
if ( zoom > 0 )
|
|
||||||
{
|
|
||||||
td.parent = new TileDecoder().process( tileDir, template, zoom-1, x >> 1, y >> 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = new File( new File( tileDir, "" + zoom ), x + "_" + y + ".osb" );
|
|
||||||
if ( !file.exists() )
|
|
||||||
{
|
|
||||||
return td;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataInputStream dis = new DataInputStream( new FileInputStream( file ) );
|
|
||||||
int textHeaderLen = dis.readInt();
|
|
||||||
int textHeaderCompressedLen = dis.readInt();
|
|
||||||
byte[] textHeaderCompressed = new byte[textHeaderCompressedLen];
|
|
||||||
dis.readFully( textHeaderCompressed );
|
|
||||||
byte[] textHeader = new byte[textHeaderLen];
|
|
||||||
|
|
||||||
Inflater decompresser = new Inflater();
|
|
||||||
decompresser.setInput( textHeaderCompressed );
|
|
||||||
int rawlen = decompresser.inflate( textHeader );
|
|
||||||
|
|
||||||
int bufferLen = dis.readInt();
|
|
||||||
byte[] buffer = new byte[bufferLen];
|
|
||||||
dis.readFully( buffer );
|
|
||||||
BitReadBuffer brb = new BitReadBuffer( buffer );
|
|
||||||
dis.close();
|
|
||||||
|
|
||||||
tagSetDecoder = new TagSetDecoder( brb );
|
|
||||||
tagValueDecoder = new TagValueDecoder( brb, textHeader );
|
|
||||||
|
|
||||||
// decode the node positions
|
|
||||||
td.nodePositions = brb.decodeSortedArray();
|
|
||||||
int nodecount = td.nodePositions.length;
|
|
||||||
td.nodes = new ArrayList<OsmNode>(nodecount);
|
|
||||||
|
|
||||||
int shift = 56-2*zoom;
|
|
||||||
long offset = (encodeMorton( x ) << shift) + (encodeMorton( y ) << (shift+1) );
|
|
||||||
|
|
||||||
for ( int nidx = 0; nidx < nodecount; nidx++ )
|
|
||||||
{
|
|
||||||
OsmNode n = new OsmNode();
|
|
||||||
long z = offset + td.nodePositions[nidx];
|
|
||||||
n.id = nidx;
|
|
||||||
n.ilon = decodeMorton( z );
|
|
||||||
n.ilat = decodeMorton( z >> 1 );
|
|
||||||
td.nodes.add( n );
|
|
||||||
}
|
|
||||||
|
|
||||||
LocaleIndexMapping indexMapping = new LocaleIndexMapping( td, brb );
|
|
||||||
|
|
||||||
// decode tagged nodes
|
|
||||||
long[] taggedIndexes = brb.decodeSortedArray();
|
|
||||||
int ntaggedNodes = taggedIndexes.length;
|
|
||||||
for( int tnidx=0; tnidx<ntaggedNodes; tnidx++ )
|
|
||||||
{
|
|
||||||
int idx = (int)taggedIndexes[tnidx];
|
|
||||||
td.nodes.get( idx ).tags = decodeTagValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode ways
|
|
||||||
long[] startIndexes = brb.decodeSortedArray();
|
|
||||||
int nways = startIndexes.length;
|
|
||||||
|
|
||||||
td.ways = new ArrayList<OsmWay>( nways );
|
|
||||||
for( int widx=0; widx<nways; widx++ )
|
|
||||||
{
|
|
||||||
OsmWay w = new OsmWay();
|
|
||||||
w.tags = decodeTagValues();
|
|
||||||
int[] nodeIndexes = decodeWayNodes( (int)startIndexes[widx], brb );
|
|
||||||
w.nodes = new ArrayList<OsmNode>( nodeIndexes.length );
|
|
||||||
for( int i=0; i<nodeIndexes.length; i++ )
|
|
||||||
{
|
|
||||||
w.nodes.add( indexMapping.nodeForLocaleIndex( nodeIndexes[i] ) );
|
|
||||||
}
|
|
||||||
w.calcBBox();
|
|
||||||
td.ways.add( w );
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode relations
|
|
||||||
int nrels = brb.decodeInt();
|
|
||||||
td.relations = new ArrayList<OsmRelation>( nrels );
|
|
||||||
for( int ridx=0; ridx<nrels; ridx++ )
|
|
||||||
{
|
|
||||||
OsmRelation r = new OsmRelation();
|
|
||||||
r.tags = decodeTagValues();
|
|
||||||
|
|
||||||
int nmembers = brb.decodeInt();
|
|
||||||
r.members = new ArrayList<OsmRelationMember>(nmembers);
|
|
||||||
for( int midx = 0; midx<nmembers; midx++ )
|
|
||||||
{
|
|
||||||
OsmRelationMember m = new OsmRelationMember();
|
|
||||||
int zoomDelta = brb.decodeInt();
|
|
||||||
int nativeIndex = brb.decodeInt();
|
|
||||||
m.role = tagValueDecoder.decodeRole();
|
|
||||||
m.way = indexMapping.getWay( zoomDelta, nativeIndex );
|
|
||||||
r.members.add( m );
|
|
||||||
}
|
|
||||||
r.calcBBox();
|
|
||||||
td.relations.add( r );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return td;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] decodeWayNodes( int startIdx, BitReadBuffer brb )
|
|
||||||
{
|
|
||||||
boolean closedPoly = brb.decodeBit();
|
|
||||||
int nnodes = brb.decodeInt() + 2;
|
|
||||||
|
|
||||||
int[] ids = new int[ closedPoly ? nnodes+1 : nnodes ];
|
|
||||||
int lastIdx = startIdx;
|
|
||||||
ids[0] = startIdx;
|
|
||||||
for( int i=1; i<nnodes; i++ )
|
|
||||||
{
|
|
||||||
boolean negative = brb.decodeBit();
|
|
||||||
int delta = (int)brb.decodeLong() + 1;
|
|
||||||
ids[i] = lastIdx = lastIdx + (negative ? -delta : delta );
|
|
||||||
}
|
|
||||||
if ( closedPoly )
|
|
||||||
{
|
|
||||||
ids[nnodes] = startIdx;
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<String,String> decodeTagValues()
|
|
||||||
{
|
|
||||||
HashMap<String,String> map = new HashMap<String,String>();
|
|
||||||
int[] tagSet = tagSetDecoder.decode();
|
|
||||||
for( int i=0; i<tagSet.length; i++ )
|
|
||||||
{
|
|
||||||
int tagIdx = tagSet[i];
|
|
||||||
String key = tagValueDecoder.getTagName( tagIdx );
|
|
||||||
String value = tagValueDecoder.decodeValue( tagIdx );
|
|
||||||
map.put( key, value );
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static int decodeMorton( long z )
|
|
||||||
{
|
|
||||||
long x = z & 0x5555555555555555L;
|
|
||||||
x = (x | (x >> 1)) & 0x3333333333333333L;
|
|
||||||
x = (x | (x >> 2)) & 0x0F0F0F0F0F0F0F0FL;
|
|
||||||
x = (x | (x >> 4)) & 0x00FF00FF00FF00FFL;
|
|
||||||
x = (x | (x >> 8)) & 0x0000FFFF0000FFFFL;
|
|
||||||
return (int)(x | (x >> 16));
|
|
||||||
}
|
|
||||||
public static long encodeMorton( int x )
|
|
||||||
{
|
|
||||||
long z = x & 0xFFFFFFFFL;
|
|
||||||
z = (z | (z << 16)) & 0x0000FFFF0000FFFFL;
|
|
||||||
z = (z | (z << 8)) & 0x00FF00FF00FF00FFL;
|
|
||||||
z = (z | (z << 4)) & 0x0F0F0F0F0F0F0F0FL;
|
|
||||||
z = (z | (z << 2)) & 0x3333333333333333L;
|
|
||||||
return (z|(z << 1)) & 0x5555555555555555L;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
|
|
||||||
public final class BitWriteBuffer
|
|
||||||
{
|
|
||||||
private static TreeMap<String, long[]> statsPerName;
|
|
||||||
private long lastbitpos = 0;
|
|
||||||
|
|
||||||
private byte[] ab;
|
|
||||||
private int idxMax;
|
|
||||||
private int idx = -1;
|
|
||||||
private int bm = 0x100; // byte mask (write mode)
|
|
||||||
private int b;
|
|
||||||
|
|
||||||
public BitWriteBuffer( byte[] ab )
|
|
||||||
{
|
|
||||||
this.ab = ab;
|
|
||||||
idxMax = ab.length-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* encode a distance with a variable bit length
|
|
||||||
* (poor mans huffman tree)
|
|
||||||
* {@code 1 -> 0}
|
|
||||||
* {@code 01 -> 1} + following 1-bit word ( 1..2 )
|
|
||||||
* {@code 001 -> 3} + following 2-bit word ( 3..6 )
|
|
||||||
* {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc.
|
|
||||||
*
|
|
||||||
* @see btools.util.BitCoderContext#decodeVarBits
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void encodeInt( int value )
|
|
||||||
{
|
|
||||||
int range = 0;
|
|
||||||
while (value > range)
|
|
||||||
{
|
|
||||||
encodeBit( false );
|
|
||||||
value -= range + 1;
|
|
||||||
range = 2 * range + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeBit( true );
|
|
||||||
encodeBounded( range, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeLong( long n )
|
|
||||||
{
|
|
||||||
int maxbit = 0;
|
|
||||||
long nn = n + 1L;
|
|
||||||
while( nn > 1L )
|
|
||||||
{
|
|
||||||
maxbit++;
|
|
||||||
nn >>= 1;
|
|
||||||
}
|
|
||||||
encodeInt( maxbit );
|
|
||||||
long range = 1 << maxbit;
|
|
||||||
encodeBounded( range-1L, n + 1L -range );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeBit( boolean value )
|
|
||||||
{
|
|
||||||
if ( bm == 0x100 )
|
|
||||||
{
|
|
||||||
bm = 1;
|
|
||||||
ab[++idx] = 0;
|
|
||||||
}
|
|
||||||
if ( value )
|
|
||||||
ab[idx] |= bm;
|
|
||||||
bm <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* encode an integer in the range 0..max (inclusive).
|
|
||||||
* For max = 2^n-1, this just encodes n bits, but in general
|
|
||||||
* this is variable length encoding, with the shorter codes
|
|
||||||
* for the central value range
|
|
||||||
*/
|
|
||||||
public void encodeBounded( long max, long value )
|
|
||||||
{
|
|
||||||
long im = 1L; // integer mask
|
|
||||||
while (im <= max)
|
|
||||||
{
|
|
||||||
if ( bm == 0x100 )
|
|
||||||
{
|
|
||||||
bm = 1;
|
|
||||||
ab[++idx] = 0;
|
|
||||||
}
|
|
||||||
if ( ( value & im ) != 0 )
|
|
||||||
{
|
|
||||||
ab[idx] |= bm;
|
|
||||||
max -= im;
|
|
||||||
}
|
|
||||||
bm <<= 1;
|
|
||||||
im <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the encoded length in bytes
|
|
||||||
*/
|
|
||||||
public int getEncodedLength()
|
|
||||||
{
|
|
||||||
return idx + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the encoded length in bits
|
|
||||||
*/
|
|
||||||
public long getWritingBitPosition()
|
|
||||||
{
|
|
||||||
long bitpos = idx << 3;
|
|
||||||
int m = bm;
|
|
||||||
while (m > 1)
|
|
||||||
{
|
|
||||||
bitpos++;
|
|
||||||
m >>= 1;
|
|
||||||
}
|
|
||||||
return bitpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeSortedArray( long[] values )
|
|
||||||
{
|
|
||||||
int size = values.length;
|
|
||||||
encodeInt( size );
|
|
||||||
if ( size == 0 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
long maxValue = values[size-1];
|
|
||||||
int nbits = 0;
|
|
||||||
while ( maxValue > 0 )
|
|
||||||
{
|
|
||||||
nbits++;
|
|
||||||
maxValue >>= 1;
|
|
||||||
}
|
|
||||||
if ( nbits > 57 ) throw new IllegalArgumentException( "encodeSortedArray accepts 57-bit numbers at max" );
|
|
||||||
encodeInt( nbits );
|
|
||||||
encodeSortedArray( values, 0, size, ( 1L << nbits ) >> 1, 0L );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encodeSortedArray( long[] values, int offset, int subsize, long nextbit, long mask )
|
|
||||||
{
|
|
||||||
if ( subsize == 1 ) // last-choice shortcut
|
|
||||||
{
|
|
||||||
long bit = 1L;
|
|
||||||
while ( bit <= nextbit )
|
|
||||||
{
|
|
||||||
encodeBit( ( values[offset] & bit ) != 0 );
|
|
||||||
bit <<= 1;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( nextbit == 0 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long data = mask & values[offset];
|
|
||||||
mask |= nextbit;
|
|
||||||
|
|
||||||
// count 0-bit-fraction
|
|
||||||
int i = offset;
|
|
||||||
int end = subsize + offset;
|
|
||||||
for ( ; i < end; i++ )
|
|
||||||
{
|
|
||||||
if ( ( values[i] & mask ) != data )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int size1 = i - offset;
|
|
||||||
int size2 = subsize - size1;
|
|
||||||
|
|
||||||
encodeBounded( subsize, size2 );
|
|
||||||
if ( size1 > 0 )
|
|
||||||
{
|
|
||||||
encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
|
|
||||||
}
|
|
||||||
if ( size2 > 0 )
|
|
||||||
{
|
|
||||||
encodeSortedArray( values, i, size2, nextbit >> 1, mask );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 )
|
|
||||||
{
|
|
||||||
long bitpos = getWritingBitPosition();
|
|
||||||
if ( statsPerName == null )
|
|
||||||
{
|
|
||||||
statsPerName = new TreeMap<String, long[]>();
|
|
||||||
}
|
|
||||||
long[] stats = statsPerName.get( name );
|
|
||||||
if ( stats == null )
|
|
||||||
{
|
|
||||||
stats = new long[2];
|
|
||||||
statsPerName.put( name, stats );
|
|
||||||
}
|
|
||||||
stats[0] += bitpos - lastbitpos;
|
|
||||||
stats[1] += 1;
|
|
||||||
lastbitpos = bitpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a textual report on the bit-statistics
|
|
||||||
*
|
|
||||||
* @see #assignBits
|
|
||||||
*/
|
|
||||||
public static String getBitReport()
|
|
||||||
{
|
|
||||||
if ( statsPerName == null )
|
|
||||||
{
|
|
||||||
return "<empty bit report>";
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoder for characters, using simple (1st order) huffmann
|
|
||||||
*/
|
|
||||||
public final class CharEncoder extends HuffmanTreeEncoder<Character>
|
|
||||||
{
|
|
||||||
private long[] alphabet;
|
|
||||||
private int range;
|
|
||||||
|
|
||||||
private TreeMap<Character,Integer> chars = new TreeMap<Character,Integer>();
|
|
||||||
|
|
||||||
public void encode( Character c )
|
|
||||||
{
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
chars.put( c, null );
|
|
||||||
}
|
|
||||||
super.encode( c );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeDictionary( BitWriteBuffer bwb )
|
|
||||||
{
|
|
||||||
if ( pass == 1 ) // means 2...
|
|
||||||
{
|
|
||||||
int idx = 0;
|
|
||||||
alphabet = new long[chars.size()];
|
|
||||||
range = chars.size()-1;
|
|
||||||
for ( Character c : chars.keySet() )
|
|
||||||
{
|
|
||||||
System.out.println( "assigning index " + idx + " to char=" + c );
|
|
||||||
alphabet[idx] = c;
|
|
||||||
chars.put( c, Integer.valueOf( idx++ ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( alphabet != null )
|
|
||||||
{
|
|
||||||
bwb.encodeSortedArray( alphabet );
|
|
||||||
}
|
|
||||||
super.encodeDictionary( bwb );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void encodeItem( Character c )
|
|
||||||
{
|
|
||||||
int idx = chars.get( c ).intValue();
|
|
||||||
System.out.println( "encoding item: c=" + c + " idx=" + idx );
|
|
||||||
bwb.encodeBounded( range, idx );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean itemEquals( Character c1, Character c2 )
|
|
||||||
{
|
|
||||||
if ( c1 == null )
|
|
||||||
{
|
|
||||||
return c2 == null;
|
|
||||||
}
|
|
||||||
if ( c2 == null )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return c1.charValue() == c2.charValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int itemHashCode( Character c)
|
|
||||||
{
|
|
||||||
return c == 0 ? 0 : c.charValue();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.PriorityQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoder for a set of tags
|
|
||||||
*
|
|
||||||
* It detects identical sets 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 abstract class HuffmanTreeEncoder<V>
|
|
||||||
{
|
|
||||||
private HashMap<TreeNode, TreeNode> identityMap;
|
|
||||||
protected BitWriteBuffer bwb;
|
|
||||||
protected int pass;
|
|
||||||
private TreeNode freq1;
|
|
||||||
|
|
||||||
public void encode( V data )
|
|
||||||
{
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TreeNode probe = new TreeNode();
|
|
||||||
probe.data = data;
|
|
||||||
TreeNode tn = identityMap.get( probe );
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
if ( tn.frequency == 1 )
|
|
||||||
{
|
|
||||||
bwb.encodeBounded( freq1.range - 1, freq1.code );
|
|
||||||
encodeItem( data );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bwb.encodeBounded( tn.range - 1, tn.code );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
if ( tn == null )
|
|
||||||
{
|
|
||||||
tn = probe;
|
|
||||||
identityMap.put( tn, tn );
|
|
||||||
}
|
|
||||||
tn.frequency++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeDictionary( BitWriteBuffer bwb )
|
|
||||||
{
|
|
||||||
this.bwb = bwb;
|
|
||||||
if ( ++pass == 3 )
|
|
||||||
{
|
|
||||||
freq1 = new TreeNode();
|
|
||||||
PriorityQueue<TreeNode> queue = new PriorityQueue<TreeNode>(2*identityMap.size(), new Comparator<TreeNode>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public int compare(TreeNode tn1, TreeNode tn2)
|
|
||||||
{
|
|
||||||
if ( tn1.frequency < tn2.frequency )
|
|
||||||
return -1;
|
|
||||||
if ( tn1.frequency > tn2.frequency )
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
queue.add( freq1 );
|
|
||||||
while (queue.size() > 1)
|
|
||||||
{
|
|
||||||
TreeNode node = new TreeNode();
|
|
||||||
node.child1 = queue.poll();
|
|
||||||
node.child2 = queue.poll();
|
|
||||||
node.frequency = node.child1.frequency + node.child2.frequency;
|
|
||||||
queue.add( node );
|
|
||||||
}
|
|
||||||
TreeNode root = queue.poll();
|
|
||||||
root.encode( 1, 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HuffmanTreeEncoder()
|
|
||||||
{
|
|
||||||
identityMap = new HashMap<TreeNode, TreeNode>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void encodeItem( V data );
|
|
||||||
|
|
||||||
protected abstract boolean itemEquals( V i1, V i2 );
|
|
||||||
|
|
||||||
protected abstract int itemHashCode( V i);
|
|
||||||
|
|
||||||
public final class TreeNode
|
|
||||||
{
|
|
||||||
public V data;
|
|
||||||
public int frequency;
|
|
||||||
public int code;
|
|
||||||
public int range;
|
|
||||||
public TreeNode child1;
|
|
||||||
public TreeNode child2;
|
|
||||||
|
|
||||||
public void encode( int range, int code )
|
|
||||||
{
|
|
||||||
this.range = range;
|
|
||||||
this.code = code;
|
|
||||||
boolean isNode = child1 != null;
|
|
||||||
bwb.encodeBit( isNode );
|
|
||||||
if ( isNode )
|
|
||||||
{
|
|
||||||
child1.encode( range << 1, code );
|
|
||||||
child2.encode( range << 1, code + range );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bwb.encodeBit( data == null );
|
|
||||||
if ( data != null )
|
|
||||||
{
|
|
||||||
encodeItem( data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals( Object o )
|
|
||||||
{
|
|
||||||
return itemEquals( ((TreeNode)o).data, data );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return itemHashCode( data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
/**
|
|
||||||
* common base class for the map-filters
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
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<String,String> tags;
|
|
||||||
|
|
||||||
public void putTag( String key, String value )
|
|
||||||
{
|
|
||||||
if ( tags == null ) tags = new HashMap<String,String>();
|
|
||||||
tags.put( key, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTag( String key )
|
|
||||||
{
|
|
||||||
return tags == null ? null : tags.get( key );
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String,String> getTagsOrNull()
|
|
||||||
{
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTags( HashMap<String,String> 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 void wayFileStart( File wayfile ) throws Exception {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextWay( WayData data ) throws Exception {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void wayFileEnd( File wayfile ) throws Exception {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void relationFileStart( File relfile ) throws Exception {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextRelation( RelationData data ) throws Exception {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void relationFileEnd( File relfile ) throws Exception {}
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
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 long gid; // geo-id
|
|
||||||
|
|
||||||
public int zoom = -1; // the zoom level this node is on
|
|
||||||
public int nativeIndex; // the index along all NATIVE nodes of it's tile
|
|
||||||
|
|
||||||
public transient int localeIndex; // the index along all USED nodes of it's tile
|
|
||||||
public transient boolean used; // whether this node is used by a way
|
|
||||||
|
|
||||||
public NodeData( long id, double lon, double lat )
|
|
||||||
{
|
|
||||||
nid = id;
|
|
||||||
double y = gudermannianInv( lat );
|
|
||||||
ilat = (int)( (1.-y/Math.PI )*( 1L << 27 )+ 0.5);
|
|
||||||
ilon = (int)( ( lon/180. + 1. )*( 1L << 27 ) + 0.5 );
|
|
||||||
}
|
|
||||||
|
|
||||||
public NodeData( long id, int ilon, int ilat )
|
|
||||||
{
|
|
||||||
this.nid = id;
|
|
||||||
this.ilat = ilat;
|
|
||||||
this.ilon = ilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inBBox( int z, int x, int y )
|
|
||||||
{
|
|
||||||
int shift = 28-z;
|
|
||||||
int x0 = x << shift;
|
|
||||||
int x1 = (x+1) << shift;
|
|
||||||
int y0 = y << shift;
|
|
||||||
int y1 = (y+1) << shift;
|
|
||||||
boolean outofbox = x1 < ilon || x0 >= ilon || y1 < ilat || y0 >= ilat;
|
|
||||||
return !outofbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double gudermannianInv(double latitude)
|
|
||||||
{
|
|
||||||
double sign = latitude < 0. ? -1. : 1.;
|
|
||||||
double sin = Math.sin( latitude * (Math.PI / 180.) * sign);
|
|
||||||
return sign * (Math.log((1.0 + sin) / (1.0 - sin)) / 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double gudermannian(double y)
|
|
||||||
{
|
|
||||||
return Math.atan(Math.sinh(y)) * (180. / Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public double getLon()
|
|
||||||
{
|
|
||||||
return (((double)ilon)/( 1L << 27 ) - 1.)*180.;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLat()
|
|
||||||
{
|
|
||||||
double y = (1. - ((double)ilat)/( 1L << 27 ))*Math.PI;
|
|
||||||
return gudermannian(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void calcGeoId()
|
|
||||||
{
|
|
||||||
if ( zoom < 0 ) throw new IllegalArgumentException( "no zoom level yet" );
|
|
||||||
|
|
||||||
gid = 0L;
|
|
||||||
|
|
||||||
for( long bm = 1L << (27-zoom); bm > 0; bm >>= 1 )
|
|
||||||
{
|
|
||||||
gid <<= 2;
|
|
||||||
if ( ( ilon & bm ) != 0 ) gid |= 1;
|
|
||||||
if ( ( ilat & bm ) != 0 ) gid |= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sortByGeoId( List<NodeData> nodes )
|
|
||||||
{
|
|
||||||
Collections.sort( nodes, new Comparator<NodeData>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public int compare(NodeData n1, NodeData n2)
|
|
||||||
{
|
|
||||||
long d = n1.gid - n2.gid;
|
|
||||||
|
|
||||||
// for equal positions sort by nid
|
|
||||||
if ( d == 0L )
|
|
||||||
{
|
|
||||||
d = n1.nid - n2.nid;
|
|
||||||
}
|
|
||||||
return d == 0 ? 0 : ( d < 0 ? -1 : 1 );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public NodeData( DiffCoderDataInputStream dis ) throws Exception
|
|
||||||
{
|
|
||||||
zoom = dis.readInt();
|
|
||||||
nativeIndex = dis.readInt();
|
|
||||||
nid = dis.readDiffed( 0 );
|
|
||||||
ilon = (int)dis.readDiffed( 1 );
|
|
||||||
ilat = (int)dis.readDiffed( 2 );
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
String key = dis.readUTF();
|
|
||||||
if ( key.length() == 0 ) break;
|
|
||||||
String value = dis.readUTF();
|
|
||||||
putTag( key, value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
|
|
||||||
{
|
|
||||||
dos.writeInt( zoom );
|
|
||||||
dos.writeInt( nativeIndex );
|
|
||||||
dos.writeDiffed( nid, 0 );
|
|
||||||
dos.writeDiffed( ilon, 1 );
|
|
||||||
dos.writeDiffed( ilat, 2 );
|
|
||||||
if ( getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
for( Map.Entry<String,String> me : getTagsOrNull().entrySet() )
|
|
||||||
{
|
|
||||||
if ( me.getKey().length() > 0 )
|
|
||||||
{
|
|
||||||
dos.writeUTF( me.getKey() );
|
|
||||||
dos.writeUTF( me.getValue() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dos.writeUTF( "" );
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mercatorLon( long x, long z )
|
|
||||||
{
|
|
||||||
return (int) ( ( 360000000L * x ) >> z );
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mercatorLat( long y, long z )
|
|
||||||
{
|
|
||||||
double n = Math.PI - ( 2.0 * Math.PI * y ) / ( 1L << z );
|
|
||||||
double d = Math.toDegrees( Math.atan( Math.sinh( n ) ) );
|
|
||||||
return (int) ( ( d + 90. ) * 1000000. + 0.5 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals( Object o )
|
|
||||||
{
|
|
||||||
if ( o instanceof NodeData )
|
|
||||||
{
|
|
||||||
NodeData n = (NodeData) o;
|
|
||||||
return n.nid == nid;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return (int)((nid >> 32) ^ nid);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public NodeIterator( NodeListener nodeListener )
|
|
||||||
{
|
|
||||||
listener = nodeListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<af.length; i++ )
|
|
||||||
{
|
|
||||||
File nodefile = af[i];
|
|
||||||
if ( nodefile.getName().endsWith( inSuffix ) )
|
|
||||||
{
|
|
||||||
processFile( nodefile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void processFile(File nodefile) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "*** NodeIterator reading: " + nodefile );
|
|
||||||
|
|
||||||
listener.nodeFileStart( nodefile );
|
|
||||||
|
|
||||||
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
NodeData n = new NodeData( di );
|
|
||||||
listener.nextNode( n );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( EOFException eof )
|
|
||||||
{
|
|
||||||
di.close();
|
|
||||||
}
|
|
||||||
listener.nodeFileEnd( nodefile );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callbacklistener for NodeIterator
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public interface NodeListener
|
|
||||||
{
|
|
||||||
void nodeFileStart( File nodefile ) throws Exception;
|
|
||||||
|
|
||||||
void nextNode( NodeData data ) throws Exception;
|
|
||||||
|
|
||||||
void nodeFileEnd( File nodefile ) throws Exception;
|
|
||||||
}
|
|
|
@ -1,248 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
import btools.util.LongList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser for OSM data
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class OsmParser2 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, "UTF8" ));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( mapFile.getName().endsWith( ".gz" ) )
|
|
||||||
{
|
|
||||||
_br = new BufferedReader(new InputStreamReader( new GZIPInputStream( new FileInputStream( mapFile ) ),"UTF8" ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_br = new BufferedReader(new InputStreamReader( new FileInputStream( mapFile ) , "UTF8" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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( "<node id=\"" );
|
|
||||||
if ( idx0 < 0 ) return false;
|
|
||||||
idx0 += 10;
|
|
||||||
int idx1 = line.indexOf( '"', idx0 );
|
|
||||||
|
|
||||||
long nodeId = Long.parseLong( line.substring( idx0, idx1 ) );
|
|
||||||
|
|
||||||
int idx2 = line.indexOf( " lat=\"" );
|
|
||||||
if ( idx2 < 0 ) return false;
|
|
||||||
idx2 += 6;
|
|
||||||
int idx3 = line.indexOf( '"', idx2 );
|
|
||||||
double lat = Double.parseDouble( line.substring( idx2, idx3 ) );
|
|
||||||
int idx4 = line.indexOf( " lon=\"" );
|
|
||||||
if ( idx4 < 0 ) return false;
|
|
||||||
idx4 += 6;
|
|
||||||
int idx5 = line.indexOf( '"', idx4 );
|
|
||||||
double lon = Double.parseDouble( line.substring( idx4, idx5 ) );
|
|
||||||
|
|
||||||
NodeData n = new NodeData( nodeId, lon, lat );
|
|
||||||
|
|
||||||
if ( !line.endsWith( "/>" ) )
|
|
||||||
{
|
|
||||||
// read additional tags
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
String l2 = _br.readLine();
|
|
||||||
if ( l2 == null ) return false;
|
|
||||||
|
|
||||||
int i2;
|
|
||||||
if ( (i2 = l2.indexOf( "<tag k=\"" )) >= 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( "</node>" ) >= 0 )
|
|
||||||
{ // end-tag
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nListener.nextNode( n );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean checkWay( String line ) throws Exception
|
|
||||||
{
|
|
||||||
int idx0 = line.indexOf( "<way id=\"" );
|
|
||||||
if ( idx0 < 0 ) return false;
|
|
||||||
|
|
||||||
idx0 += 9;
|
|
||||||
int idx1 = line.indexOf( '"', idx0 );
|
|
||||||
long id = Long.parseLong( line.substring( idx0, idx1 ) );
|
|
||||||
|
|
||||||
WayData w = new WayData( id );
|
|
||||||
|
|
||||||
// read the nodes
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
String l2 = _br.readLine();
|
|
||||||
if ( l2 == null ) return false;
|
|
||||||
|
|
||||||
int i2;
|
|
||||||
if ( (i2 = l2.indexOf( "<nd ref=\"" )) >= 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( "<tag k=\"" )) >= 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( "</way>" ) >= 0 )
|
|
||||||
{ // end-tag
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wListener.nextWay( w );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkChangeset( String line ) throws Exception
|
|
||||||
{
|
|
||||||
int idx0 = line.indexOf( "<changeset id=\"" );
|
|
||||||
if ( idx0 < 0 ) return false;
|
|
||||||
|
|
||||||
if ( !line.endsWith( "/>" ) )
|
|
||||||
{
|
|
||||||
int loopcheck = 0;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
String l2 = _br.readLine();
|
|
||||||
if ( l2.indexOf("</changeset>") >= 0 || ++loopcheck > 10000 ) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkRelation( String line ) throws Exception
|
|
||||||
{
|
|
||||||
int idx0 = line.indexOf( "<relation id=\"" );
|
|
||||||
if ( idx0 < 0 ) return false;
|
|
||||||
|
|
||||||
idx0 += 14;
|
|
||||||
int idx1 = line.indexOf( '"', idx0 );
|
|
||||||
long rid = Long.parseLong( line.substring( idx0, idx1 ) );
|
|
||||||
|
|
||||||
LongList wayIds = new LongList( 16 );
|
|
||||||
List<String> roles = new ArrayList<String>(16);
|
|
||||||
RelationData r = new RelationData( rid, wayIds, roles );
|
|
||||||
|
|
||||||
// read the nodes
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
String l2 = _br.readLine();
|
|
||||||
if ( l2 == null ) return false;
|
|
||||||
|
|
||||||
int i2;
|
|
||||||
if ( (i2 = l2.indexOf( "<member type=\"way\" ref=\"" )) >= 0 ) // <member type="relation" ref="452156" role="backward"/>
|
|
||||||
{ // node reference
|
|
||||||
i2 += 24;
|
|
||||||
int ri2 = l2.indexOf( '"', i2 );
|
|
||||||
long wid = Long.parseLong( l2.substring( i2, ri2 ) );
|
|
||||||
|
|
||||||
int role1 = ri2 + 8;
|
|
||||||
int role2 = l2.indexOf( '"', role1 );
|
|
||||||
String role = l2.substring( role1, role2 );
|
|
||||||
|
|
||||||
r.ways.add( wid );
|
|
||||||
r.roles.add( role );
|
|
||||||
}
|
|
||||||
else if ( (i2 = l2.indexOf( "<tag k=\"" )) >= 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( "</relation>" ) >= 0 )
|
|
||||||
{ // end-tag
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rListener.nextRelation( r );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/**
|
|
||||||
* This program
|
|
||||||
* - reads an *.osm from stdin
|
|
||||||
* - writes zoom 0 tiles
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
|
|
||||||
import btools.util.DiffCoderDataOutputStream;
|
|
||||||
|
|
||||||
public class OsmSplitter extends MapCreatorBase
|
|
||||||
{
|
|
||||||
private long recordCnt;
|
|
||||||
private long nodesParsed;
|
|
||||||
private long waysParsed;
|
|
||||||
private long relsParsed;
|
|
||||||
private long changesetsParsed;
|
|
||||||
|
|
||||||
private DataOutputStream wayDos;
|
|
||||||
private DataOutputStream relDos;
|
|
||||||
private DiffCoderDataOutputStream nodeDos;
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println("*** OsmSplitter : transform an osm map to zoom 0 tiles");
|
|
||||||
if (args.length != 2)
|
|
||||||
{
|
|
||||||
System.out.println("usage : java OsmSplitter <tile-dir> <inputfile> ");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new OsmSplitter().process(
|
|
||||||
new File( args[0] )
|
|
||||||
, new File( args[1] )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process (File outTileDir, File mapFile ) throws Exception
|
|
||||||
{
|
|
||||||
if ( !outTileDir.isDirectory() ) throw new RuntimeException( "out tile directory " + outTileDir + " does not exist" );
|
|
||||||
|
|
||||||
File z0 = new File( outTileDir, "0" );
|
|
||||||
z0.mkdirs();
|
|
||||||
File ways = new File( z0, "0_0.wtl" );
|
|
||||||
File nodes = new File( z0, "0_0.ntl" );
|
|
||||||
File rels = new File( z0, "0_0.rtl" );
|
|
||||||
|
|
||||||
wayDos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( ways ) ) );
|
|
||||||
relDos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( rels ) ) );
|
|
||||||
nodeDos = new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( nodes ) ) );
|
|
||||||
|
|
||||||
// read the osm map into memory
|
|
||||||
long t0 = System.currentTimeMillis();
|
|
||||||
new OsmParser2().readMap( mapFile, this, this, this );
|
|
||||||
long t1 = System.currentTimeMillis();
|
|
||||||
|
|
||||||
System.out.println( "parsing time (ms) =" + (t1-t0) );
|
|
||||||
|
|
||||||
// close all files
|
|
||||||
wayDos.close();
|
|
||||||
nodeDos.close();
|
|
||||||
|
|
||||||
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();
|
|
||||||
n.writeTo( nodeDos );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextWay( WayData w ) throws Exception
|
|
||||||
{
|
|
||||||
waysParsed++;
|
|
||||||
checkStats();
|
|
||||||
|
|
||||||
w.writeTo( wayDos );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextRelation( RelationData r ) throws Exception
|
|
||||||
{
|
|
||||||
relsParsed++;
|
|
||||||
checkStats();
|
|
||||||
|
|
||||||
r.writeTo( relDos );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.LongList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for relation data on the preprocessor level
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class RelationData extends MapCreatorBase
|
|
||||||
{
|
|
||||||
public long rid;
|
|
||||||
public LongList ways;
|
|
||||||
public List<String> roles;
|
|
||||||
|
|
||||||
public RelationData( long id, LongList ways, List<String> roles )
|
|
||||||
{
|
|
||||||
rid = id;
|
|
||||||
this.ways = ways;
|
|
||||||
this.roles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RelationData( DataInputStream di ) throws Exception
|
|
||||||
{
|
|
||||||
ways = new LongList( 16 );
|
|
||||||
roles = new ArrayList<String>();
|
|
||||||
rid = readId( di) ;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
String key = di.readUTF();
|
|
||||||
if ( key.length() == 0 ) break;
|
|
||||||
String value = di.readUTF();
|
|
||||||
putTag( key, value );
|
|
||||||
}
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
long wid = readId( di );
|
|
||||||
if ( wid == -1 ) break;
|
|
||||||
ways.add( wid );
|
|
||||||
roles.add( di.readUTF() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo( java.io.DataOutputStream dos ) throws Exception
|
|
||||||
{
|
|
||||||
writeId( dos, rid );
|
|
||||||
if ( getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
for( Map.Entry<String,String> me : getTagsOrNull().entrySet() )
|
|
||||||
{
|
|
||||||
if ( me.getKey().length() > 0 )
|
|
||||||
{
|
|
||||||
dos.writeUTF( me.getKey() );
|
|
||||||
dos.writeUTF( me.getValue() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dos.writeUTF( "" );
|
|
||||||
|
|
||||||
int size = ways.size();
|
|
||||||
for( int i=0; i < size; i++ )
|
|
||||||
{
|
|
||||||
writeId( dos, ways.get( i ) );
|
|
||||||
dos.writeUTF( roles.get(i) );
|
|
||||||
}
|
|
||||||
writeId( dos, -1 ); // stopbyte
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over a relation file
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class RelationIterator extends MapCreatorBase
|
|
||||||
{
|
|
||||||
private RelationListener listener;
|
|
||||||
|
|
||||||
public RelationIterator( RelationListener relationListener )
|
|
||||||
{
|
|
||||||
listener = relationListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processFile(File relationfile) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "*** RelationIterator reading: " + relationfile );
|
|
||||||
|
|
||||||
listener.relationFileStart( relationfile );
|
|
||||||
|
|
||||||
DataInputStream di = new DataInputStream( new BufferedInputStream ( new FileInputStream( relationfile ) ) );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
RelationData r = new RelationData( di );
|
|
||||||
listener.nextRelation( r );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( EOFException eof )
|
|
||||||
{
|
|
||||||
di.close();
|
|
||||||
}
|
|
||||||
listener.relationFileEnd( relationfile );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callbacklistener for Relations
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public interface RelationListener
|
|
||||||
{
|
|
||||||
void relationFileStart( File relfile ) throws Exception;
|
|
||||||
|
|
||||||
void nextRelation( RelationData data ) throws Exception;
|
|
||||||
|
|
||||||
void relationFileEnd( File relfile ) throws Exception;
|
|
||||||
}
|
|
|
@ -1,194 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.PriorityQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoder for a set of tags
|
|
||||||
*
|
|
||||||
* It detects identical sets 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 TagSetEncoder
|
|
||||||
{
|
|
||||||
private HashMap<TagSet, TagSet> identityMap;
|
|
||||||
private BitWriteBuffer bwb;
|
|
||||||
private int pass;
|
|
||||||
private TagSet freq1;
|
|
||||||
|
|
||||||
public void encodeTagSet( int[] data )
|
|
||||||
{
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TagSet tvsProbe = new TagSet();
|
|
||||||
tvsProbe.data = data;
|
|
||||||
TagSet tvs = identityMap.get( tvsProbe );
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
if ( tvs.frequency == 1 )
|
|
||||||
{
|
|
||||||
bwb.encodeBounded( freq1.range - 1, freq1.code );
|
|
||||||
encodeTagSequence( bwb, data );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bwb.encodeBounded( tvs.range - 1, tvs.code );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
if ( tvs == null )
|
|
||||||
{
|
|
||||||
tvs = tvsProbe;
|
|
||||||
identityMap.put( tvs, tvs );
|
|
||||||
}
|
|
||||||
tvs.frequency++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeDictionary( BitWriteBuffer bwb )
|
|
||||||
{
|
|
||||||
if ( ++pass == 3 )
|
|
||||||
{
|
|
||||||
freq1 = new TagSet();
|
|
||||||
PriorityQueue<TagSet> queue = new PriorityQueue<TagSet>(2*identityMap.size(), new TagSet.FrequencyComparator());
|
|
||||||
for( TagSet ts : identityMap.values() )
|
|
||||||
{
|
|
||||||
if ( ts.frequency > 1 )
|
|
||||||
{
|
|
||||||
queue.add( ts );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
freq1.frequency++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queue.add( freq1 );
|
|
||||||
while (queue.size() > 1)
|
|
||||||
{
|
|
||||||
TagSet node = new TagSet();
|
|
||||||
node.child1 = queue.poll();
|
|
||||||
node.child2 = queue.poll();
|
|
||||||
node.frequency = node.child1.frequency + node.child2.frequency;
|
|
||||||
queue.add( node );
|
|
||||||
}
|
|
||||||
TagSet root = queue.poll();
|
|
||||||
root.encode( bwb, 1, 0 );
|
|
||||||
}
|
|
||||||
this.bwb = bwb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagSetEncoder()
|
|
||||||
{
|
|
||||||
identityMap = new HashMap<TagSet, TagSet>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void encodeTagSequence( BitWriteBuffer bwb, int[] data )
|
|
||||||
{
|
|
||||||
int tagcount = data.length;
|
|
||||||
bwb.encodeInt( tagcount );
|
|
||||||
int lastIdx = -1;
|
|
||||||
for( int i=0; i<tagcount; i++ )
|
|
||||||
{
|
|
||||||
int idx = data[i];
|
|
||||||
bwb.encodeInt( idx - lastIdx -1 );
|
|
||||||
lastIdx = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class TagSet
|
|
||||||
{
|
|
||||||
public int[] data;
|
|
||||||
public int frequency;
|
|
||||||
public int code;
|
|
||||||
public int range;
|
|
||||||
public TagSet child1;
|
|
||||||
public TagSet child2;
|
|
||||||
|
|
||||||
public void encode( BitWriteBuffer bwb, int range, int code )
|
|
||||||
{
|
|
||||||
this.range = range;
|
|
||||||
this.code = code;
|
|
||||||
boolean isNode = child1 != null;
|
|
||||||
bwb.encodeBit( isNode );
|
|
||||||
if ( isNode )
|
|
||||||
{
|
|
||||||
child1.encode( bwb, range << 1, code );
|
|
||||||
child2.encode( bwb, range << 1, code + range );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bwb.encodeBit( data == null );
|
|
||||||
if ( data != null )
|
|
||||||
{
|
|
||||||
encodeTagSequence( bwb, data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals( Object o )
|
|
||||||
{
|
|
||||||
if ( o instanceof TagSet )
|
|
||||||
{
|
|
||||||
TagSet tvs = (TagSet) o;
|
|
||||||
if ( data == null )
|
|
||||||
{
|
|
||||||
return tvs.data == null;
|
|
||||||
}
|
|
||||||
if ( tvs.data == null )
|
|
||||||
{
|
|
||||||
return data == null;
|
|
||||||
}
|
|
||||||
if ( data.length != tvs.data.length )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for ( int i = 0; i < data.length; i++ )
|
|
||||||
{
|
|
||||||
if ( data[i] != tvs.data[i] )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
if ( data == null )
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int h = 17;
|
|
||||||
for ( int i = 0; i < data.length; i++ )
|
|
||||||
{
|
|
||||||
h = ( h << 8 ) + data[i];
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FrequencyComparator implements Comparator<TagSet>
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(TagSet tvs1, TagSet tvs2) {
|
|
||||||
if ( tvs1.frequency < tvs2.frequency )
|
|
||||||
return -1;
|
|
||||||
if ( tvs1.frequency > tvs2.frequency )
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,303 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.PriorityQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoder for the tag-value statistics
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class TagValueEncoder
|
|
||||||
{
|
|
||||||
HashMap<String,Tag> tags = new HashMap<String,Tag>();
|
|
||||||
ArrayList<TagGroup> groups = new ArrayList<TagGroup>();
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos ;
|
|
||||||
DataOutputStream dos;
|
|
||||||
|
|
||||||
ArrayList<String> stringList;
|
|
||||||
HashMap<String,Integer> stringMap;
|
|
||||||
ArrayList<Tag> taglist;
|
|
||||||
|
|
||||||
private int setId = 0;
|
|
||||||
private int nextIdx = 0;
|
|
||||||
|
|
||||||
private int pass;
|
|
||||||
|
|
||||||
private static String[][] taggroups = new String[][] {
|
|
||||||
{ "highway", "name", "maxspeed", "lanes", "service", "tracktype", "surface" }
|
|
||||||
, { "access", "foot", "bicycle", "motorcar", "motor_vehicle", "motorcycle", "vehicle" }
|
|
||||||
, { "building", "addr:street", "addr:housenumber", "addr:city", "addr:postcode", "addr:housename" }
|
|
||||||
};
|
|
||||||
|
|
||||||
private void encodeString( BitWriteBuffer bc, String s )
|
|
||||||
{
|
|
||||||
Integer ii = stringMap.get( s );
|
|
||||||
bc.encodeBit( ii == null );
|
|
||||||
if ( ii == null )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] textBytes = s.getBytes( "UTF8" );
|
|
||||||
bc.encodeInt( textBytes.length );
|
|
||||||
dos.write( textBytes );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
throw new RuntimeException( e );
|
|
||||||
}
|
|
||||||
ii = Integer.valueOf( stringList.size() );
|
|
||||||
stringList.add( s );
|
|
||||||
stringMap.put( s, ii );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bc.encodeBounded( stringList.size()-1, ii.intValue() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TagGroup implements Comparable<TagGroup>
|
|
||||||
{
|
|
||||||
String[] names;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
int lastSetId = 0;
|
|
||||||
|
|
||||||
void incCount()
|
|
||||||
{
|
|
||||||
if ( setId != lastSetId ) count++;
|
|
||||||
lastSetId = setId;
|
|
||||||
}
|
|
||||||
|
|
||||||
TagGroup( String[] names )
|
|
||||||
{
|
|
||||||
this.names = names;
|
|
||||||
for( String name : names )
|
|
||||||
{
|
|
||||||
tags.put( name, new Tag( name, this ) );
|
|
||||||
}
|
|
||||||
groups.add( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
void indexTags()
|
|
||||||
{
|
|
||||||
for( String name : names )
|
|
||||||
{
|
|
||||||
Tag t = tags.get( name );
|
|
||||||
if ( t.count > 0 ) t.idx = nextIdx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo( TagGroup g )
|
|
||||||
{
|
|
||||||
return g.count - count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TagValueEncoder()
|
|
||||||
{
|
|
||||||
for( String[] names : taggroups )
|
|
||||||
{
|
|
||||||
new TagGroup( names );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Tag implements Comparable<Tag>
|
|
||||||
{
|
|
||||||
Tag( String name, TagGroup group )
|
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
this.group = group;
|
|
||||||
}
|
|
||||||
String name;
|
|
||||||
int count;
|
|
||||||
int idx;
|
|
||||||
|
|
||||||
private Object tree;
|
|
||||||
|
|
||||||
HashMap<String,Value> values = new HashMap<String,Value>();
|
|
||||||
|
|
||||||
List<Value> valueList;
|
|
||||||
TagGroup group;
|
|
||||||
|
|
||||||
void addValue( String value )
|
|
||||||
{
|
|
||||||
Value v = values.get( value );
|
|
||||||
if ( v == null )
|
|
||||||
{
|
|
||||||
v = new Value( value );
|
|
||||||
values.put( value, v );
|
|
||||||
}
|
|
||||||
v.frequency++;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeDictionary( BitWriteBuffer bc ) throws IOException
|
|
||||||
{
|
|
||||||
encodeString( bc, name );
|
|
||||||
|
|
||||||
PriorityQueue<Value> queue = new PriorityQueue<Value>( values.size() );
|
|
||||||
queue.addAll( values.values() );
|
|
||||||
while (queue.size() > 1)
|
|
||||||
{
|
|
||||||
queue.add( new Value( queue.poll(), queue.poll() ) );
|
|
||||||
}
|
|
||||||
queue.poll().encodeTree( bc, 1, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo( Tag t )
|
|
||||||
{
|
|
||||||
return idx - t.idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Value implements Comparable<Value>
|
|
||||||
{
|
|
||||||
Value( String value )
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
Value( Value c1, Value c2 )
|
|
||||||
{
|
|
||||||
child1 = c1;
|
|
||||||
child2 = c2;
|
|
||||||
frequency = c1.frequency + c2.frequency;
|
|
||||||
}
|
|
||||||
String value;
|
|
||||||
int code;
|
|
||||||
int range;
|
|
||||||
Value child1;
|
|
||||||
Value child2;
|
|
||||||
int frequency;
|
|
||||||
|
|
||||||
void encodeTree( BitWriteBuffer bc, int range, int code ) throws IOException
|
|
||||||
{
|
|
||||||
this.range = range;
|
|
||||||
this.code = code;
|
|
||||||
boolean isNode = child1 != null;
|
|
||||||
bc.encodeBit( isNode );
|
|
||||||
if ( isNode )
|
|
||||||
{
|
|
||||||
child1.encodeTree( bc, range << 1, code );
|
|
||||||
child2.encodeTree( bc, range << 1, code + range );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bc.encodeBit( false ); // no inline item here
|
|
||||||
encodeString( bc, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
void encode( BitWriteBuffer bc )
|
|
||||||
{
|
|
||||||
bc.encodeBounded( range - 1, code );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo( Value v )
|
|
||||||
{
|
|
||||||
return frequency - v.frequency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] encodeDictionary( BitWriteBuffer bc ) throws IOException
|
|
||||||
{
|
|
||||||
if ( ++pass == 1 )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
nextIdx = 0;
|
|
||||||
Collections.sort( groups );
|
|
||||||
for( TagGroup g : groups )
|
|
||||||
{
|
|
||||||
g.indexTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
taglist = new ArrayList<Tag>();
|
|
||||||
for( Tag t : tags.values() )
|
|
||||||
{
|
|
||||||
if ( t.count > 0 )
|
|
||||||
{
|
|
||||||
taglist.add( t );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Collections.sort( taglist );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
stringList = new ArrayList<String>();
|
|
||||||
stringMap = new HashMap<String,Integer>();
|
|
||||||
|
|
||||||
baos = new ByteArrayOutputStream();
|
|
||||||
dos = new DataOutputStream( baos );
|
|
||||||
|
|
||||||
bc.encodeInt( taglist.size() );
|
|
||||||
for( Tag t : taglist )
|
|
||||||
{
|
|
||||||
t.encodeDictionary( bc );
|
|
||||||
}
|
|
||||||
|
|
||||||
dos.close();
|
|
||||||
byte[] textData = baos.toByteArray();
|
|
||||||
dos = null;
|
|
||||||
baos = null;
|
|
||||||
return textData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTagIndex( String name )
|
|
||||||
{
|
|
||||||
return tags.get( name ).idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> sortTagNames( Collection<String> col )
|
|
||||||
{
|
|
||||||
ArrayList<Tag> taglist = new ArrayList<Tag>( col.size() );
|
|
||||||
for( String name : col )
|
|
||||||
{
|
|
||||||
taglist.add( tags.get( name ) );
|
|
||||||
}
|
|
||||||
Collections.sort( taglist );
|
|
||||||
ArrayList<String> res = new ArrayList<String>( taglist.size() );
|
|
||||||
for( Tag t : taglist )
|
|
||||||
{
|
|
||||||
res.add( t.name );
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startTagSet()
|
|
||||||
{
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
setId++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeValue( BitWriteBuffer bc, String name, String value )
|
|
||||||
{
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
Tag t = tags.get( name );
|
|
||||||
if ( t == null )
|
|
||||||
{
|
|
||||||
String[] names = new String[1];
|
|
||||||
names[0] = name;
|
|
||||||
new TagGroup( names );
|
|
||||||
t = tags.get( name );
|
|
||||||
}
|
|
||||||
t.addValue( value );
|
|
||||||
}
|
|
||||||
else // pass 2+3
|
|
||||||
{
|
|
||||||
tags.get( name ).values.get( value ).encode( bc );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.DiffCoderDataInputStream;
|
|
||||||
import btools.util.DiffCoderDataOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container a tile during encoding
|
|
||||||
*/
|
|
||||||
public class TileData extends MapCreatorBase
|
|
||||||
{
|
|
||||||
public int zoom;
|
|
||||||
public int x;
|
|
||||||
public int y;
|
|
||||||
public List<NodeData> nodeList;
|
|
||||||
public TileData parent;
|
|
||||||
}
|
|
|
@ -1,555 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TileEncoder encodes a given node/way file pair
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class TileEncoder extends MapCreatorBase
|
|
||||||
{
|
|
||||||
private Map<NodeData,NodeData> nodeMap;
|
|
||||||
private Map<WayData,WayData> wayMap;
|
|
||||||
private List<NodeData> used = new ArrayList<NodeData>();
|
|
||||||
|
|
||||||
private NodeData templateNode = new NodeData( 0, 0, 0 );
|
|
||||||
private WayData templateWay = new WayData( 0, null );
|
|
||||||
|
|
||||||
private TileData tile;
|
|
||||||
|
|
||||||
private BitWriteBuffer bwb;
|
|
||||||
|
|
||||||
private byte[] buffer;
|
|
||||||
|
|
||||||
private List<WayData> wayList;
|
|
||||||
|
|
||||||
private List<RelationData> relationList;
|
|
||||||
|
|
||||||
// statistics only
|
|
||||||
private int nTagSets;
|
|
||||||
|
|
||||||
private int nTaggedNodes;
|
|
||||||
private long totalNodes;
|
|
||||||
private long totalTaggedNodes;
|
|
||||||
private long totalWays;
|
|
||||||
private long totalTextBytes;
|
|
||||||
private long totalTiles;
|
|
||||||
|
|
||||||
private int pass;
|
|
||||||
private boolean dostats;
|
|
||||||
private TagValueEncoder tagValueEncoder;
|
|
||||||
private TagSetEncoder tagSetEnoder;
|
|
||||||
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "*** TileEncoder: encodes a given node/way file pair" );
|
|
||||||
if ( args.length != 1 )
|
|
||||||
{
|
|
||||||
System.out.println( "usage: java TileEncoder <node-file>" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new TileEncoder().process( new File( args[0] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process( File nodeFile) throws Exception
|
|
||||||
{
|
|
||||||
TileData t0 = new TileData(); // zoom 0 dummy
|
|
||||||
process( nodeFile, t0 );
|
|
||||||
|
|
||||||
System.out.println( "**** total statistics ****" );
|
|
||||||
System.out.println( "tiles=" + totalTiles + " nodes=" + totalNodes + " taggedNodes=" + totalTaggedNodes + " ways=" + totalWays + " textBytes= " + totalTextBytes );
|
|
||||||
System.out.println( bwb.getBitReport() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process( File nodeFile, TileData tile ) throws Exception
|
|
||||||
{
|
|
||||||
this.tile = tile;
|
|
||||||
|
|
||||||
if ( !nodeFile.exists() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println( "******* processing: " + nodeFile );
|
|
||||||
|
|
||||||
new NodeIterator( this ).processFile( nodeFile );
|
|
||||||
|
|
||||||
// process childs
|
|
||||||
|
|
||||||
int zoomStep = 2;
|
|
||||||
int xyStep = 1 << zoomStep;
|
|
||||||
|
|
||||||
int nextZoom = tile.zoom + zoomStep;
|
|
||||||
int x0 = tile.x << zoomStep;
|
|
||||||
int y0 = tile.y << zoomStep;
|
|
||||||
|
|
||||||
File childDir = new File( nodeFile.getParentFile().getParentFile(), "" + nextZoom );
|
|
||||||
|
|
||||||
for( int dx = 0; dx < xyStep; dx++ )
|
|
||||||
{
|
|
||||||
for( int dy = 0; dy < xyStep; dy++ )
|
|
||||||
{
|
|
||||||
TileData nextTile = new TileData();
|
|
||||||
nextTile.zoom = nextZoom;
|
|
||||||
nextTile.x = x0 + dx;
|
|
||||||
nextTile.y = y0 + dy;
|
|
||||||
nextTile.parent = tile;
|
|
||||||
File nextFile = new File( childDir, nextTile.x + "_" + nextTile.y + ".ntl" );
|
|
||||||
process( nextFile, nextTile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeFileStart( File nodeFile ) throws Exception
|
|
||||||
{
|
|
||||||
tile.nodeList = new ArrayList<NodeData>();
|
|
||||||
nodeMap = new HashMap<NodeData,NodeData>();
|
|
||||||
wayMap = new HashMap<WayData,WayData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextNode( NodeData n ) throws Exception
|
|
||||||
{
|
|
||||||
// if no level yet, it's ours
|
|
||||||
if ( n.zoom == -1 || n.zoom == tile.zoom )
|
|
||||||
{
|
|
||||||
n.zoom = tile.zoom;
|
|
||||||
n.used = true;
|
|
||||||
tile.nodeList.add( n );
|
|
||||||
}
|
|
||||||
n.localeIndex = nodeMap.size();
|
|
||||||
nodeMap.put( n,n );
|
|
||||||
n.calcGeoId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
|
||||||
{
|
|
||||||
NodeData.sortByGeoId( tile.nodeList );
|
|
||||||
int idx = 0;
|
|
||||||
for( NodeData n : tile.nodeList )
|
|
||||||
{
|
|
||||||
n.nativeIndex = idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read corresponding way-file into wayList
|
|
||||||
wayList = new ArrayList<WayData>();
|
|
||||||
String name = nodeFile.getName();
|
|
||||||
String wayfilename = name.substring( 0, name.length()-3 ) + "wtl";
|
|
||||||
File wayfile = new File( nodeFile.getParent(), wayfilename );
|
|
||||||
if ( wayfile.exists() )
|
|
||||||
{
|
|
||||||
new WayIterator( this ).processFile( wayfile );
|
|
||||||
}
|
|
||||||
|
|
||||||
// read corresponding relation-file
|
|
||||||
relationList = new ArrayList<RelationData>();
|
|
||||||
String relfilename = name.substring( 0, name.length()-3 ) + "rtl";
|
|
||||||
File relfile = new File( nodeFile.getParent(), relfilename );
|
|
||||||
if ( relfile.exists() )
|
|
||||||
{
|
|
||||||
new RelationIterator( this ).processFile( relfile );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int nnodes = tile.nodeList.size();
|
|
||||||
|
|
||||||
tagValueEncoder = new TagValueEncoder();
|
|
||||||
tagSetEnoder = new TagSetEncoder();
|
|
||||||
|
|
||||||
long[] nodePositions = new long[nnodes];
|
|
||||||
for( int i=0; i<nnodes; i++ )
|
|
||||||
{
|
|
||||||
nodePositions[i] = tile.nodeList.get(i).gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
for( pass=1;pass<=3; pass++) // 3 passes: counters, stat-collection, encoding
|
|
||||||
{
|
|
||||||
nTagSets = 0;
|
|
||||||
|
|
||||||
dostats = pass == 3;
|
|
||||||
|
|
||||||
buffer = new byte[10000000];
|
|
||||||
bwb = new BitWriteBuffer( buffer );
|
|
||||||
|
|
||||||
tagSetEnoder.encodeDictionary( bwb );
|
|
||||||
if ( dostats ) bwb.assignBits( "tagset-dictionary" );
|
|
||||||
|
|
||||||
// encode the dictionary
|
|
||||||
byte[] textData = tagValueEncoder.encodeDictionary( bwb );
|
|
||||||
if ( dostats ) bwb.assignBits( "value-dictionary" );
|
|
||||||
|
|
||||||
// encode the node positions
|
|
||||||
bwb.encodeSortedArray( nodePositions );
|
|
||||||
if ( dostats ) bwb.assignBits( "node-positions" );
|
|
||||||
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
writeDownzoomRefs( bwb );
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode the tagged nodes
|
|
||||||
writeTaggedNodes();
|
|
||||||
|
|
||||||
writeWays( bwb );
|
|
||||||
|
|
||||||
writeRelations( bwb );
|
|
||||||
|
|
||||||
if ( pass == 1 && nTagSets == 0 ) // stop it if nothing tagged
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
assignLocalIndexes();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
// Compress the text-bytes
|
|
||||||
Deflater compresser = new Deflater();
|
|
||||||
compresser.setInput(textData);
|
|
||||||
compresser.finish();
|
|
||||||
byte[] textHeader = new byte[textData.length + 1024];
|
|
||||||
int textHeaderLen = compresser.deflate(textHeader);
|
|
||||||
|
|
||||||
totalTiles++;
|
|
||||||
totalNodes += tile.nodeList.size();
|
|
||||||
totalTaggedNodes += nTaggedNodes;
|
|
||||||
totalTextBytes += textHeaderLen;
|
|
||||||
System.out.println( "nodes=" + tile.nodeList.size() + " taggedNodes=" + nTaggedNodes + " ways=" + wayList.size() + " textBytes= " + textHeaderLen );
|
|
||||||
|
|
||||||
// write result to file
|
|
||||||
String datafilename = name.substring( 0, name.length()-3 ) + "osb";
|
|
||||||
File datafile = new File( nodeFile.getParent(), datafilename );
|
|
||||||
|
|
||||||
DataOutputStream dos = new DataOutputStream( new FileOutputStream( datafile ) );
|
|
||||||
dos.writeInt( textData.length );
|
|
||||||
dos.writeInt( textHeaderLen );
|
|
||||||
dos.write( textHeader, 0, textHeaderLen );
|
|
||||||
int size = bwb.getEncodedLength();
|
|
||||||
dos.writeInt( size );
|
|
||||||
dos.write( buffer, 0, size );
|
|
||||||
dos.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( relfile.exists() )
|
|
||||||
{
|
|
||||||
relfile.delete();
|
|
||||||
}
|
|
||||||
if ( wayfile.exists() )
|
|
||||||
{
|
|
||||||
wayfile.delete();
|
|
||||||
}
|
|
||||||
if ( nodeFile.exists() )
|
|
||||||
{
|
|
||||||
nodeFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextWay( WayData way ) throws Exception
|
|
||||||
{
|
|
||||||
// if no level yet, it's ours
|
|
||||||
if ( way.zoom == -1 || way.zoom == tile.zoom )
|
|
||||||
{
|
|
||||||
way.zoom = tile.zoom;
|
|
||||||
way.startNodeIdx = -1;
|
|
||||||
wayList.add( way );
|
|
||||||
}
|
|
||||||
wayMap.put( way,way );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextRelation( RelationData r ) throws Exception
|
|
||||||
{
|
|
||||||
relationList.add( r );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assignLocalIndexes()
|
|
||||||
{
|
|
||||||
used = new ArrayList<NodeData>();
|
|
||||||
for( NodeData n : nodeMap.values() )
|
|
||||||
{
|
|
||||||
if ( n.used )
|
|
||||||
{
|
|
||||||
used.add( n );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NodeData.sortByGeoId( used );
|
|
||||||
int idx = 0;
|
|
||||||
for( NodeData n : used )
|
|
||||||
{
|
|
||||||
n.localeIndex = idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeDownzoomRefs( BitWriteBuffer bwb )
|
|
||||||
{
|
|
||||||
// total locale nodes
|
|
||||||
bwb.encodeInt( used.size() );
|
|
||||||
|
|
||||||
for( int zoom=0; zoom<tile.zoom; zoom++ )
|
|
||||||
{
|
|
||||||
// count
|
|
||||||
int cnt = 0;
|
|
||||||
for( NodeData n : used )
|
|
||||||
{
|
|
||||||
if ( n.zoom == zoom )
|
|
||||||
{
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long[] localeIndexes = new long[cnt];
|
|
||||||
long[] nativeIndexes = new long[cnt];
|
|
||||||
int idx = 0;
|
|
||||||
for( NodeData n : used )
|
|
||||||
{
|
|
||||||
if ( n.zoom == zoom )
|
|
||||||
{
|
|
||||||
// System.out.println( " ---> locale=" + n.localeIndex + " native=" + n.nativeIndex );
|
|
||||||
localeIndexes[idx] = n.localeIndex;
|
|
||||||
nativeIndexes[idx] = n.nativeIndex;
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bwb.encodeSortedArray( localeIndexes );
|
|
||||||
if ( dostats ) bwb.assignBits( "localindexes" );
|
|
||||||
bwb.encodeSortedArray( nativeIndexes );
|
|
||||||
if ( dostats ) bwb.assignBits( "nativeindexes" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getLocaleIndexForNid( long nid )
|
|
||||||
{
|
|
||||||
templateNode.nid = nid;
|
|
||||||
NodeData n = nodeMap.get( templateNode );
|
|
||||||
if ( n == null ) throw new IllegalArgumentException( "nid=" + nid + " not found" );
|
|
||||||
n.used = true;
|
|
||||||
return n.localeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encodeWay( BitWriteBuffer bwb, WayData way ) throws Exception
|
|
||||||
{
|
|
||||||
int nnodes = way.nodes.size();
|
|
||||||
boolean closedPoly = way.nodes.get(0) == way.nodes.get(nnodes-1);
|
|
||||||
if ( closedPoly )
|
|
||||||
{
|
|
||||||
nnodes--;
|
|
||||||
}
|
|
||||||
if ( nnodes < 2 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeTags( way.getTagsOrNull() );
|
|
||||||
|
|
||||||
bwb.encodeBit( closedPoly );
|
|
||||||
bwb.encodeInt( nnodes-2 );
|
|
||||||
|
|
||||||
if ( dostats ) bwb.assignBits( "way-node-count" );
|
|
||||||
|
|
||||||
// determine the tile-index for each node
|
|
||||||
int lastIdx = 0;
|
|
||||||
for (int i=0; i<nnodes; i++ )
|
|
||||||
{
|
|
||||||
long nid = way.nodes.get(i);
|
|
||||||
int idx = getLocaleIndexForNid( nid );
|
|
||||||
if ( i == 0 )
|
|
||||||
{
|
|
||||||
way.startNodeIdx = idx;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int delta = idx-lastIdx;
|
|
||||||
|
|
||||||
if ( delta == 0 )
|
|
||||||
{
|
|
||||||
System.out.println( "double node in way, ignoring" );
|
|
||||||
way.startNodeIdx = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean negative = delta < 0;
|
|
||||||
bwb.encodeBit( negative );
|
|
||||||
bwb.encodeLong( (negative ? -delta : delta) -1 );
|
|
||||||
|
|
||||||
if ( dostats ) bwb.assignBits( "way-node-idx-delta" );
|
|
||||||
}
|
|
||||||
lastIdx = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void writeWays( BitWriteBuffer bwb ) throws Exception
|
|
||||||
{
|
|
||||||
// in pass 3, sort ways according startNodeIdx and encode start-indexes
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
if ( wayList.size() > 0 )
|
|
||||||
{
|
|
||||||
ArrayList<WayData> goodWays = new ArrayList<WayData>();
|
|
||||||
for( WayData w : wayList )
|
|
||||||
{
|
|
||||||
if ( w.startNodeIdx >= 0 )
|
|
||||||
{
|
|
||||||
goodWays.add( w );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WayData.sortByStartNode( goodWays );
|
|
||||||
wayList = goodWays;
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode start-node-indexes
|
|
||||||
int waycount = wayList.size();
|
|
||||||
long[] startIndexes = new long[waycount];
|
|
||||||
int i = 0;
|
|
||||||
for( WayData w : wayList )
|
|
||||||
{
|
|
||||||
w.nativeIndex = i;
|
|
||||||
startIndexes[i++] = w.startNodeIdx;
|
|
||||||
}
|
|
||||||
bwb.encodeSortedArray( startIndexes );
|
|
||||||
if ( dostats ) bwb.assignBits( "way-start-idx" );
|
|
||||||
}
|
|
||||||
for( WayData way : wayList )
|
|
||||||
{
|
|
||||||
encodeWay( bwb, way );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeRelations( BitWriteBuffer bwb ) throws Exception
|
|
||||||
{
|
|
||||||
bwb.encodeInt( relationList.size() );
|
|
||||||
if ( dostats ) bwb.assignBits( "relation-count" );
|
|
||||||
for( RelationData rel : relationList )
|
|
||||||
{
|
|
||||||
encodeRelation( bwb, rel );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encodeRelation( BitWriteBuffer bwb, RelationData rel ) throws Exception
|
|
||||||
{
|
|
||||||
writeTags( rel.getTagsOrNull() );
|
|
||||||
|
|
||||||
int size = rel.ways.size();
|
|
||||||
if ( dostats ) bwb.assignBits( "way-node-count" );
|
|
||||||
|
|
||||||
// count valid members
|
|
||||||
int validMembers = 0;
|
|
||||||
for( int i=0; i < size; i++ )
|
|
||||||
{
|
|
||||||
long wid = rel.ways.get( i );
|
|
||||||
String role = rel.roles.get(i);
|
|
||||||
templateWay.wid = wid;
|
|
||||||
WayData w = wayMap.get( templateWay );
|
|
||||||
if ( w == null ) continue;
|
|
||||||
validMembers++;
|
|
||||||
}
|
|
||||||
bwb.encodeInt( validMembers );
|
|
||||||
|
|
||||||
for( int i=0; i < size; i++ )
|
|
||||||
{
|
|
||||||
long wid = rel.ways.get( i );
|
|
||||||
String role = rel.roles.get(i);
|
|
||||||
templateWay.wid = wid;
|
|
||||||
WayData w = wayMap.get( templateWay );
|
|
||||||
if ( w == null ) continue;
|
|
||||||
|
|
||||||
int zoomDelta = tile.zoom - w.zoom;
|
|
||||||
|
|
||||||
bwb.encodeInt( zoomDelta );
|
|
||||||
bwb.encodeInt( w.nativeIndex );
|
|
||||||
tagValueEncoder.encodeValue( bwb, "role", role );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTaggedNodes() throws Exception
|
|
||||||
{
|
|
||||||
// count tagged nodes
|
|
||||||
int cnt = 0;
|
|
||||||
for( int idx=0; idx<tile.nodeList.size(); idx++ )
|
|
||||||
{
|
|
||||||
NodeData n = tile.nodeList.get( idx );
|
|
||||||
if ( n.zoom == tile.zoom && n.getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// build index array
|
|
||||||
long[] taggedIndexes = new long[cnt];
|
|
||||||
int i = 0;
|
|
||||||
for( int idx=0; idx<tile.nodeList.size(); idx++ )
|
|
||||||
{
|
|
||||||
if ( tile.nodeList.get( idx ).getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
taggedIndexes[i++] = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nTaggedNodes = cnt;
|
|
||||||
|
|
||||||
bwb.encodeSortedArray( taggedIndexes );
|
|
||||||
if ( dostats ) bwb.assignBits( "tagged-node-idx" );
|
|
||||||
|
|
||||||
for( int idx=0; idx<tile.nodeList.size(); idx++ )
|
|
||||||
{
|
|
||||||
NodeData n = tile.nodeList.get( idx );
|
|
||||||
if ( n.getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
writeTags( n.getTagsOrNull() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTags( HashMap<String, String> tags ) throws Exception
|
|
||||||
{
|
|
||||||
List<String> names;
|
|
||||||
|
|
||||||
if ( tags == null )
|
|
||||||
{
|
|
||||||
tags = new HashMap<String, String>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass > 1 )
|
|
||||||
{
|
|
||||||
// create tagset as sorted int-array
|
|
||||||
names = tagValueEncoder.sortTagNames( tags.keySet() );
|
|
||||||
int ntags = names.size();
|
|
||||||
int[] tagset = new int[ ntags ];
|
|
||||||
for( int i=0; i<ntags; i++ )
|
|
||||||
{
|
|
||||||
tagset[i] = tagValueEncoder.getTagIndex( names.get(i) );
|
|
||||||
}
|
|
||||||
// ... and encode it
|
|
||||||
tagSetEnoder.encodeTagSet( tagset );
|
|
||||||
if ( dostats ) bwb.assignBits( "tag-set" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nTagSets++;
|
|
||||||
|
|
||||||
names = new ArrayList<String>( tags.keySet() ); // unsorted is o.k. in pass 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// then encode the values
|
|
||||||
tagValueEncoder.startTagSet();
|
|
||||||
for( String name : names )
|
|
||||||
{
|
|
||||||
String value = tags.get( name );
|
|
||||||
tagValueEncoder.encodeValue( bwb, name, value );
|
|
||||||
if ( dostats ) bwb.assignBits( "value-index" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,492 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.DenseLongMap;
|
|
||||||
import btools.util.TinyDenseLongMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TileSplitter splits a tile into pieces
|
|
||||||
*/
|
|
||||||
public class TileSplitter extends MapCreatorBase
|
|
||||||
{
|
|
||||||
private NodeData templateNode = new NodeData( 0, 0, 0 );
|
|
||||||
|
|
||||||
private DenseLongMap nodeIndexMap;
|
|
||||||
private DenseLongMap bigWayMemberMap;
|
|
||||||
|
|
||||||
private DenseLongMap wayIndexMap;
|
|
||||||
private DenseLongMap bigRelMemberMap;
|
|
||||||
|
|
||||||
private DenseLongMap relIndexMap;
|
|
||||||
|
|
||||||
private Map<NodeData,NodeData> nodeMap;
|
|
||||||
|
|
||||||
private List<NodeData> thisLevelNodes;
|
|
||||||
private Map<Long,Integer> thisLevelNodesIndexes;
|
|
||||||
|
|
||||||
private List<WayData> thisLevelWays;
|
|
||||||
private Map<Long,Integer> thisLevelWaysIndexes;
|
|
||||||
|
|
||||||
private int level;
|
|
||||||
private int baseLon;
|
|
||||||
private int baseLat;
|
|
||||||
|
|
||||||
private int nodeCount = 0;
|
|
||||||
private String typeSuffix;
|
|
||||||
private boolean inPassLoop;
|
|
||||||
private int pass; // 1 == build tileIndexMap, 2 == collect this-level-nodes, 3 == output nodes
|
|
||||||
private File inTileDir;
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println("*** TileSplitter: cut tiles into 16 pieces");
|
|
||||||
if (args.length != 1)
|
|
||||||
{
|
|
||||||
System.out.println("usage: java TileSplitter <tile-dir>" );
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new TileSplitter().process( new File( args[0] ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process( File tileDir) throws Exception
|
|
||||||
{
|
|
||||||
for( int level = 0; level < 12; level += 2 )
|
|
||||||
{
|
|
||||||
process( tileDir, level );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process( File tileDir, int level ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println("processing level: " + level );
|
|
||||||
|
|
||||||
inTileDir = new File( tileDir, "" + (level) );
|
|
||||||
outTileDir = new File( tileDir, "" + (level+2) );
|
|
||||||
outTileDir.mkdirs();
|
|
||||||
this.level = level;
|
|
||||||
|
|
||||||
// *** initialize 3-pass processing of nodes, ways and relations
|
|
||||||
inPassLoop = false;
|
|
||||||
new NodeIterator( this ).processDir( inTileDir, ".ntl" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeFileStart( File nodeFile ) throws Exception
|
|
||||||
{
|
|
||||||
if ( !inPassLoop )
|
|
||||||
{
|
|
||||||
inPassLoop = true;
|
|
||||||
pass = 1;
|
|
||||||
new NodeIterator( this ).processFile( nodeFile );
|
|
||||||
pass = 2;
|
|
||||||
new NodeIterator( this ).processFile( nodeFile );
|
|
||||||
pass = 3;
|
|
||||||
new NodeIterator( this ).processFile( nodeFile );
|
|
||||||
pass = 4;
|
|
||||||
inPassLoop = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println( "nodeFileStart pass=" + pass );
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
getBaseTileFromName( nodeFile.getName() );
|
|
||||||
nodeIndexMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap();
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if ( pass == 3 )
|
|
||||||
{
|
|
||||||
nodeMap = new HashMap<NodeData,NodeData>();
|
|
||||||
thisLevelNodes = new ArrayList<NodeData>();
|
|
||||||
}
|
|
||||||
else // nodePass = 4
|
|
||||||
{
|
|
||||||
NodeData.sortByGeoId( thisLevelNodes );
|
|
||||||
|
|
||||||
thisLevelNodesIndexes = new HashMap<Long,Integer>();
|
|
||||||
int idx = 0;
|
|
||||||
for( NodeData n : thisLevelNodes )
|
|
||||||
{
|
|
||||||
thisLevelNodesIndexes.put( Long.valueOf( n.nid ), Integer.valueOf( idx++ ) );
|
|
||||||
}
|
|
||||||
thisLevelNodes = null;
|
|
||||||
}
|
|
||||||
typeSuffix = "ntl";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void getBaseTileFromName( String name )
|
|
||||||
{
|
|
||||||
System.out.println( "getBaseTileFromName: " + name );
|
|
||||||
int idx1 = name.indexOf( '_' );
|
|
||||||
int idx2 = name.indexOf( '.' );
|
|
||||||
baseLon = Integer.parseInt( name.substring( 0, idx1 ) );
|
|
||||||
baseLat = Integer.parseInt( name.substring( idx1+1, idx2 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextNode( NodeData n ) throws Exception
|
|
||||||
{
|
|
||||||
int tidx = getTileIndex( n );
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
nodeCount++;
|
|
||||||
nodeIndexMap.put( n.nid, tidx );
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boolean usedHere = bigWayMemberMap.getInt( n.nid ) == 0;
|
|
||||||
|
|
||||||
if ( usedHere ) // if used on this level...
|
|
||||||
{
|
|
||||||
// if no level yet, this is it
|
|
||||||
if ( n.zoom == -1 )
|
|
||||||
{
|
|
||||||
n.zoom = level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
if ( n.zoom != -1 )
|
|
||||||
{
|
|
||||||
n.calcGeoId();
|
|
||||||
nodeMap.put( n,n );
|
|
||||||
if ( n.zoom == level )
|
|
||||||
{
|
|
||||||
thisLevelNodes.add( n );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // pass == 4
|
|
||||||
{
|
|
||||||
// add the index
|
|
||||||
if ( n.zoom == level )
|
|
||||||
{
|
|
||||||
n.nativeIndex = thisLevelNodesIndexes.get( Long.valueOf( n.nid ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( usedHere )
|
|
||||||
{
|
|
||||||
n.writeTo( getOutStreamForTile( 16 ) );
|
|
||||||
}
|
|
||||||
n.writeTo( getOutStreamForTile( tidx ) ); // write to subtile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "nodeFileEnd pass=" + pass );
|
|
||||||
|
|
||||||
closeTileOutStreams();
|
|
||||||
File parentNodes = new File( outTileDir, getNameForTile( 16 ) );
|
|
||||||
|
|
||||||
// read corresponding way-file
|
|
||||||
if ( pass == 2 )
|
|
||||||
{
|
|
||||||
bigWayMemberMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap();
|
|
||||||
}
|
|
||||||
String name = nodeFile.getName();
|
|
||||||
String wayfilename = name.substring( 0, name.length()-3 ) + "wtl";
|
|
||||||
File wayfile = new File( inTileDir, wayfilename );
|
|
||||||
if ( wayfile.exists() )
|
|
||||||
{
|
|
||||||
new WayIterator( this ).processFile( wayfile );
|
|
||||||
}
|
|
||||||
|
|
||||||
// read corresponding relation-file
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
bigRelMemberMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap();
|
|
||||||
}
|
|
||||||
String relfilename = name.substring( 0, name.length()-3 ) + "rtl";
|
|
||||||
File relfile = new File( inTileDir, relfilename );
|
|
||||||
if ( relfile.exists() )
|
|
||||||
{
|
|
||||||
new RelationIterator( this ).processFile( relfile );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 4 )
|
|
||||||
{
|
|
||||||
nodeFile.delete();
|
|
||||||
if ( parentNodes.exists() )
|
|
||||||
{
|
|
||||||
parentNodes.renameTo( nodeFile );
|
|
||||||
}
|
|
||||||
else if ( nodeCount > 0 )
|
|
||||||
{
|
|
||||||
nodeFile.createNewFile(); // create even empty to signal existence of childs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void wayFileStart( File wayFile ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "wayFileStart pass=" + pass );
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
wayIndexMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap();
|
|
||||||
}
|
|
||||||
else if ( pass == 3 )
|
|
||||||
{
|
|
||||||
thisLevelWays = new ArrayList<WayData>();
|
|
||||||
}
|
|
||||||
else if ( pass == 4 )
|
|
||||||
{
|
|
||||||
WayData.sortByStartNode( thisLevelWays );
|
|
||||||
|
|
||||||
thisLevelWaysIndexes = new HashMap<Long,Integer>();
|
|
||||||
int idx = 0;
|
|
||||||
for( WayData w : thisLevelWays )
|
|
||||||
{
|
|
||||||
thisLevelWaysIndexes.put( Long.valueOf( w.wid ), Integer.valueOf( idx++ ) );
|
|
||||||
}
|
|
||||||
thisLevelWays = null;
|
|
||||||
}
|
|
||||||
typeSuffix = "wtl";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextWay( WayData w ) throws Exception
|
|
||||||
{
|
|
||||||
int widx = getTileIndex( w );
|
|
||||||
if ( widx == -1 )
|
|
||||||
{
|
|
||||||
System.out.println( "************ invalid way: " + w.wid );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
wayIndexMap.put( w.wid, widx );
|
|
||||||
}
|
|
||||||
else // pass >= 2
|
|
||||||
{
|
|
||||||
boolean usedHere = bigRelMemberMap.getInt( w.wid ) == 0;
|
|
||||||
if ( usedHere || widx == 16 )
|
|
||||||
{
|
|
||||||
// if no level yet, this is it
|
|
||||||
if ( w.zoom == -1 )
|
|
||||||
{
|
|
||||||
w.zoom = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 2 )
|
|
||||||
{
|
|
||||||
int nnodes = w.nodes.size();
|
|
||||||
for (int i=0; i<nnodes; i++ )
|
|
||||||
{
|
|
||||||
bigWayMemberMap.put( w.nodes.get(i), 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
if ( w.zoom == level )
|
|
||||||
{
|
|
||||||
w.startNodeIdx = getLocaleIndexForNid( w.nodes.get(0) );
|
|
||||||
thisLevelWays.add( w );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( pass == 4 )
|
|
||||||
{
|
|
||||||
if ( w.zoom == level )
|
|
||||||
{
|
|
||||||
w.nativeIndex = thisLevelWaysIndexes.get( Long.valueOf( w.wid ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( usedHere && widx != 16 )
|
|
||||||
{
|
|
||||||
w.writeTo( getOutStreamForTile( 16 ) );
|
|
||||||
}
|
|
||||||
w.writeTo( getOutStreamForTile( widx ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void wayFileEnd( File wayFile ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "wayFileEnd pass=" + pass );
|
|
||||||
|
|
||||||
closeTileOutStreams();
|
|
||||||
|
|
||||||
if ( pass == 4 )
|
|
||||||
{
|
|
||||||
wayFile.delete();
|
|
||||||
File parentWays = new File( outTileDir, getNameForTile( 16 ) );
|
|
||||||
if ( parentWays.exists() )
|
|
||||||
{
|
|
||||||
parentWays.renameTo( wayFile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void relationFileStart( File relFile ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "relFileStart pass=" + pass );
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
relIndexMap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap() : new TinyDenseLongMap();
|
|
||||||
}
|
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else // nodePass = 3
|
|
||||||
{
|
|
||||||
}
|
|
||||||
typeSuffix = "rtl";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextRelation( RelationData r ) throws Exception
|
|
||||||
{
|
|
||||||
int ridx = getTileIndex( r );
|
|
||||||
if ( ridx == -1 )
|
|
||||||
{
|
|
||||||
System.out.println( "************ invalid relation: " + r.rid );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
relIndexMap.put( r.rid, ridx );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 1 && ridx == 16 )
|
|
||||||
{
|
|
||||||
int nways = r.ways.size();
|
|
||||||
for (int i=0; i<nways; i++ )
|
|
||||||
{
|
|
||||||
bigRelMemberMap.put( r.ways.get(i), 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pass == 4 )
|
|
||||||
{
|
|
||||||
r.writeTo( getOutStreamForTile( ridx ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void relationFileEnd( File relFile ) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "relFileEnd pass=" + pass );
|
|
||||||
|
|
||||||
closeTileOutStreams();
|
|
||||||
|
|
||||||
if ( pass == 4 )
|
|
||||||
{
|
|
||||||
relFile.delete();
|
|
||||||
File parentRels = new File( outTileDir, getNameForTile( 16 ) );
|
|
||||||
if ( parentRels.exists() )
|
|
||||||
{
|
|
||||||
parentRels.renameTo( relFile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getLocaleIndexForNid( long nid )
|
|
||||||
{
|
|
||||||
templateNode.nid = nid;
|
|
||||||
NodeData n = nodeMap.get( templateNode );
|
|
||||||
if ( n == null ) throw new IllegalArgumentException( "nid=" + nid + " not found" );
|
|
||||||
n.used = true;
|
|
||||||
return n.localeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getTileIndex( NodeData n )
|
|
||||||
{
|
|
||||||
int idxLon = ( n.ilon >> ( 26 - level ) ) & 3;
|
|
||||||
int idxLat = ( n.ilat >> ( 26 - level ) ) & 3;
|
|
||||||
return 4 * idxLon + idxLat;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getTileIndex( WayData w )
|
|
||||||
{
|
|
||||||
int nnodes = w.nodes.size();
|
|
||||||
|
|
||||||
int wayTileIndex = 16;
|
|
||||||
|
|
||||||
// determine the tile-index for each node
|
|
||||||
for (int i=0; i<nnodes; i++ )
|
|
||||||
{
|
|
||||||
int tileIndex = nodeIndexMap.getInt( w.nodes.get(i) );
|
|
||||||
if ( tileIndex == -1 )
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ( wayTileIndex == 16 )
|
|
||||||
{
|
|
||||||
wayTileIndex = tileIndex;
|
|
||||||
}
|
|
||||||
else if ( tileIndex != wayTileIndex )
|
|
||||||
{
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wayTileIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getTileIndex( RelationData r )
|
|
||||||
{
|
|
||||||
int nways = r.ways.size();
|
|
||||||
|
|
||||||
int relTileIndex = 16;
|
|
||||||
boolean hasAny = false;
|
|
||||||
|
|
||||||
// determine the tile-index for each way
|
|
||||||
for (int i=0; i<nways; i++ )
|
|
||||||
{
|
|
||||||
int tileIndex = wayIndexMap.getInt( r.ways.get(i) );
|
|
||||||
if ( tileIndex == -1 )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
hasAny = true;
|
|
||||||
if ( relTileIndex == 16 )
|
|
||||||
{
|
|
||||||
relTileIndex = tileIndex;
|
|
||||||
}
|
|
||||||
else if ( tileIndex != relTileIndex )
|
|
||||||
{
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasAny ? relTileIndex : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getNameForTile( int tileIndex )
|
|
||||||
{
|
|
||||||
if ( tileIndex == 16 )
|
|
||||||
{
|
|
||||||
return "parent." + typeSuffix;
|
|
||||||
}
|
|
||||||
int idxLon = baseLon * 4 + (tileIndex >> 2);
|
|
||||||
int idxLat = baseLat * 4 + (tileIndex & 3);
|
|
||||||
return idxLon + "_" + idxLat + "." + typeSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.LongList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for waydata on the preprocessor level
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class WayData extends MapCreatorBase
|
|
||||||
{
|
|
||||||
public long wid;
|
|
||||||
public LongList nodes;
|
|
||||||
|
|
||||||
public int startNodeIdx;
|
|
||||||
|
|
||||||
private int minx;
|
|
||||||
private int miny;
|
|
||||||
private int maxx;
|
|
||||||
private int maxy;
|
|
||||||
|
|
||||||
public int zoom = -1; // the zoom level this node is on
|
|
||||||
public int nativeIndex; // the index along all NATIVE ways of it's tile
|
|
||||||
|
|
||||||
public void calcBBox( List<NodeData> nodeList )
|
|
||||||
{
|
|
||||||
int nn = nodes.size();
|
|
||||||
for( int i=0; i<nn; i++ )
|
|
||||||
{
|
|
||||||
NodeData n = nodeList.get((int)nodes.get(i));
|
|
||||||
if ( i == 0 )
|
|
||||||
{
|
|
||||||
minx = maxx = n.ilon;
|
|
||||||
miny = maxy = n.ilat;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( n.ilon < minx ) minx = n.ilon;
|
|
||||||
if ( n.ilon > maxx ) maxx = n.ilon;
|
|
||||||
if ( n.ilat < miny ) miny = n.ilat;
|
|
||||||
if ( n.ilat > maxy ) maxy = n.ilat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inBBox( int z, int x, int y )
|
|
||||||
{
|
|
||||||
int shift = 28-z;
|
|
||||||
int x0 = x << shift;
|
|
||||||
int x1 = (x+1) << shift;
|
|
||||||
int y0 = y << shift;
|
|
||||||
int y1 = (y+1) << shift;
|
|
||||||
boolean outofbox = x1 < minx || x0 >= maxx || y1 < miny || y0 >= maxy;
|
|
||||||
return !outofbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
zoom = di.readInt();
|
|
||||||
nativeIndex = di.readInt();
|
|
||||||
nodes = new LongList( 16 );
|
|
||||||
wid = readId( di) ;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
String key = di.readUTF();
|
|
||||||
if ( key.length() == 0 ) break;
|
|
||||||
String value = di.readUTF();
|
|
||||||
putTag( key, value );
|
|
||||||
}
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
long nid = readId( di );
|
|
||||||
if ( nid == -1 ) break;
|
|
||||||
nodes.add( nid );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo( DataOutputStream dos ) throws Exception
|
|
||||||
{
|
|
||||||
dos.writeInt( zoom );
|
|
||||||
dos.writeInt( nativeIndex );
|
|
||||||
writeId( dos, wid );
|
|
||||||
if ( getTagsOrNull() != null )
|
|
||||||
{
|
|
||||||
for( Map.Entry<String,String> me : getTagsOrNull().entrySet() )
|
|
||||||
{
|
|
||||||
if ( me.getKey().length() > 0 )
|
|
||||||
{
|
|
||||||
dos.writeUTF( me.getKey() );
|
|
||||||
dos.writeUTF( me.getValue() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dos.writeUTF( "" );
|
|
||||||
|
|
||||||
int size = nodes.size();
|
|
||||||
for( int i=0; i < size; i++ )
|
|
||||||
{
|
|
||||||
writeId( dos, nodes.get( i ) );
|
|
||||||
}
|
|
||||||
writeId( dos, -1 ); // stopbyte
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sortByStartNode( List<WayData> ways )
|
|
||||||
{
|
|
||||||
Collections.sort( ways, new Comparator<WayData>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public int compare(WayData w1, WayData w2)
|
|
||||||
{
|
|
||||||
long d = w1.startNodeIdx - w2.startNodeIdx;
|
|
||||||
|
|
||||||
// for equal start indexes sort by wid
|
|
||||||
if ( d == 0L )
|
|
||||||
{
|
|
||||||
d = w1.wid - w2.wid;
|
|
||||||
}
|
|
||||||
return d == 0 ? 0 : ( d < 0 ? -1 : 1 );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals( Object o )
|
|
||||||
{
|
|
||||||
if ( o instanceof WayData )
|
|
||||||
{
|
|
||||||
WayData w = (WayData) o;
|
|
||||||
return w.wid == wid;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return (int)((wid >> 32) ^ wid);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public WayIterator( WayListener wayListener )
|
|
||||||
{
|
|
||||||
listener = wayListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processFile(File wayfile) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "*** WayIterator reading: " + wayfile );
|
|
||||||
|
|
||||||
listener.wayFileStart( wayfile );
|
|
||||||
|
|
||||||
DataInputStream di = new DataInputStream( new BufferedInputStream ( new FileInputStream( wayfile ) ) );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
WayData w = new WayData( di );
|
|
||||||
listener.nextWay( w );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( EOFException eof )
|
|
||||||
{
|
|
||||||
di.close();
|
|
||||||
}
|
|
||||||
listener.wayFileEnd( wayfile );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callbacklistener for WayIterator
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public interface WayListener
|
|
||||||
{
|
|
||||||
void wayFileStart( File wayfile ) throws Exception;
|
|
||||||
|
|
||||||
void nextWay( WayData data ) throws Exception;
|
|
||||||
|
|
||||||
void wayFileEnd( File wayfile ) throws Exception;
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import btools.mapdecoder.BitReadBuffer;
|
|
||||||
import btools.mapdecoder.CharDecoder;
|
|
||||||
|
|
||||||
public class BitCodingTest
|
|
||||||
{
|
|
||||||
// @Test
|
|
||||||
public void charEncodeDecodeTest()
|
|
||||||
{
|
|
||||||
byte[] ab = new byte[4000];
|
|
||||||
BitWriteBuffer bwb = new BitWriteBuffer( ab );
|
|
||||||
CharEncoder ce = new CharEncoder();
|
|
||||||
|
|
||||||
for( int pass=1; pass<=3; pass++ )
|
|
||||||
{
|
|
||||||
ce.encodeDictionary( bwb );
|
|
||||||
for ( char c = 'a'; c <= 'z'; c++ )
|
|
||||||
{
|
|
||||||
ce.encode( Character.valueOf( c ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BitReadBuffer brb = new BitReadBuffer( ab );
|
|
||||||
CharDecoder cd = new CharDecoder( brb );
|
|
||||||
for ( char c = 'a'; c <= 'z'; c++ )
|
|
||||||
{
|
|
||||||
Character c1 = cd.decode();
|
|
||||||
Assert.assertTrue( "char mismatch c=" + c + "c1=" + c1, c == c1.charValue() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void varBitsEncodeDecodeTest()
|
|
||||||
{
|
|
||||||
byte[] ab = new byte[4000];
|
|
||||||
BitWriteBuffer bwb = new BitWriteBuffer( ab );
|
|
||||||
for ( int i = 0; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
bwb.encodeInt( i );
|
|
||||||
bwb.encodeLong( i );
|
|
||||||
}
|
|
||||||
BitReadBuffer brb = new BitReadBuffer( ab );
|
|
||||||
|
|
||||||
for ( int i = 0; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
int value = brb.decodeInt();
|
|
||||||
Assert.assertTrue( "int value mismatch i=" + i + "v=" + value, value == i );
|
|
||||||
long lvalue = brb.decodeLong();
|
|
||||||
Assert.assertTrue( "long value mismatch i=" + i + "v=" + lvalue, value == i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void boundedEncodeDecodeTest()
|
|
||||||
{
|
|
||||||
byte[] ab = new byte[581969];
|
|
||||||
BitWriteBuffer bwb = new BitWriteBuffer( ab );
|
|
||||||
for ( int max = 1; max < 1000; max++ )
|
|
||||||
{
|
|
||||||
for ( int val = 0; val <= max; val++ )
|
|
||||||
{
|
|
||||||
bwb.encodeBounded( max, val );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BitReadBuffer brb = new BitReadBuffer( ab );
|
|
||||||
|
|
||||||
for ( int max = 1; max < 1000; max++ )
|
|
||||||
{
|
|
||||||
for ( int val = 0; val <= max; val++ )
|
|
||||||
{
|
|
||||||
long valDecoded = brb.decodeBounded( max );
|
|
||||||
if ( valDecoded != val )
|
|
||||||
{
|
|
||||||
Assert.fail( "mismatch at max=" + max + " " + valDecoded + "<>" + val );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sortedLongArrayEncodeDecodeTest()
|
|
||||||
{
|
|
||||||
Random rand = new Random(1234);
|
|
||||||
int size = 20;
|
|
||||||
long[] values = new long[size];
|
|
||||||
for ( int i = 0; i < size; i++ )
|
|
||||||
{
|
|
||||||
values[i] = rand.nextInt() & 0x0fffffff;
|
|
||||||
}
|
|
||||||
values[5] = 175384; // force collision
|
|
||||||
values[8] = 175384;
|
|
||||||
|
|
||||||
values[15] = 275384; // force neighbours
|
|
||||||
values[18] = 275385;
|
|
||||||
|
|
||||||
encodeDecodeArray( "Test1", values );
|
|
||||||
|
|
||||||
values = new long[1];
|
|
||||||
values[0] = 0x134567890123456L;
|
|
||||||
encodeDecodeArray( "Test2", values );
|
|
||||||
|
|
||||||
values = new long[0];
|
|
||||||
encodeDecodeArray( "Test3", values );
|
|
||||||
|
|
||||||
values = new long[100000];
|
|
||||||
for ( int i = 0; i < values.length; i++ )
|
|
||||||
{
|
|
||||||
values[i] = (((long)rand.nextInt())&0xffffffffL) << rand.nextInt(26); // 32 + 25 bits
|
|
||||||
}
|
|
||||||
encodeDecodeArray( "Test4", values );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encodeDecodeArray( String testName, long[] values )
|
|
||||||
{
|
|
||||||
Arrays.sort( values );
|
|
||||||
|
|
||||||
byte[] ab = new byte[3000000];
|
|
||||||
BitWriteBuffer bwb = new BitWriteBuffer( ab );
|
|
||||||
|
|
||||||
bwb.encodeSortedArray( values );
|
|
||||||
|
|
||||||
long[] decodedValues = new BitReadBuffer( ab ).decodeSortedArray();
|
|
||||||
|
|
||||||
for ( int i = 0; i < values.length; i++ )
|
|
||||||
{
|
|
||||||
if ( values[i] != decodedValues[i] )
|
|
||||||
{
|
|
||||||
Assert.fail( "mismatch at " + testName + " i=" + i + " " + values[i] + "<>" + decodedValues[i] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package btools.mapsplitter;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import btools.mapdecoder.TileDecoder;
|
|
||||||
import btools.mapdecoder.OsmTile;
|
|
||||||
|
|
||||||
public class MapsplitterTest
|
|
||||||
{
|
|
||||||
@Test
|
|
||||||
public void mapsplitterTest() 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 tmpdir = new File( workingDir, "tmp2" );
|
|
||||||
tmpdir.mkdir();
|
|
||||||
|
|
||||||
// run OsmSplitter
|
|
||||||
File tiles = new File( tmpdir, "tiles" );
|
|
||||||
tiles.mkdir();
|
|
||||||
new OsmSplitter().process( tiles, mapfile );
|
|
||||||
|
|
||||||
// run TileSplitter to split up to level 12
|
|
||||||
new TileSplitter().process( tiles );
|
|
||||||
|
|
||||||
new TileEncoder().process( new File( tiles, "0/0_0.ntl" ) );
|
|
||||||
new TileDecoder().process( tiles, null, 12, 2147, 1389 );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter</artifactId>
|
|
||||||
<version>1.4.11</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<artifactId>brouter-mem-router</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter-util</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter-expressions</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter-mapaccess</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter-core</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.btools</groupId>
|
|
||||||
<artifactId>brouter-map-creator</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
|
@ -1,317 +0,0 @@
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.expressions.BExpressionContextWay;
|
|
||||||
import btools.expressions.BExpressionMetaData;
|
|
||||||
import btools.mapaccess.OsmPos;
|
|
||||||
import btools.mapcreator.MapCreatorBase;
|
|
||||||
import btools.mapcreator.NodeData;
|
|
||||||
import btools.mapcreator.NodeIterator;
|
|
||||||
import btools.mapcreator.WayData;
|
|
||||||
import btools.mapcreator.WayIterator;
|
|
||||||
import btools.util.ByteArrayUnifier;
|
|
||||||
import btools.util.CompactLongMap;
|
|
||||||
import btools.util.FrozenLongMap;
|
|
||||||
import btools.util.LazyArrayOfLists;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GraphLoader loads the routing graph from
|
|
||||||
* the nodes+way files (much like mapcreator.WayLinker)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
public class GraphLoader extends MapCreatorBase
|
|
||||||
{
|
|
||||||
private CompactLongMap<OsmNodeP> nodesMap;
|
|
||||||
|
|
||||||
private Map<String, StationNode> stationMap;
|
|
||||||
|
|
||||||
private BExpressionContextWay expctxWay;
|
|
||||||
|
|
||||||
private ByteArrayUnifier abUnifier;
|
|
||||||
|
|
||||||
private int currentTile;
|
|
||||||
|
|
||||||
private long linksLoaded = 0L;
|
|
||||||
private long nodesLoaded = 0L;
|
|
||||||
|
|
||||||
private static final int MAXTILES = 2592;
|
|
||||||
private List<LazyArrayOfLists<OsmNodeP>> seglistsArray = new ArrayList<LazyArrayOfLists<OsmNodeP>>(2592);
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
|
||||||
{
|
|
||||||
System.out.println("*** GraphLoader: load a routing graph in memory");
|
|
||||||
if (args.length != 5)
|
|
||||||
{
|
|
||||||
System.out.println("usage: java GraphLoader <node-tiles-in> <way-tiles-in> <lookup-file> <profile-file> <fahtplan-file>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BExpressionMetaData meta = new BExpressionMetaData();
|
|
||||||
|
|
||||||
// read lookup + profile for lookup-version + access-filter
|
|
||||||
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
|
|
||||||
File lookupFile = new File( args[2] );
|
|
||||||
File profileFile = new File( args[3] );
|
|
||||||
meta.readMetaData( lookupFile );
|
|
||||||
expctxWay.parseFile( profileFile, "global" );
|
|
||||||
|
|
||||||
GraphLoader graph = new GraphLoader();
|
|
||||||
File[] fahrplanFiles = new File[2];
|
|
||||||
fahrplanFiles[0] = new File( args[4] );
|
|
||||||
fahrplanFiles[1] = new File( args[5] );
|
|
||||||
graph.process( new File( args[0] ), new File( args[1] ), fahrplanFiles, expctxWay );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void process( File nodeTilesIn, File wayTilesIn, File[] fahrplanFiles, BExpressionContextWay expctxWay ) throws Exception
|
|
||||||
{
|
|
||||||
this.expctxWay = expctxWay;
|
|
||||||
|
|
||||||
seglistsArray = new ArrayList<LazyArrayOfLists<OsmNodeP>>(MAXTILES);
|
|
||||||
for( int i=0; i < MAXTILES; i++ )
|
|
||||||
{
|
|
||||||
seglistsArray.add( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
abUnifier = new ByteArrayUnifier( 16384, false );
|
|
||||||
|
|
||||||
nodesMap = new CompactLongMap<OsmNodeP>();
|
|
||||||
|
|
||||||
// read all nodes
|
|
||||||
new NodeIterator( this, false ).processDir( nodeTilesIn, ".u5d" );
|
|
||||||
|
|
||||||
// freeze the nodes-map
|
|
||||||
nodesMap = new FrozenLongMap<OsmNodeP>( nodesMap );
|
|
||||||
|
|
||||||
// trim the list array
|
|
||||||
for( int i=0; i<MAXTILES; i++ )
|
|
||||||
{
|
|
||||||
if ( seglistsArray.get(i) != null )
|
|
||||||
{
|
|
||||||
seglistsArray.get(i).trimAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// then read the ways
|
|
||||||
new WayIterator( this, false ).processDir( wayTilesIn, ".wt5" );
|
|
||||||
|
|
||||||
nodesMap = null; // don't need that anymore
|
|
||||||
|
|
||||||
System.out.println( "nodesLoaded=" + nodesLoaded + " linksLoaded=" + linksLoaded );
|
|
||||||
|
|
||||||
// now load the train-schedules
|
|
||||||
stationMap = ScheduleParser.parseTrainTable( fahrplanFiles, this, expctxWay );
|
|
||||||
|
|
||||||
System.gc();
|
|
||||||
long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
|
||||||
System.out.println( "memory after graph loading: " + mem / 1024 / 1024 + " MB" );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public OsmNodeP matchNodeForPosition( OsmPos pos, BExpressionContextWay wayCtx, boolean transitonly )
|
|
||||||
{
|
|
||||||
if ( transitonly )
|
|
||||||
{
|
|
||||||
return matchStationForPosition( pos );
|
|
||||||
}
|
|
||||||
|
|
||||||
int ilon = pos.getILon();
|
|
||||||
int ilat = pos.getILat();
|
|
||||||
|
|
||||||
List<OsmNodeP> nodes = new ArrayList<OsmNodeP>();
|
|
||||||
nodes.addAll( subListForPos( ilon-6125, ilat-6125 ) );
|
|
||||||
nodes.addAll( subListForPos( ilon-6125, ilat+6125 ) );
|
|
||||||
nodes.addAll( subListForPos( ilon+6125, ilat-6125 ) );
|
|
||||||
nodes.addAll( subListForPos( ilon+6125, ilat+6125 ) );
|
|
||||||
|
|
||||||
int mindist = Integer.MAX_VALUE;
|
|
||||||
OsmNodeP bestmatch = null;
|
|
||||||
|
|
||||||
for( OsmNodeP node : nodes )
|
|
||||||
{
|
|
||||||
if ( transitonly )
|
|
||||||
{
|
|
||||||
StationNode sn = getStationNode( node );
|
|
||||||
if ( sn != null )
|
|
||||||
{
|
|
||||||
int dist = pos.calcDistance( sn );
|
|
||||||
if ( dist < mindist )
|
|
||||||
{
|
|
||||||
mindist = dist;
|
|
||||||
bestmatch = sn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int dist = pos.calcDistance( node );
|
|
||||||
if ( dist < mindist )
|
|
||||||
{
|
|
||||||
if ( wayCtx == null || hasRoutableLinks(node, wayCtx) )
|
|
||||||
{
|
|
||||||
mindist = dist;
|
|
||||||
bestmatch = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bestmatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private StationNode getStationNode( OsmNodeP node )
|
|
||||||
{
|
|
||||||
for( OsmLinkP link = node.getFirstLink(); link != null; link = link.getNext( node ) )
|
|
||||||
{
|
|
||||||
OsmNodeP tn = link.getTarget( node );
|
|
||||||
if ( tn instanceof StationNode )
|
|
||||||
{
|
|
||||||
return (StationNode)tn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmNodeP matchStationForPosition( OsmPos pos )
|
|
||||||
{
|
|
||||||
int mindist = Integer.MAX_VALUE;
|
|
||||||
OsmNodeP bestmatch = null;
|
|
||||||
|
|
||||||
for( OsmNodeP node : stationMap.values() )
|
|
||||||
{
|
|
||||||
int dist = pos.calcDistance( node );
|
|
||||||
if ( dist < mindist )
|
|
||||||
{
|
|
||||||
mindist = dist;
|
|
||||||
bestmatch = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bestmatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasRoutableLinks( OsmNodeP node, BExpressionContextWay wayCtx )
|
|
||||||
{
|
|
||||||
for( OsmLinkP link = node.getFirstLink(); link != null; link = link.getNext( node ) )
|
|
||||||
{
|
|
||||||
if ( link.isWayLink() )
|
|
||||||
{
|
|
||||||
wayCtx.evaluate( false, link.descriptionBitmap );
|
|
||||||
if ( wayCtx.getCostfactor() < 10000.f )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeFileStart( File nodefile ) throws Exception
|
|
||||||
{
|
|
||||||
currentTile = tileForFilename( nodefile.getName() );
|
|
||||||
seglistsArray.set(currentTile, new LazyArrayOfLists<OsmNodeP>(160000) );
|
|
||||||
System.out.println( "nodes currentTile=" + currentTile );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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;
|
|
||||||
|
|
||||||
// add to the map
|
|
||||||
nodesMap.fastPut( data.nid, n );
|
|
||||||
|
|
||||||
// add also to the list array
|
|
||||||
subListForPos( n.ilon, n.ilat ).add( n );
|
|
||||||
|
|
||||||
nodesLoaded++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean wayFileStart( File wayfile ) throws Exception
|
|
||||||
{
|
|
||||||
currentTile = tileForFilename( wayfile.getName() );
|
|
||||||
System.out.println( "ways currentTile=" + currentTile );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nextWay( WayData way ) throws Exception
|
|
||||||
{
|
|
||||||
byte[] description = abUnifier.unify( way.description );
|
|
||||||
|
|
||||||
byte wayBits = 0;
|
|
||||||
expctxWay.decode( description );
|
|
||||||
if ( !expctxWay.getBooleanLookupValue( "bridge" ) ) wayBits |= OsmNodeP.NO_BRIDGE_BIT;
|
|
||||||
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);
|
|
||||||
n1 = n2;
|
|
||||||
n2 = nodesMap.get( nid );
|
|
||||||
if ( n1 != null && n2 != null && n1 != n2 )
|
|
||||||
{
|
|
||||||
if ( tileForPos( n1.ilon, n1.ilat ) == currentTile )
|
|
||||||
{
|
|
||||||
OsmLinkP link = n2.createLink(n1);
|
|
||||||
link.descriptionBitmap = description;
|
|
||||||
linksLoaded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( n2 != null )
|
|
||||||
{
|
|
||||||
n2.wayBits |= wayBits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// from BInstallerView.java
|
|
||||||
private int tileForFilename( String filename )
|
|
||||||
{
|
|
||||||
String basename = filename.substring( 0, filename.length() - 4 );
|
|
||||||
String uname = basename.toUpperCase();
|
|
||||||
int idx = uname.indexOf( "_" );
|
|
||||||
if ( idx < 0 ) return -1;
|
|
||||||
String slon = uname.substring( 0, idx );
|
|
||||||
String slat = uname.substring( idx+1 );
|
|
||||||
int ilon = slon.charAt(0) == 'W' ? -Integer.valueOf( slon.substring(1) ) :
|
|
||||||
( slon.charAt(0) == 'E' ? Integer.valueOf( slon.substring(1) ) : -1 );
|
|
||||||
int ilat = slat.charAt(0) == 'S' ? -Integer.valueOf( slat.substring(1) ) :
|
|
||||||
( slat.charAt(0) == 'N' ? Integer.valueOf( slat.substring(1) ) : -1 );
|
|
||||||
if ( ilon < -180 || ilon >= 180 || ilon % 5 != 0 ) return -1;
|
|
||||||
if ( ilat < - 90 || ilat >= 90 || ilat % 5 != 0 ) return -1;
|
|
||||||
return (ilon+180) / 5 + 72*((ilat+90)/5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int tileForPos( int ilon, int ilat )
|
|
||||||
{
|
|
||||||
return ilon / 5000000 + 72 * ( ilat / 5000000 );
|
|
||||||
}
|
|
||||||
|
|
||||||
private int subIdxForPos( int ilon, int ilat )
|
|
||||||
{
|
|
||||||
int lonModulo = ilon % 5000000;
|
|
||||||
int latModulo = ilat % 5000000;
|
|
||||||
return ( lonModulo / 12500 ) + 400 * (latModulo / 12500);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<OsmNodeP> subListForPos( int ilon, int ilat )
|
|
||||||
{
|
|
||||||
if ( ilon < 0 || ilon >= 360000000 || ilat < 0 || ilat >= 180000000 )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException( "illegal position: " + ilon + " " + ilat );
|
|
||||||
}
|
|
||||||
int tileNr = tileForPos( ilon, ilat );
|
|
||||||
if ( seglistsArray.get(tileNr) == null ) return new ArrayList<OsmNodeP>();
|
|
||||||
return seglistsArray.get(tileNr).getList( subIdxForPos( ilon, ilat ) );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/**
|
|
||||||
* Set off departure offsets (immutable)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import btools.router.OsmTrack;
|
|
||||||
|
|
||||||
public class Iternity implements Comparable<Iternity>
|
|
||||||
{
|
|
||||||
OsmTrack track;
|
|
||||||
OffsetSet offsets;
|
|
||||||
List<String> details = new ArrayList<String>();
|
|
||||||
List<String> lines = new ArrayList<String>();
|
|
||||||
long departtime;
|
|
||||||
long arrivaltime;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo( Iternity it )
|
|
||||||
{
|
|
||||||
return arrivaltime == it.arrivaltime ? 0 : ( arrivaltime < it.arrivaltime ? -1 : 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendSummary( List<String> sum )
|
|
||||||
{
|
|
||||||
SimpleDateFormat df = new SimpleDateFormat( "dd.MM HH:mm", Locale.GERMAN );
|
|
||||||
sum.add( "depart: " + df.format( new Date( departtime ) )+ " arrive: " + df.format( new Date( arrivaltime ) ) );
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder( "--- " );
|
|
||||||
for( String line: lines )
|
|
||||||
{
|
|
||||||
sb.append( line ).append( ' ' );
|
|
||||||
}
|
|
||||||
sb.append( "--- " );
|
|
||||||
long mins = ( arrivaltime-departtime ) / 60000L;
|
|
||||||
sb.append( mins ).append( "min" );
|
|
||||||
sum.add( sb.toString() );
|
|
||||||
|
|
||||||
int firstOffset = -1;
|
|
||||||
boolean hasLaterTrips = false;
|
|
||||||
sb = new StringBuilder( "(+later trips: " );
|
|
||||||
for ( int offset = 0; offset < offsets.size(); offset++ )
|
|
||||||
{
|
|
||||||
if ( offsets.contains( offset ) )
|
|
||||||
{
|
|
||||||
if ( firstOffset < 0 )
|
|
||||||
{
|
|
||||||
firstOffset = offset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.append( "+" + (offset-firstOffset) + "min " );
|
|
||||||
hasLaterTrips = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( sb.length() > 47 )
|
|
||||||
{
|
|
||||||
sb.setLength( 47 );
|
|
||||||
sb.append( "..." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append( ")" );
|
|
||||||
if ( hasLaterTrips )
|
|
||||||
{
|
|
||||||
sum.add( sb.toString() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
/**
|
|
||||||
* Set off departure offsets (immutable)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
|
|
||||||
public class OffsetSet
|
|
||||||
{
|
|
||||||
private Map<BitSet, OffsetSet> existingSets = new HashMap<BitSet, OffsetSet>();
|
|
||||||
|
|
||||||
private static final int size = 185;
|
|
||||||
|
|
||||||
protected BitSet mask;
|
|
||||||
|
|
||||||
public OffsetSet emptySet()
|
|
||||||
{
|
|
||||||
return new OffsetSet( new BitSet( size ), existingSets );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OffsetSet fullSet()
|
|
||||||
{
|
|
||||||
BitSet allbits = new BitSet( size );
|
|
||||||
allbits.set( 0, size );
|
|
||||||
return new OffsetSet( allbits, new HashMap<BitSet, OffsetSet>() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private OffsetSet( BitSet m, Map<BitSet, OffsetSet> knownSets )
|
|
||||||
{
|
|
||||||
existingSets = knownSets;
|
|
||||||
existingSets.put( m , this );
|
|
||||||
mask = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OffsetSet create( BitSet m )
|
|
||||||
{
|
|
||||||
if ( m.isEmpty() )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if ( m.equals( mask ) )
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
OffsetSet set = existingSets.get( m );
|
|
||||||
if ( set == null )
|
|
||||||
{
|
|
||||||
set = new OffsetSet( m, existingSets );
|
|
||||||
// System.out.println( "created set: " + set + " instancecount=" + existingSets.size() );
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet create( List<Integer> offsets )
|
|
||||||
{
|
|
||||||
BitSet m = new BitSet( size );
|
|
||||||
for ( Integer offset : offsets )
|
|
||||||
{
|
|
||||||
int i = offset.intValue();
|
|
||||||
if ( i >= 0 && i < size )
|
|
||||||
{
|
|
||||||
m.set( i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return create( m );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet filterWithSet( SortedSet<Integer> usedTimes, int minuteArrival )
|
|
||||||
{
|
|
||||||
BitSet fmask = (BitSet)mask.clone();
|
|
||||||
int idx = 0;
|
|
||||||
int maxtime = usedTimes.isEmpty() ? Integer.MAX_VALUE: usedTimes.first().intValue() + size();
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
idx = fmask.nextSetBit( idx );
|
|
||||||
if ( idx < 0 ) break;
|
|
||||||
int i = minuteArrival + idx;
|
|
||||||
if ( i > maxtime || !usedTimes.add( Integer.valueOf(i) ) )
|
|
||||||
{
|
|
||||||
fmask.set( idx, false );
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
return create( fmask );
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size()
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains( int offset )
|
|
||||||
{
|
|
||||||
return mask.get( offset );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet add( int offset )
|
|
||||||
{
|
|
||||||
if ( mask.get( offset ) )
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
BitSet m = (BitSet)mask.clone();
|
|
||||||
m.set( offset );
|
|
||||||
return create( m );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet add( OffsetSet offsets )
|
|
||||||
{
|
|
||||||
BitSet m = (BitSet)mask.clone();
|
|
||||||
m.or( offsets.mask );
|
|
||||||
return create( m );
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear all bits from this set in the argument set
|
|
||||||
public OffsetSet filter( OffsetSet in )
|
|
||||||
{
|
|
||||||
BitSet fmask = (BitSet)in.mask.clone();
|
|
||||||
fmask.andNot( mask );
|
|
||||||
return create( fmask );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet ensureMaxOffset( int max )
|
|
||||||
{
|
|
||||||
if ( max < size )
|
|
||||||
{
|
|
||||||
BitSet fmask = (BitSet)mask.clone();
|
|
||||||
fmask.set( max > 0 ? max : 0, size, false );
|
|
||||||
return create( fmask );
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet filterAndClose( OffsetSetHolder gateHolder, boolean closeGate )
|
|
||||||
{
|
|
||||||
OffsetSet gate = gateHolder.getOffsetSet();
|
|
||||||
BitSet gmask = (BitSet)gate.mask.clone();
|
|
||||||
|
|
||||||
BitSet fmask = (BitSet)mask.clone();
|
|
||||||
|
|
||||||
fmask.andNot( gmask );
|
|
||||||
|
|
||||||
gmask.or( fmask );
|
|
||||||
|
|
||||||
if ( closeGate )
|
|
||||||
{
|
|
||||||
gateHolder.setOffsetSet( create( gmask ) ); // modify the gate
|
|
||||||
}
|
|
||||||
return create( fmask );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet sweepWith( OffsetSet sweeper, int timeDiff )
|
|
||||||
{
|
|
||||||
BitSet sweepmask = sweeper.mask;
|
|
||||||
BitSet fmask = (BitSet)mask.clone();
|
|
||||||
|
|
||||||
if ( timeDiff >= 0 )
|
|
||||||
{
|
|
||||||
int idx = 0;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
idx = sweepmask.nextSetBit( idx ) + 1;
|
|
||||||
if ( idx < 1 ) break;
|
|
||||||
|
|
||||||
int sweepStart = Math.max( 0, idx-timeDiff );
|
|
||||||
fmask.set( sweepStart, idx, false );
|
|
||||||
}
|
|
||||||
int sweepStart = Math.max( 0, size-timeDiff );
|
|
||||||
fmask.set( sweepStart, size, false );
|
|
||||||
}
|
|
||||||
// System.out.println( "sweep: " + mask + " with: " + sweepmask + "=" + fmask );
|
|
||||||
|
|
||||||
return create( fmask );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return mask.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/**
|
|
||||||
* Set off departure offsets (immutable)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
|
|
||||||
public interface OffsetSetHolder
|
|
||||||
{
|
|
||||||
OffsetSet getOffsetSet();
|
|
||||||
void setOffsetSet( OffsetSet offsetSet );
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
/**
|
|
||||||
* Container for link between two Osm nodes (pre-pocessor version)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
public class OsmLinkP implements OffsetSetHolder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 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 static int currentserial = 0; // serial version to invalidate link
|
|
||||||
// occupation
|
|
||||||
private int instanceserial = 0;
|
|
||||||
|
|
||||||
private OffsetSet offsets;
|
|
||||||
|
|
||||||
public boolean isConnection()
|
|
||||||
{
|
|
||||||
return descriptionBitmap == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWayLink()
|
|
||||||
{
|
|
||||||
return descriptionBitmap != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmLinkP( OsmNodeP source, OsmNodeP target )
|
|
||||||
{
|
|
||||||
sourceNode = source;
|
|
||||||
targetNode = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected OsmLinkP()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet getOffsetSet()
|
|
||||||
{
|
|
||||||
return offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOffsetSet( OffsetSet offsets )
|
|
||||||
{
|
|
||||||
this.offsets = offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVirgin()
|
|
||||||
{
|
|
||||||
return instanceserial != currentserial;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet filterAndClose( OffsetSet in, long arrival )
|
|
||||||
{
|
|
||||||
if ( offsets == null || isVirgin() )
|
|
||||||
{
|
|
||||||
initLink();
|
|
||||||
offsets = in;
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
return in.filterAndClose( this, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initLink()
|
|
||||||
{
|
|
||||||
instanceserial = currentserial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
/**
|
|
||||||
* Container for an osm node (pre-pocessor version)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import btools.mapaccess.OsmPos;
|
|
||||||
import btools.util.CheapRulerSingleton;
|
|
||||||
|
|
||||||
public class OsmNodeP extends OsmLinkP implements Comparable<OsmNodeP>, OsmPos
|
|
||||||
{
|
|
||||||
public OsmNodeP( double dlon, double dlat )
|
|
||||||
{
|
|
||||||
ilon = (int)(dlon * 1000000 + 180000000);
|
|
||||||
ilat = (int)(dlat * 1000000 + 90000000);
|
|
||||||
}
|
|
||||||
public OsmNodeP()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The latitude
|
|
||||||
*/
|
|
||||||
public int ilat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The longitude
|
|
||||||
*/
|
|
||||||
public int ilon;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The elevation
|
|
||||||
*/
|
|
||||||
public short selev;
|
|
||||||
|
|
||||||
public final static int NO_BRIDGE_BIT = 1;
|
|
||||||
public final static int NO_TUNNEL_BIT = 2;
|
|
||||||
|
|
||||||
public byte wayBits = 0;
|
|
||||||
|
|
||||||
// interface OsmPos
|
|
||||||
@Override
|
|
||||||
public int getILat()
|
|
||||||
{
|
|
||||||
return ilat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getILon()
|
|
||||||
{
|
|
||||||
return ilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short getSElev()
|
|
||||||
{
|
|
||||||
// if all bridge or all tunnel, elevation=no-data
|
|
||||||
return ( wayBits & NO_BRIDGE_BIT ) == 0 || ( wayBits & NO_TUNNEL_BIT ) == 0 ? Short.MIN_VALUE : selev;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
// inherited instance is available, use this
|
|
||||||
sourceNode = source;
|
|
||||||
targetNode = this;
|
|
||||||
source.addLink( this );
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
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 );
|
|
||||||
previous = link;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmLinkP getFirstLink()
|
|
||||||
{
|
|
||||||
return sourceNode == null && targetNode == null ? previous : this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interface OsmPos
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int calcDistance( OsmPos p )
|
|
||||||
{
|
|
||||||
return (int)(CheapRulerSingleton.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getIdFromPos()
|
|
||||||
{
|
|
||||||
return ((long)ilon)<<32 | ilat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getNodeDecsription()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String toString2()
|
|
||||||
{
|
|
||||||
return (ilon-180000000) + "_" + (ilat-90000000) + "_" + (selev/4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares two OsmNodes for position ordering.
|
|
||||||
*
|
|
||||||
* @return -1,0,1 depending an comparson result
|
|
||||||
*/
|
|
||||||
public int compareTo( OsmNodeP n )
|
|
||||||
{
|
|
||||||
long id1 = getIdFromPos();
|
|
||||||
long id2 = n.getIdFromPos();
|
|
||||||
if ( id1 < id2 ) return -1;
|
|
||||||
if ( id1 > id2 ) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OffsetSet filterAndCloseNode( OffsetSet in, boolean modifyGate )
|
|
||||||
{
|
|
||||||
return in; // do nothing (StationNode overrides)
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName()
|
|
||||||
{
|
|
||||||
return "<waynode>";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/**
|
|
||||||
* Container for an osm node with tags (pre-pocessor version)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
|
|
||||||
public class OsmNodePT extends OsmNodeP
|
|
||||||
{
|
|
||||||
public byte[] descriptionBits;
|
|
||||||
|
|
||||||
public OsmNodePT()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmNodePT( byte[] descriptionBits )
|
|
||||||
{
|
|
||||||
this.descriptionBits = descriptionBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final byte[] getNodeDecsription()
|
|
||||||
{
|
|
||||||
return descriptionBits;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
/**
|
|
||||||
* Parser for a train schedule
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import btools.expressions.BExpressionContextWay;
|
|
||||||
|
|
||||||
final class ScheduleParser
|
|
||||||
{
|
|
||||||
public static Map<String, StationNode> parseTrainTable( File[] files, GraphLoader graph, BExpressionContextWay expctxWay )
|
|
||||||
{
|
|
||||||
ScheduledLine currentLine = null;
|
|
||||||
StationNode lastStationInLine = null;
|
|
||||||
|
|
||||||
boolean readingLocations = false;
|
|
||||||
|
|
||||||
Map<String, StationNode> stationMap = new HashMap<String, StationNode>();
|
|
||||||
|
|
||||||
for ( File file : files )
|
|
||||||
{
|
|
||||||
BufferedReader br = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
br = new BufferedReader( new InputStreamReader( new FileInputStream( file ) , "ISO-8859-1" ) );
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
String line = br.readLine();
|
|
||||||
if ( line == null )
|
|
||||||
break;
|
|
||||||
line = line.trim();
|
|
||||||
if ( line.length() == 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( line.startsWith( "#" ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( line.startsWith( "-- locations" ) )
|
|
||||||
{
|
|
||||||
readingLocations = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( line.startsWith( "-- trainline" ) )
|
|
||||||
{
|
|
||||||
readingLocations = false;
|
|
||||||
currentLine = new ScheduledLine();
|
|
||||||
currentLine.name = line.substring( "-- trainline".length() ).trim();
|
|
||||||
lastStationInLine = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( readingLocations )
|
|
||||||
{
|
|
||||||
StationNode station = new StationNode();
|
|
||||||
|
|
||||||
// Eschborn 50.14323,8.56112
|
|
||||||
StringTokenizer tk = new StringTokenizer( line, " \t" );
|
|
||||||
station.name = tk.nextToken();
|
|
||||||
|
|
||||||
if ( stationMap.containsKey( station.name ) )
|
|
||||||
{
|
|
||||||
System.out.println( "skipping station name already known: " + station.name );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int locIdx = 0;
|
|
||||||
String loc = null;
|
|
||||||
int elev = 0;
|
|
||||||
int nconnections = 0;
|
|
||||||
while (tk.hasMoreTokens() || locIdx == 1)
|
|
||||||
{
|
|
||||||
if ( tk.hasMoreTokens() )
|
|
||||||
{
|
|
||||||
loc = tk.nextToken();
|
|
||||||
}
|
|
||||||
StringTokenizer tloc = new StringTokenizer( loc, "," );
|
|
||||||
int ilat = (int) ( ( Double.parseDouble( tloc.nextToken() ) + 90. ) * 1000000. + 0.5 );
|
|
||||||
int ilon = (int) ( ( Double.parseDouble( tloc.nextToken() ) + 180. ) * 1000000. + 0.5 );
|
|
||||||
if ( locIdx == 0 )
|
|
||||||
{
|
|
||||||
station.ilat = ilat;
|
|
||||||
station.ilon = ilon;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OsmNodeP pos = new OsmNodeP();
|
|
||||||
pos.ilat = ilat;
|
|
||||||
pos.ilon = ilon;
|
|
||||||
|
|
||||||
OsmNodeP node = graph.matchNodeForPosition( pos, expctxWay, false );
|
|
||||||
if ( node != null )
|
|
||||||
{
|
|
||||||
elev += node.selev;
|
|
||||||
nconnections++;
|
|
||||||
|
|
||||||
// link station to connecting node
|
|
||||||
OsmLinkP link = new OsmLinkP( station, node );
|
|
||||||
link.descriptionBitmap = null;
|
|
||||||
station.addLink( link );
|
|
||||||
node.addLink( link );
|
|
||||||
|
|
||||||
int distance = station.calcDistance( node );
|
|
||||||
System.out.println( "matched connection for station " + station.name + " at " + distance + " meter" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
locIdx++;
|
|
||||||
}
|
|
||||||
if ( nconnections > 0 )
|
|
||||||
{
|
|
||||||
station.selev = (short) ( elev / nconnections );
|
|
||||||
}
|
|
||||||
stationMap.put( station.name, station );
|
|
||||||
}
|
|
||||||
else if ( currentLine != null )
|
|
||||||
{
|
|
||||||
int idx = line.indexOf( ' ' );
|
|
||||||
String name = line.substring( 0, idx );
|
|
||||||
StationNode nextStationInLine = stationMap.get( name );
|
|
||||||
if ( nextStationInLine == null )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException( "unknown station: " + name );
|
|
||||||
}
|
|
||||||
String value = line.substring( idx ).trim();
|
|
||||||
int offsetMinute = 0;
|
|
||||||
if ( lastStationInLine == null )
|
|
||||||
{
|
|
||||||
currentLine.schedule = new TrainSchedule( value );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( value.startsWith( "+" ) )
|
|
||||||
value = value.substring( 1 );
|
|
||||||
offsetMinute = Integer.parseInt( value );
|
|
||||||
|
|
||||||
ScheduledLink link = new ScheduledLink( lastStationInLine, nextStationInLine );
|
|
||||||
link.line = currentLine;
|
|
||||||
link.indexInLine = currentLine.offsetMinutes.size() - 1;
|
|
||||||
|
|
||||||
// System.out.println( "adding: " + link );
|
|
||||||
lastStationInLine.addLink( link );
|
|
||||||
}
|
|
||||||
currentLine.offsetMinutes.add( Integer.valueOf( offsetMinute ) );
|
|
||||||
|
|
||||||
lastStationInLine = nextStationInLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println( "read " + stationMap.size() + " stations" );
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException( e );
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if ( br != null )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
br.close();
|
|
||||||
}
|
|
||||||
catch (Exception e) { /* ignore */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stationMap;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/**
|
|
||||||
* A specific departure
|
|
||||||
* (relative to a certain station and line)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final class ScheduledDeparture
|
|
||||||
{
|
|
||||||
long waitTime;
|
|
||||||
long rideTime;
|
|
||||||
OffsetSet offsets;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "wait=" + waitTime + " ride=" + rideTime + " offsets=" + offsets;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
/**
|
|
||||||
* A train line as a common set of stations with many departures
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class ScheduledLine
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
List<Integer> offsetMinutes = new ArrayList<Integer>();
|
|
||||||
TrainSchedule schedule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a list of departures relative to the start-time plus the individual
|
|
||||||
* offsets according to the offset mask
|
|
||||||
*
|
|
||||||
* departures with the same wait-time are aggregated in one result element
|
|
||||||
* with multiple 1-bits in the offset mask
|
|
||||||
*
|
|
||||||
* departures with different wait-times are returned as separate items
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
* the value to add to this set.
|
|
||||||
* @return true if "id" already contained in this set.
|
|
||||||
*/
|
|
||||||
public List<ScheduledDeparture> getScheduledDepartures( int idx, long timeFrom, OffsetSet offsets )
|
|
||||||
{
|
|
||||||
List<ScheduledDeparture> result = new ArrayList<ScheduledDeparture>();
|
|
||||||
|
|
||||||
long minutesFrom = ( timeFrom + 59999L ) / 60000L;
|
|
||||||
long timeFromCorrection = minutesFrom * 60000L - timeFrom;
|
|
||||||
|
|
||||||
if ( idx < 0 || idx >= offsetMinutes.size() - 1 )
|
|
||||||
return result;
|
|
||||||
|
|
||||||
int offsetStart = offsetMinutes.get( idx ).intValue();
|
|
||||||
int offsetEnd = offsetMinutes.get( idx + 1 ).intValue();
|
|
||||||
|
|
||||||
Map<Integer, List<Integer>> waitOffsets = getDepartures( offsetStart, timeFrom + timeFromCorrection, offsets );
|
|
||||||
|
|
||||||
for ( Map.Entry<Integer, List<Integer>> e : waitOffsets.entrySet() )
|
|
||||||
{
|
|
||||||
ScheduledDeparture depart = new ScheduledDeparture();
|
|
||||||
depart.waitTime = e.getKey().intValue() * 60000L + timeFromCorrection;
|
|
||||||
depart.offsets = offsets.create( e.getValue() );
|
|
||||||
depart.rideTime = ( offsetEnd - offsetStart ) * 60000L;
|
|
||||||
result.add( depart );
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<Integer, List<Integer>> getDepartures( int offsetStart, long timeFrom, OffsetSet offsets )
|
|
||||||
{
|
|
||||||
Map<Integer, List<Integer>> waitOffsets = new HashMap<Integer, List<Integer>>();
|
|
||||||
int size = offsets.size();
|
|
||||||
|
|
||||||
for ( int offset = 0;; )
|
|
||||||
{
|
|
||||||
// skip to next offset bit
|
|
||||||
while (offset < size && !offsets.contains( offset ))
|
|
||||||
{
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
if ( offset >= size )
|
|
||||||
return waitOffsets;
|
|
||||||
|
|
||||||
int toNext = schedule.getMinutesToNext( timeFrom + 60000L * ( offset - offsetStart ) );
|
|
||||||
|
|
||||||
if ( toNext < 0 )
|
|
||||||
return waitOffsets;
|
|
||||||
int departOffset = offset + toNext;
|
|
||||||
|
|
||||||
// whats the closest offset within the next toNext minutes
|
|
||||||
int lastOffset = offset;
|
|
||||||
while (toNext-- >= 0 && offset < size)
|
|
||||||
{
|
|
||||||
if ( offsets.contains( offset ) )
|
|
||||||
{
|
|
||||||
lastOffset = offset;
|
|
||||||
}
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if ( lastOffset == size - 1 )
|
|
||||||
// return waitOffsets; // todo?
|
|
||||||
|
|
||||||
int waitTime = departOffset - lastOffset;
|
|
||||||
|
|
||||||
// if we have that wait time in the list, just add the offset bit
|
|
||||||
List<Integer> offsetList = waitOffsets.get( Integer.valueOf( waitTime ) );
|
|
||||||
if ( offsetList == null )
|
|
||||||
{
|
|
||||||
offsetList = new ArrayList<Integer>();
|
|
||||||
waitOffsets.put( Integer.valueOf( waitTime ), offsetList );
|
|
||||||
}
|
|
||||||
offsetList.add( Integer.valueOf( lastOffset ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/**
|
|
||||||
* Container for link between two Osm nodes (pre-pocessor version)
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
|
|
||||||
public class ScheduledLink extends OsmLinkP
|
|
||||||
{
|
|
||||||
public ScheduledLink( StationNode source, StationNode target )
|
|
||||||
{
|
|
||||||
super( source, target );
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScheduledLine line;
|
|
||||||
public int indexInLine;
|
|
||||||
|
|
||||||
public boolean isConnection()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWayLink()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "ScheduledLink: line=" + line.name + " indexInLine=" + indexInLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SortedSet<Integer> usedTimes;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initLink()
|
|
||||||
{
|
|
||||||
super.initLink();
|
|
||||||
usedTimes = new TreeSet<Integer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public OffsetSet filterAndClose( OffsetSet in, long arrival )
|
|
||||||
{
|
|
||||||
OffsetSet filtered = super.filterAndClose( in, arrival );
|
|
||||||
if ( filtered != null && arrival >= 0 )
|
|
||||||
{
|
|
||||||
int minutesArrival = (int) ( arrival / 60000L );
|
|
||||||
filtered = filtered.filterWithSet( usedTimes, minutesArrival );
|
|
||||||
}
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,597 +0,0 @@
|
||||||
/**
|
|
||||||
* Simple Train Router
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import btools.mapaccess.OsmPos;
|
|
||||||
import btools.router.OsmPathElement;
|
|
||||||
import btools.router.OsmTrack;
|
|
||||||
import btools.router.RoutingContext;
|
|
||||||
import btools.router.RoutingEngine;
|
|
||||||
import btools.util.SortedHeap;
|
|
||||||
|
|
||||||
final class ScheduledRouter
|
|
||||||
{
|
|
||||||
private GraphLoader graph;
|
|
||||||
|
|
||||||
public long linksProcessed = 0L;
|
|
||||||
public long linksReProcessed = 0L;
|
|
||||||
public long closedSkippedChained = 0L;
|
|
||||||
public long skippedChained = 0L;
|
|
||||||
|
|
||||||
private RoutingContext rc;
|
|
||||||
private RoutingEngine re;
|
|
||||||
|
|
||||||
private long time0;
|
|
||||||
private OsmNodeP start;
|
|
||||||
private OsmNodeP end;
|
|
||||||
|
|
||||||
SortedHeap<ScheduledTrip> openSet = new SortedHeap<ScheduledTrip>();
|
|
||||||
|
|
||||||
ScheduledRouter( GraphLoader graph, RoutingContext rc, RoutingEngine re )
|
|
||||||
{
|
|
||||||
this.graph = graph;
|
|
||||||
this.rc = rc;
|
|
||||||
this.re = re;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Iternity> trips = new ArrayList<Iternity>();
|
|
||||||
private static long oldChecksum = 0;
|
|
||||||
|
|
||||||
private String startEndText()
|
|
||||||
{
|
|
||||||
return (start == null ? "unmatched" : start.getName() ) + "->" + (end == null ? "unmatched" : end.getName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmTrack findRoute( OsmPos startPos, OsmPos endPos, int alternativeIdx ) throws Exception
|
|
||||||
{
|
|
||||||
if ( alternativeIdx == -1 ) // lowest cost result
|
|
||||||
{
|
|
||||||
List<Iternity> singleTrip = _findRoute( startPos, endPos, true );
|
|
||||||
if ( singleTrip.isEmpty() )
|
|
||||||
{
|
|
||||||
if ( linksProcessed + linksReProcessed > 5000000 ) throw new RuntimeException( "5 million links limit reached" );
|
|
||||||
else throw new RuntimeException( "no track found! (" + startEndText() + ")" );
|
|
||||||
}
|
|
||||||
Iternity iternity = singleTrip.get( 0 );
|
|
||||||
OsmTrack t = iternity.track;
|
|
||||||
t.iternity = iternity.details;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for identical params
|
|
||||||
long[] nogocheck = rc.getNogoChecksums();
|
|
||||||
|
|
||||||
long checksum = nogocheck[0] + nogocheck[1] + nogocheck[2];
|
|
||||||
checksum += startPos.getILat() + startPos.getILon() + endPos.getILat() + endPos.getILon();
|
|
||||||
checksum += rc.localFunction.hashCode();
|
|
||||||
|
|
||||||
if ( checksum != oldChecksum )
|
|
||||||
{
|
|
||||||
trips = _findRoute( startPos, endPos, false );
|
|
||||||
Collections.sort( trips ); // sort by arrival time
|
|
||||||
oldChecksum = checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( trips.isEmpty() )
|
|
||||||
{
|
|
||||||
if ( linksProcessed + linksReProcessed > 5000000 ) throw new RuntimeException( "5 million links limit reached" );
|
|
||||||
else throw new RuntimeException( "no track found! (" + startEndText() + ")" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( alternativeIdx == 0 ) // = result overview
|
|
||||||
{
|
|
||||||
List<String> details = new ArrayList<String>();
|
|
||||||
for ( int idx = 0; idx < trips.size(); idx++ )
|
|
||||||
{
|
|
||||||
if ( idx > 0 ) details.add( "" );
|
|
||||||
Iternity iternity = trips.get( idx );
|
|
||||||
iternity.appendSummary( details );
|
|
||||||
}
|
|
||||||
Iternity iternity = trips.get( 0 );
|
|
||||||
OsmTrack t = iternity.track;
|
|
||||||
t.iternity = details;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx = alternativeIdx > trips.size() ? trips.size()-1 : alternativeIdx-1;
|
|
||||||
Iternity iternity = trips.get( idx );
|
|
||||||
OsmTrack t = iternity.track;
|
|
||||||
t.iternity = iternity.details;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private List<Iternity> _findRoute( OsmPos startPos, OsmPos endPos, boolean fastStop ) throws Exception
|
|
||||||
{
|
|
||||||
List<Iternity> iternities = new ArrayList<Iternity>();
|
|
||||||
|
|
||||||
start = graph.matchNodeForPosition( startPos, rc.expctxWay, rc.transitonly );
|
|
||||||
if ( start == null )
|
|
||||||
throw new IllegalArgumentException( "unmatched start: " + startPos );
|
|
||||||
end = graph.matchNodeForPosition( endPos, rc.expctxWay, rc.transitonly );
|
|
||||||
if ( end == null )
|
|
||||||
throw new IllegalArgumentException( "unmatched end: " + endPos );
|
|
||||||
|
|
||||||
time0 = System.currentTimeMillis() + (long) ( rc.starttimeoffset * 60000L );
|
|
||||||
long minutes0 = ( time0 + 59999L ) / 60000L;
|
|
||||||
time0 = minutes0 * 60000L;
|
|
||||||
|
|
||||||
OffsetSet fullSet = OffsetSet.fullSet();
|
|
||||||
OffsetSet finishedOffsets = fullSet.emptySet();
|
|
||||||
|
|
||||||
OsmLinkP startLink = new OsmLinkP( null, start );
|
|
||||||
|
|
||||||
ScheduledTrip startTrip = new ScheduledTrip( OffsetSet.fullSet(), startLink, null, null );
|
|
||||||
openSet.add( 0, startTrip );
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
if ( re.isTerminated() )
|
|
||||||
{
|
|
||||||
throw new RuntimeException( "operation terminated" );
|
|
||||||
}
|
|
||||||
if ( linksProcessed + linksReProcessed > 5000000 )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get cheapest trip from heap
|
|
||||||
ScheduledTrip trip = openSet.popLowestKeyValue();
|
|
||||||
if ( trip == null )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
OsmLinkP currentLink = trip.link;
|
|
||||||
OsmNodeP currentNode = trip.getTargetNode();
|
|
||||||
if ( currentNode == null )
|
|
||||||
{
|
|
||||||
System.out.println( "ups: " + trip );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( currentLink.isVirgin() )
|
|
||||||
{
|
|
||||||
linksProcessed++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
linksReProcessed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* int maxOffset = (int)((maxarrival - trip.arrival)/60000L);
|
|
||||||
OffsetSet offsets = trip.offsets.ensureMaxOffset( maxOffset );
|
|
||||||
if ( offsets == null )
|
|
||||||
continue;
|
|
||||||
*/
|
|
||||||
|
|
||||||
OffsetSet offsets = trip.offsets;
|
|
||||||
|
|
||||||
// check global closure
|
|
||||||
offsets = finishedOffsets.filter( offsets );
|
|
||||||
if ( offsets == null )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* offsets = efficientSubset( offsets, trip.adjustedCost );
|
|
||||||
if ( offsets == null )
|
|
||||||
continue;
|
|
||||||
*/
|
|
||||||
|
|
||||||
boolean continueOnLineOnly = false;
|
|
||||||
// check local closure for the target node:
|
|
||||||
if ( currentNode.filterAndCloseNode( offsets, true ) == null )
|
|
||||||
{
|
|
||||||
continueOnLineOnly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check local closure for links:
|
|
||||||
offsets = currentLink.filterAndClose( offsets, trip.arrival );
|
|
||||||
if ( offsets == null )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// check for arrival
|
|
||||||
if ( currentNode == end )
|
|
||||||
{
|
|
||||||
Iternity iternity = null;
|
|
||||||
OffsetSet toffsets = trip.offsets;
|
|
||||||
|
|
||||||
int lastIdx = iternities.size()-1;
|
|
||||||
// hack: for equal cost tracks, merge offsets (assuming identical iternity)
|
|
||||||
if ( lastIdx >= 0 && iternities.get( lastIdx ).track.cost == trip.cost )
|
|
||||||
{
|
|
||||||
toffsets = toffsets.add( iternities.get( lastIdx ).offsets );
|
|
||||||
iternities.remove( lastIdx );
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int offset = 0; offset < trip.offsets.size(); offset++ )
|
|
||||||
{
|
|
||||||
if ( trip.offsets.contains( offset ) )
|
|
||||||
{
|
|
||||||
iternity = compileTrip( trip, offset );
|
|
||||||
iternity.offsets = toffsets;
|
|
||||||
System.out.println( "---- begin route ------ (cost " + iternity.track.cost + ")" );
|
|
||||||
for ( String s : iternity.details )
|
|
||||||
System.out.println( s );
|
|
||||||
System.out.println( "---- end route ------ (arrival " + new Date( iternity.arrivaltime ) + ")" );
|
|
||||||
break; // + plus more offsets..
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finishedOffsets = finishedOffsets.add( offsets );
|
|
||||||
if ( iternity != null )
|
|
||||||
{
|
|
||||||
// tracks come in cost-ascending order, so the arrival time
|
|
||||||
// must decrease for the new track to be efficient
|
|
||||||
// if ( iternities.isEmpty() || iternities.get( iternities.size() - 1 ).arrivaltime > iternity.arrivaltime )
|
|
||||||
if ( efficientSubset( iternities, iternity.offsets, iternity.track.cost ) != null )
|
|
||||||
{
|
|
||||||
iternities.add( iternity );
|
|
||||||
if ( fastStop )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println( "*** added track to result list !**** ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println( "*** finishedOffsets = " + finishedOffsets );
|
|
||||||
|
|
||||||
}
|
|
||||||
for ( OsmLinkP link = currentNode.getFirstLink(); link != null; link = link.getNext( currentNode ) )
|
|
||||||
{
|
|
||||||
if ( continueOnLineOnly )
|
|
||||||
{
|
|
||||||
if ( ! ( currentLink instanceof ScheduledLink && link instanceof ScheduledLink ) ) continue;
|
|
||||||
if ( ((ScheduledLink) currentLink).line != ((ScheduledLink) link ).line ) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* // pre-check target closure
|
|
||||||
if ( link.getTarget( currentNode ).filterAndCloseNode( offsets, false ) == null )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if ( !rc.transitonly || link instanceof ScheduledLink )
|
|
||||||
{
|
|
||||||
addNextTripsForLink( trip, currentNode, currentLink, link, offsets, 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iternities;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OffsetSet efficientSubset( List<Iternity> iternities, OffsetSet offsets, int cost )
|
|
||||||
{
|
|
||||||
for( Iternity it : iternities )
|
|
||||||
{
|
|
||||||
// determine a time-diff from a cost-diff
|
|
||||||
int dcost = cost - it.track.cost;
|
|
||||||
int dtime = (int) ( dcost * .06 / rc.cost1speed + 1. ); // 22km/h
|
|
||||||
offsets = offsets.sweepWith( it.offsets, dtime );
|
|
||||||
if ( offsets == null )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void addToOpenSet( ScheduledTrip nextTrip )
|
|
||||||
{
|
|
||||||
int distance = nextTrip.getTargetNode().calcDistance( end );
|
|
||||||
nextTrip.adjustedCost = nextTrip.cost + (int) ( distance * rc.pass1coefficient + 0.5 );
|
|
||||||
openSet.add( nextTrip.adjustedCost, nextTrip );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNextTripsForLink( ScheduledTrip trip, OsmNodeP currentNode, OsmLinkP currentLink, OsmLinkP link, OffsetSet offsets, int level )
|
|
||||||
{
|
|
||||||
OsmNodeP node = link.getTarget( currentNode );
|
|
||||||
if ( node == null )
|
|
||||||
{
|
|
||||||
System.out.println( "ups2: " + link );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( node == trip.originNode )
|
|
||||||
{
|
|
||||||
link.filterAndClose( offsets, -1 ); // invalidate reverse link
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc distance and check nogos
|
|
||||||
rc.nogoCost = 0.;
|
|
||||||
int distance = rc.calcDistance( currentNode.ilon, currentNode.ilat, node.ilon, node.ilat );
|
|
||||||
if ( Math.abs(rc.nogoCost) > 0.)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( link instanceof ScheduledLink )
|
|
||||||
{
|
|
||||||
// System.out.println( "next trip for link: " + link + " at offset " +
|
|
||||||
// offsets );
|
|
||||||
|
|
||||||
ScheduledLink slink = (ScheduledLink) link;
|
|
||||||
ScheduledLine line = slink.line;
|
|
||||||
|
|
||||||
// line change delay
|
|
||||||
long delay = 0L;
|
|
||||||
if ( currentLink instanceof ScheduledLink )
|
|
||||||
{
|
|
||||||
delay = ( (ScheduledLink) currentLink ).line == line ? 0L : (long) ( rc.changetime * 1000. ); // 3 minutes
|
|
||||||
}
|
|
||||||
long changePenalty = delay > 0 ? 60000L : 0L;
|
|
||||||
|
|
||||||
List<ScheduledDeparture> nextDepartures = line.getScheduledDepartures( slink.indexInLine, time0 + trip.arrival + delay, offsets );
|
|
||||||
for ( ScheduledDeparture nextDeparture : nextDepartures )
|
|
||||||
{
|
|
||||||
ScheduledTrip nextTrip = new ScheduledTrip( nextDeparture.offsets, link, currentNode, trip );
|
|
||||||
long waitTime = nextDeparture.waitTime + delay;
|
|
||||||
long rideTime = nextDeparture.rideTime;
|
|
||||||
|
|
||||||
nextTrip.cost = trip.cost + (int) ( ( rideTime + changePenalty + waitTime * rc.waittimeadjustment ) * rc.cost1speed / 3600. ); // 22km/h
|
|
||||||
nextTrip.departure = trip.arrival + waitTime;
|
|
||||||
nextTrip.arrival = nextTrip.departure + rideTime;
|
|
||||||
|
|
||||||
addToOpenSet( nextTrip );
|
|
||||||
|
|
||||||
// System.out.println( "found: " + nextTrip );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( link.isWayLink() )
|
|
||||||
{
|
|
||||||
// get costfactor
|
|
||||||
rc.expctxWay.evaluate( link.isReverse( currentNode ), link.descriptionBitmap );
|
|
||||||
|
|
||||||
// *** penalty for distance
|
|
||||||
float costfactor = rc.expctxWay.getCostfactor();
|
|
||||||
if ( costfactor > 9999. )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int waycost = (int) ( distance * costfactor + 0.5f );
|
|
||||||
|
|
||||||
// *** add initial cost if factor changed
|
|
||||||
float costdiff = costfactor - trip.lastcostfactor;
|
|
||||||
if ( costdiff > 0.0005 || costdiff < -0.0005 )
|
|
||||||
{
|
|
||||||
waycost += (int) rc.expctxWay.getInitialcost();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( node.getNodeDecsription() != null )
|
|
||||||
{
|
|
||||||
rc.expctxNode.evaluate( rc.expctxWay.getNodeAccessGranted() != 0., node.getNodeDecsription() );
|
|
||||||
float initialcost = rc.expctxNode.getInitialcost();
|
|
||||||
if ( initialcost >= 1000000. )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
waycost += (int) initialcost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// *** penalty for turning angles
|
|
||||||
if ( trip.originNode != null )
|
|
||||||
{
|
|
||||||
// penalty proportional to direction change
|
|
||||||
double angle = rc.calcAngle( trip.originNode.ilon, trip.originNode.ilat, currentNode.ilon, currentNode.ilat, node.ilon, node.ilat );
|
|
||||||
double cos = 1. - rc.getCosAngle();
|
|
||||||
|
|
||||||
int turncost = (int) ( cos * rc.expctxWay.getTurncost() + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
|
|
||||||
waycost += turncost;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScheduledTrip nextTrip = new ScheduledTrip( offsets, link, currentNode, trip );
|
|
||||||
|
|
||||||
// *** penalty for elevation
|
|
||||||
short ele2 = node.selev;
|
|
||||||
short ele1 = trip.selev;
|
|
||||||
int elefactor = 250000;
|
|
||||||
if ( ele2 == Short.MIN_VALUE )
|
|
||||||
ele2 = ele1;
|
|
||||||
nextTrip.selev = ele2;
|
|
||||||
if ( ele1 != Short.MIN_VALUE )
|
|
||||||
{
|
|
||||||
nextTrip.ehbd = trip.ehbd + ( ele1 - ele2 ) * elefactor - distance * rc.downhillcutoff;
|
|
||||||
nextTrip.ehbu = trip.ehbu + ( ele2 - ele1 ) * elefactor - distance * rc.uphillcutoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( nextTrip.ehbd > rc.elevationpenaltybuffer )
|
|
||||||
{
|
|
||||||
int excess = nextTrip.ehbd - rc.elevationpenaltybuffer;
|
|
||||||
int reduce = distance * rc.elevationbufferreduce;
|
|
||||||
if ( reduce > excess )
|
|
||||||
{
|
|
||||||
reduce = excess;
|
|
||||||
}
|
|
||||||
excess = nextTrip.ehbd - rc.elevationmaxbuffer;
|
|
||||||
if ( reduce < excess )
|
|
||||||
{
|
|
||||||
reduce = excess;
|
|
||||||
}
|
|
||||||
nextTrip.ehbd -= reduce;
|
|
||||||
if ( rc.downhillcostdiv > 0 )
|
|
||||||
{
|
|
||||||
int elevationCost = reduce / rc.downhillcostdiv;
|
|
||||||
waycost += elevationCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( nextTrip.ehbd < 0 )
|
|
||||||
{
|
|
||||||
nextTrip.ehbd = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( nextTrip.ehbu > rc.elevationpenaltybuffer )
|
|
||||||
{
|
|
||||||
int excess = nextTrip.ehbu - rc.elevationpenaltybuffer;
|
|
||||||
int reduce = distance * rc.elevationbufferreduce;
|
|
||||||
if ( reduce > excess )
|
|
||||||
{
|
|
||||||
reduce = excess;
|
|
||||||
}
|
|
||||||
excess = nextTrip.ehbu - rc.elevationmaxbuffer;
|
|
||||||
if ( reduce < excess )
|
|
||||||
{
|
|
||||||
reduce = excess;
|
|
||||||
}
|
|
||||||
nextTrip.ehbu -= reduce;
|
|
||||||
if ( rc.uphillcostdiv > 0 )
|
|
||||||
{
|
|
||||||
int elevationCost = reduce / rc.uphillcostdiv;
|
|
||||||
waycost += elevationCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( nextTrip.ehbu < 0 )
|
|
||||||
{
|
|
||||||
nextTrip.ehbu = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTrip.lastcostfactor = costfactor;
|
|
||||||
nextTrip.cost = trip.cost + (int) ( waycost * rc.additionalcostfactor + 0.5 );
|
|
||||||
nextTrip.departure = trip.arrival;
|
|
||||||
nextTrip.arrival = nextTrip.departure + (long) ( waycost * 3600. / rc.cost1speed ); // 22km/h
|
|
||||||
addToOpenSet( nextTrip );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// connecting link
|
|
||||||
{
|
|
||||||
ScheduledTrip nextTrip = new ScheduledTrip( offsets, link, currentNode, trip );
|
|
||||||
|
|
||||||
long delay = (long) ( rc.buffertime * 1000. ); // 2 min
|
|
||||||
nextTrip.cost = trip.cost + (int) ( delay * rc.waittimeadjustment * rc.cost1speed / 3600. );
|
|
||||||
nextTrip.departure = trip.arrival;
|
|
||||||
nextTrip.arrival = nextTrip.departure + delay;
|
|
||||||
|
|
||||||
addToOpenSet( nextTrip );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Iternity compileTrip( ScheduledTrip trip, int offset )
|
|
||||||
{
|
|
||||||
Iternity iternity = new Iternity();
|
|
||||||
|
|
||||||
OsmTrack track = new OsmTrack();
|
|
||||||
ScheduledTrip current = trip;
|
|
||||||
ScheduledLine lastLine = new ScheduledLine();
|
|
||||||
ScheduledLine dummyLine = new ScheduledLine();
|
|
||||||
List<ScheduledTrip> list = new ArrayList<ScheduledTrip>();
|
|
||||||
|
|
||||||
int distance = 0;
|
|
||||||
long departure = 0;
|
|
||||||
|
|
||||||
ScheduledTrip itrip = null;
|
|
||||||
|
|
||||||
String profile = extractProfile( rc.localFunction );
|
|
||||||
|
|
||||||
SimpleDateFormat df = new SimpleDateFormat( "dd.MM HH:mm" );
|
|
||||||
// time0 = df.parse(startTime).getTime();
|
|
||||||
|
|
||||||
|
|
||||||
OsmNodeP nextNode = null;
|
|
||||||
while (current != null)
|
|
||||||
{
|
|
||||||
departure = current.departure;
|
|
||||||
|
|
||||||
// System.out.println( "trip=" + current );
|
|
||||||
OsmNodeP node = current.getTargetNode();
|
|
||||||
OsmPathElement pe = OsmPathElement.create( node.ilon, node.ilat, node.selev, null, false );
|
|
||||||
track.addNode( pe );
|
|
||||||
|
|
||||||
if ( nextNode != null )
|
|
||||||
{
|
|
||||||
distance += node.calcDistance( nextNode );
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isScheduled = current.link instanceof ScheduledLink;
|
|
||||||
boolean isConnection = current.link.descriptionBitmap == null && !isScheduled;
|
|
||||||
ScheduledLine line = isScheduled ? ( (ScheduledLink) current.link ).line : isConnection ? dummyLine : null;
|
|
||||||
|
|
||||||
if ( line != lastLine && !isConnection )
|
|
||||||
{
|
|
||||||
itrip = new ScheduledTrip();
|
|
||||||
itrip.departure = current.departure;
|
|
||||||
itrip.arrival = current.arrival;
|
|
||||||
itrip.originNode = current.originNode;
|
|
||||||
itrip.link = current.link;
|
|
||||||
|
|
||||||
if ( isScheduled && list.size() > 0 )
|
|
||||||
{
|
|
||||||
list.get( list.size() - 1 ).originNode = current.getTargetNode();
|
|
||||||
}
|
|
||||||
list.add( itrip );
|
|
||||||
}
|
|
||||||
else if ( itrip != null && !isConnection )
|
|
||||||
{
|
|
||||||
itrip.departure = current.departure;
|
|
||||||
itrip.originNode = current.originNode;
|
|
||||||
}
|
|
||||||
lastLine = line;
|
|
||||||
current = current.origin;
|
|
||||||
nextNode = node;
|
|
||||||
}
|
|
||||||
track.distance = distance;
|
|
||||||
track.cost = trip.cost;
|
|
||||||
|
|
||||||
for ( int i = list.size() - 1; i >= 0; i-- )
|
|
||||||
{
|
|
||||||
current = list.get( i );
|
|
||||||
String lineName = profile;
|
|
||||||
|
|
||||||
boolean isScheduled = current.link instanceof ScheduledLink;
|
|
||||||
if ( isScheduled )
|
|
||||||
{
|
|
||||||
lineName = ( (ScheduledLink) current.link ).line.name;
|
|
||||||
}
|
|
||||||
String stationName = "*position*";
|
|
||||||
if ( current.originNode instanceof StationNode )
|
|
||||||
{
|
|
||||||
stationName = ( (StationNode) current.originNode ).name;
|
|
||||||
}
|
|
||||||
String nextStationName = "*position*";
|
|
||||||
if ( i > 0 && list.get( i - 1 ).originNode instanceof StationNode )
|
|
||||||
{
|
|
||||||
nextStationName = ( (StationNode) list.get( i - 1 ).originNode ).name;
|
|
||||||
}
|
|
||||||
if ( i == 0 && current.link.targetNode instanceof StationNode )
|
|
||||||
{
|
|
||||||
nextStationName = ( (StationNode)current.link.targetNode ).name;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Date d0 = new Date( time0 + 60000L * offset + current.departure );
|
|
||||||
Date d1 = new Date( time0 + 60000L * offset + current.arrival );
|
|
||||||
if ( iternity.details.size() > 0 )
|
|
||||||
iternity.details.add( "" );
|
|
||||||
iternity.details.add( "depart: " + df.format( d0 ) + " " + stationName );
|
|
||||||
iternity.details.add( " --- " + lineName + " ---" );
|
|
||||||
iternity.details.add( "arrive: " + df.format( d1 ) + " " + nextStationName );
|
|
||||||
|
|
||||||
if ( !iternity.lines.contains( lineName ) )
|
|
||||||
{
|
|
||||||
iternity.lines.add( lineName );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iternity.track = track;
|
|
||||||
iternity.arrivaltime = time0 + 60000L * offset + trip.arrival;
|
|
||||||
iternity.departtime = time0 + 60000L * offset + departure;
|
|
||||||
|
|
||||||
return iternity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractProfile( String s )
|
|
||||||
{
|
|
||||||
int idx = s.lastIndexOf( '/' );
|
|
||||||
if ( idx >= 0 )
|
|
||||||
s = s.substring( idx + 1 );
|
|
||||||
idx = s.indexOf( '.' );
|
|
||||||
if ( idx >= 0 )
|
|
||||||
s = s.substring( 0, idx );
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* Simple Train Router
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public final class ScheduledTrip
|
|
||||||
{
|
|
||||||
public OffsetSet offsets;
|
|
||||||
|
|
||||||
ScheduledTrip origin;
|
|
||||||
OsmLinkP link;
|
|
||||||
OsmNodeP originNode;
|
|
||||||
int cost; // in meter!
|
|
||||||
int adjustedCost; // in meter!
|
|
||||||
long arrival; // in millis!
|
|
||||||
long departure; // in millis!
|
|
||||||
|
|
||||||
public float lastcostfactor;
|
|
||||||
|
|
||||||
public int ehbd; // in micrometer
|
|
||||||
public int ehbu; // in micrometer
|
|
||||||
public short selev = Short.MIN_VALUE;
|
|
||||||
|
|
||||||
ScheduledTrip()
|
|
||||||
{
|
|
||||||
// dummy for OpenSetM
|
|
||||||
}
|
|
||||||
|
|
||||||
ScheduledTrip( OffsetSet offsets, OsmLinkP link, OsmNodeP originNode, ScheduledTrip origin )
|
|
||||||
{
|
|
||||||
this.offsets = offsets;
|
|
||||||
this.link = link;
|
|
||||||
this.origin = origin;
|
|
||||||
this.originNode = originNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmNodeP getTargetNode()
|
|
||||||
{
|
|
||||||
return link.getTarget(originNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
String prefix = "PlainLink";
|
|
||||||
if ( link instanceof ScheduledLink )
|
|
||||||
{
|
|
||||||
ScheduledLink l = (ScheduledLink)link;
|
|
||||||
ScheduledLine line = l.line;
|
|
||||||
prefix = "ScheduledLink: line=" + line.name;
|
|
||||||
}
|
|
||||||
else if ( link.isConnection() )
|
|
||||||
{
|
|
||||||
prefix = "ConnectingLink";
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + " depart=" + departure + " arrival=" + arrival + " cost=" + cost + " offsets=" + offsets;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/**
|
|
||||||
* A train station and it's connections
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final class StationNode extends OsmNodeP
|
|
||||||
{
|
|
||||||
private int instanceserial = 0;
|
|
||||||
|
|
||||||
|
|
||||||
private static class NodeOffsets implements OffsetSetHolder
|
|
||||||
{
|
|
||||||
private OffsetSet offsets;
|
|
||||||
|
|
||||||
public OffsetSet getOffsetSet()
|
|
||||||
{
|
|
||||||
return offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOffsetSet( OffsetSet offsets )
|
|
||||||
{
|
|
||||||
this.offsets = offsets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NodeOffsets offsets;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OffsetSet filterAndCloseNode( OffsetSet in, boolean closeGate )
|
|
||||||
{
|
|
||||||
if ( offsets == null || instanceserial != currentserial )
|
|
||||||
{
|
|
||||||
if ( closeGate )
|
|
||||||
{
|
|
||||||
instanceserial = currentserial;
|
|
||||||
offsets = new NodeOffsets();
|
|
||||||
offsets.setOffsetSet( in );
|
|
||||||
}
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
return in.filterAndClose( offsets, closeGate );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String name;
|
|
||||||
|
|
||||||
public String getName()
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,282 +0,0 @@
|
||||||
/**
|
|
||||||
* Information on matched way point
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
final class TrainSchedule
|
|
||||||
{
|
|
||||||
private static class TrainScheduleCron
|
|
||||||
{
|
|
||||||
long minutes;
|
|
||||||
long hours;
|
|
||||||
long dows;
|
|
||||||
|
|
||||||
TrainScheduleCron( String value )
|
|
||||||
{
|
|
||||||
StringTokenizer tk = new StringTokenizer( value, "_" );
|
|
||||||
minutes = parseElement( tk.nextToken() );
|
|
||||||
hours = parseElement( tk.nextToken() );
|
|
||||||
dows = parseElement( tk.nextToken() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private long parseElement( String s )
|
|
||||||
{
|
|
||||||
if ( "*".equals( s ) )
|
|
||||||
{
|
|
||||||
return Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
StringTokenizer tk = new StringTokenizer( s, "," );
|
|
||||||
long res = 0;
|
|
||||||
while (tk.hasMoreTokens())
|
|
||||||
{
|
|
||||||
String sub = tk.nextToken();
|
|
||||||
int start, end;
|
|
||||||
int idx = sub.indexOf( '-' );
|
|
||||||
if ( idx < 0 )
|
|
||||||
{
|
|
||||||
start = Integer.parseInt( sub );
|
|
||||||
end = start;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
start = Integer.parseInt( sub.substring( 0, idx ) );
|
|
||||||
end = Integer.parseInt( sub.substring( idx + 1 ) );
|
|
||||||
}
|
|
||||||
for ( int i = start; i <= end; i++ )
|
|
||||||
{
|
|
||||||
res |= ( 1L << i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matches( int minute, int hour, int dow )
|
|
||||||
{
|
|
||||||
return ( ( 1L << minute ) & minutes ) != 0 && ( ( 1L << hour ) & hours ) != 0 && ( ( 1L << dow ) & dows ) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TrainScheduleCron> cronsPositive;
|
|
||||||
private List<TrainScheduleCron> cronsNegative;
|
|
||||||
private Map<String,List<Integer>> timeTable = new HashMap<String,List<Integer>>();
|
|
||||||
|
|
||||||
public TrainSchedule( String cronstring )
|
|
||||||
{
|
|
||||||
StringTokenizer tk = new StringTokenizer( cronstring, " " );
|
|
||||||
|
|
||||||
cronsPositive = new ArrayList<TrainScheduleCron>();
|
|
||||||
cronsNegative = new ArrayList<TrainScheduleCron>();
|
|
||||||
|
|
||||||
while (tk.hasMoreTokens())
|
|
||||||
{
|
|
||||||
String prefix = tk.nextToken();
|
|
||||||
String value = tk.nextToken();
|
|
||||||
if ( "+".equals( prefix ) )
|
|
||||||
{
|
|
||||||
cronsPositive.add( new TrainScheduleCron( value ) );
|
|
||||||
}
|
|
||||||
else if ( "-".equals( prefix ) )
|
|
||||||
{
|
|
||||||
cronsNegative.add( new TrainScheduleCron( value ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// prefix is a calendar id, value a slash-separated time list
|
|
||||||
List<Integer> times = new ArrayList<Integer>();
|
|
||||||
StringTokenizer tk3 = new StringTokenizer( value, "/" );
|
|
||||||
while( tk3.hasMoreTokens() )
|
|
||||||
{
|
|
||||||
times.add( Integer.valueOf( time2Minute( tk3.nextToken() ) ) );
|
|
||||||
}
|
|
||||||
Collections.sort( times );
|
|
||||||
timeTable.put( prefix, times );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int time2Minute( String s )
|
|
||||||
{
|
|
||||||
StringTokenizer tk = new StringTokenizer( s, ":" );
|
|
||||||
int mins = 60 * Integer.parseInt( tk.nextToken() );
|
|
||||||
mins += Integer.parseInt( tk.nextToken() );
|
|
||||||
return mins;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMinutesToNext( long timeFrom )
|
|
||||||
{
|
|
||||||
if ( timeTable.isEmpty() )
|
|
||||||
{
|
|
||||||
return getMinutesToNextCron( timeFrom );
|
|
||||||
}
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.setTime( new Date( timeFrom ) );
|
|
||||||
int minute = cal.get( Calendar.MINUTE );
|
|
||||||
int hour = cal.get( Calendar.HOUR_OF_DAY );
|
|
||||||
|
|
||||||
int year = cal.get( Calendar.YEAR );
|
|
||||||
int month = cal.get( Calendar.MONTH ) + 1;
|
|
||||||
int day = cal.get( Calendar.DAY_OF_MONTH );
|
|
||||||
|
|
||||||
String sday = "" + ( year*10000 + month * 100 + day );
|
|
||||||
|
|
||||||
List<Integer> alltimes = null;
|
|
||||||
boolean listIsCopy = false;
|
|
||||||
for( String calId : timeTable.keySet() )
|
|
||||||
{
|
|
||||||
if ( calendarHasDate( calId, sday ) )
|
|
||||||
{
|
|
||||||
List<Integer> times = timeTable.get( calId );
|
|
||||||
if ( alltimes == null )
|
|
||||||
{
|
|
||||||
alltimes = times;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( !listIsCopy )
|
|
||||||
{
|
|
||||||
alltimes = new ArrayList<Integer>( alltimes );
|
|
||||||
listIsCopy = true;
|
|
||||||
}
|
|
||||||
alltimes.addAll( times );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( alltimes == null ) return -1;
|
|
||||||
if ( listIsCopy )
|
|
||||||
{
|
|
||||||
Collections.sort( alltimes );
|
|
||||||
}
|
|
||||||
|
|
||||||
int mins = 60*hour + minute;
|
|
||||||
for( Integer t : alltimes )
|
|
||||||
{
|
|
||||||
if ( t.intValue() >= mins )
|
|
||||||
{
|
|
||||||
return t.intValue() - mins;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getMinutesToNextCron( long timeFrom )
|
|
||||||
{
|
|
||||||
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.setTime( new Date( timeFrom ) );
|
|
||||||
int minute = cal.get( Calendar.MINUTE );
|
|
||||||
int hour = cal.get( Calendar.HOUR_OF_DAY );
|
|
||||||
int dow = cal.get( Calendar.DAY_OF_WEEK );
|
|
||||||
dow = dow > 1 ? dow - 1 : dow + 6;
|
|
||||||
|
|
||||||
for ( int cnt = 0; cnt < 180; cnt++ )
|
|
||||||
{
|
|
||||||
boolean veto = false;
|
|
||||||
for ( TrainScheduleCron cron : cronsNegative )
|
|
||||||
{
|
|
||||||
if ( cron.matches( minute, hour, dow ) )
|
|
||||||
{
|
|
||||||
veto = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !veto )
|
|
||||||
{
|
|
||||||
for ( TrainScheduleCron cron : cronsPositive )
|
|
||||||
{
|
|
||||||
if ( cron.matches( minute, hour, dow ) )
|
|
||||||
return cnt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ++minute == 60 )
|
|
||||||
{
|
|
||||||
minute = 0;
|
|
||||||
if ( ++hour == 24 )
|
|
||||||
{
|
|
||||||
hour = 0;
|
|
||||||
if ( ++dow == 8 )
|
|
||||||
{
|
|
||||||
dow = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String,Set<String>> calendarMap;
|
|
||||||
|
|
||||||
private static boolean calendarHasDate( String calId, String date )
|
|
||||||
{
|
|
||||||
if ( calendarMap == null )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
readCalendar();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException( "cannot readcalendar: " + e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Set<String> idSet = calendarMap.get( calId );
|
|
||||||
if ( idSet != null )
|
|
||||||
{
|
|
||||||
return idSet.contains( date );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readCalendar() throws Exception
|
|
||||||
{
|
|
||||||
System.out.println( "reading calendar..." );
|
|
||||||
calendarMap = new HashMap<String,Set<String>>();
|
|
||||||
|
|
||||||
Map<String,String> uniqueMap = new HashMap<String,String>();
|
|
||||||
|
|
||||||
BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream( new File( "../calendar_dates.txt" ) ), "UTF-8" ) );
|
|
||||||
br.readLine(); // skip header
|
|
||||||
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
String line = br.readLine();
|
|
||||||
if ( line == null ) break;
|
|
||||||
StringTokenizer tk = new StringTokenizer( line, "," );
|
|
||||||
String calId = tk.nextToken();
|
|
||||||
String calDate = tk.nextToken();
|
|
||||||
|
|
||||||
Set<String> idSet = calendarMap.get( calId );
|
|
||||||
if ( idSet == null )
|
|
||||||
{
|
|
||||||
idSet = new HashSet<String>();
|
|
||||||
calendarMap.put( calId, idSet );
|
|
||||||
}
|
|
||||||
String uniqueDate = uniqueMap.get( calDate );
|
|
||||||
if ( uniqueDate == null )
|
|
||||||
{
|
|
||||||
uniqueDate = calDate;
|
|
||||||
uniqueMap.put( uniqueDate, uniqueDate );
|
|
||||||
}
|
|
||||||
idSet.add( uniqueDate );
|
|
||||||
}
|
|
||||||
br.close();
|
|
||||||
System.out.println( "read calendar with " + calendarMap.size() + " ids" );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.expressions.BExpressionContext;
|
|
||||||
import btools.router.OsmNodeNamed;
|
|
||||||
import btools.router.OsmTrack;
|
|
||||||
import btools.router.RoutingContext;
|
|
||||||
import btools.router.RoutingEngine;
|
|
||||||
|
|
||||||
public class TwinRoutingEngine extends RoutingEngine
|
|
||||||
{
|
|
||||||
private static Object graphSync = new Object();
|
|
||||||
private static GraphLoader graph;
|
|
||||||
|
|
||||||
public TwinRoutingEngine( String outfileBase, String logfileBase, String segmentDir,
|
|
||||||
List<OsmNodeNamed> waypoints, RoutingContext rc )
|
|
||||||
{
|
|
||||||
super( outfileBase, logfileBase, segmentDir, waypoints, rc );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doRun( long maxRunningTime )
|
|
||||||
{
|
|
||||||
if ( routingContext.localFunction != null && routingContext.localFunction.startsWith( "../im/") )
|
|
||||||
{
|
|
||||||
doMemoryRun( maxRunningTime );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
super.doRun( maxRunningTime );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static private Map<String,BExpressionContext[]> expressionCache = new HashMap<String,BExpressionContext[]>();
|
|
||||||
|
|
||||||
private void doMemoryRun( long maxRunningTime )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
synchronized( graphSync )
|
|
||||||
{
|
|
||||||
if ( graph == null )
|
|
||||||
{
|
|
||||||
loadGraph();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* // reuse old expression-caches
|
|
||||||
BExpressionContext[] exp = expressionCache.get( routingContext.localFunction );
|
|
||||||
if ( exp == null )
|
|
||||||
{
|
|
||||||
exp = new BExpressionContext[2];
|
|
||||||
exp[0] = routingContext.expctxWay;
|
|
||||||
exp[1] = routingContext.expctxNode;
|
|
||||||
expressionCache.put( routingContext.localFunction, exp );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.out.println( "re-using exp-ctx for : " + routingContext.localFunction );
|
|
||||||
routingContext.expctxWay = exp[0];
|
|
||||||
routingContext.expctxNode = exp[1];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
OsmLinkP.currentserial++;
|
|
||||||
ScheduledRouter router = new ScheduledRouter( graph, routingContext, this );
|
|
||||||
foundTrack = router.findRoute( waypoints.get(0), waypoints.get(1), routingContext.getAlternativeIdx(-1,10) );
|
|
||||||
System.out.println( "linksProcessed=" + router.linksProcessed + " linksReProcessed=" + router.linksReProcessed);
|
|
||||||
System.out.println( "skippedChained=" + router.skippedChained + " closedSkippedChained=" + router.closedSkippedChained);
|
|
||||||
|
|
||||||
System.out.println( "expCtxWay: requests: " + routingContext.expctxWay.cacheStats() );
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
errorMessage = e.getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void loadGraph() throws Exception
|
|
||||||
{
|
|
||||||
File parentDir = new File( segmentDir ).getParentFile();
|
|
||||||
File nodeTilesIn = new File( parentDir, "unodes55");
|
|
||||||
File wayTilesIn = new File( parentDir, "waytiles55");
|
|
||||||
File[] fahrplanFiles = new File[2];
|
|
||||||
fahrplanFiles[0] = new File( parentDir, "fahrplan_nahverkehr.txt" );
|
|
||||||
fahrplanFiles[1] = new File( parentDir, "fahrplan_dbfern.txt" );
|
|
||||||
|
|
||||||
graph = new GraphLoader();
|
|
||||||
graph.process( nodeTilesIn, wayTilesIn, fahrplanFiles, routingContext.expctxWay );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import btools.expressions.BExpressionContextWay;
|
|
||||||
import btools.expressions.BExpressionMetaData;
|
|
||||||
|
|
||||||
public class MemrouterTest
|
|
||||||
{
|
|
||||||
@Test
|
|
||||||
public void memrouterTest() throws Exception
|
|
||||||
{
|
|
||||||
URL dummyurl = this.getClass().getResource( "/dummy.txt" );
|
|
||||||
Assert.assertTrue( "dummy.txt not found", dummyurl != null );
|
|
||||||
File workingDir = new File(dummyurl.getFile()).getParentFile();
|
|
||||||
File profileDir = new File( workingDir, "/../../../misc/profiles2" );
|
|
||||||
File dataDir = new File( workingDir, "/../../../brouter-map-creator/target/test-classes/tmp" );
|
|
||||||
File lookupFile = new File( profileDir, "lookups.dat" );
|
|
||||||
File profileFile = new File( profileDir, "trekking.brf" );
|
|
||||||
File waytiles55 = new File( dataDir, "waytiles55" );
|
|
||||||
File unodes55 = new File( dataDir, "unodes55" );
|
|
||||||
File[] fahrplanFiles = new File[1];
|
|
||||||
fahrplanFiles[0] = new File( workingDir, "fahrplan.txt" );
|
|
||||||
|
|
||||||
// read lookup + profile for lookup-version + access-filter
|
|
||||||
BExpressionMetaData meta = new BExpressionMetaData();
|
|
||||||
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
|
|
||||||
meta.readMetaData( lookupFile );
|
|
||||||
expctxWay.parseFile( profileFile, "global" );
|
|
||||||
|
|
||||||
|
|
||||||
// run GraphLoader
|
|
||||||
new GraphLoader().process( unodes55, waytiles55, fahrplanFiles, expctxWay );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package btools.memrouter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class OffsetSetTest
|
|
||||||
{
|
|
||||||
@Test
|
|
||||||
public void offsetSetTest() throws Exception
|
|
||||||
{
|
|
||||||
OffsetSet fullset = OffsetSet.fullSet();
|
|
||||||
OffsetSet s2 = fullset.emptySet().add( 4 );
|
|
||||||
|
|
||||||
Assert.assertTrue( "added bit not found", s2.contains( 4 ) );
|
|
||||||
Assert.assertTrue( "found false bit", !s2.contains( 3 ) );
|
|
||||||
|
|
||||||
Assert.assertTrue( "found bit in fullset", fullset.contains( 17 ) );
|
|
||||||
|
|
||||||
List<Integer> offsetList = new ArrayList<Integer>();
|
|
||||||
offsetList.add( Integer.valueOf( 4 ));
|
|
||||||
offsetList.add( Integer.valueOf( 5 ));
|
|
||||||
offsetList.add( Integer.valueOf( 23 ));
|
|
||||||
offsetList.add( Integer.valueOf( 50 ));
|
|
||||||
|
|
||||||
OffsetSet s4 = fullset.create( offsetList );
|
|
||||||
Assert.assertTrue( "added bit 23 not found", s4.contains( 23 ) );
|
|
||||||
Assert.assertTrue( "found false bit 17", !s4.contains( 17 ) );
|
|
||||||
|
|
||||||
OffsetSet s5 = s2.filter( s4 ); // de-select 4 from s4
|
|
||||||
Assert.assertTrue( "added bit 5 not found", s5.contains( 5 ) );
|
|
||||||
Assert.assertTrue( "found false bit 4", !s5.contains( 4 ) );
|
|
||||||
|
|
||||||
OffsetSet s6 = s4.filter( s2 ); // de-select 4,5,23,50 from s2 -> = null
|
|
||||||
Assert.assertTrue( "expected empty set", s6 == null );
|
|
||||||
|
|
||||||
OsmLinkP holder = new OsmLinkP();
|
|
||||||
holder.setOffsetSet( fullset.emptySet().add( 24 ) );
|
|
||||||
|
|
||||||
OffsetSet s7 = s4.filterAndClose( holder, true );
|
|
||||||
// Assert.assertTrue( "bit 4 too much", !s7.contains( 4 ) );
|
|
||||||
// Assert.assertTrue( "bit 5 not found", s7.contains( 5 ) );
|
|
||||||
// Assert.assertTrue( "bit 23 not found", s7.contains( 23 ) );
|
|
||||||
// Assert.assertTrue( "bit 50 too much", !s7.contains( 50 ) );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,546 +0,0 @@
|
||||||
-- locations
|
|
||||||
KoblenzHbf 50.35066,7.58997
|
|
||||||
Frankfurt(M)Hbf 50.10676,8.66323 50.10754,8.66107 50.10596,8.66448 50.10765,8.66566
|
|
||||||
Frankfurt/Hoechst 50.10271,8.54212 50.10363,8.54240 50.10186,8.54298
|
|
||||||
Bensheim-Auerbach 49.70216,8.61371
|
|
||||||
Bensheim 49.68108,8.61724
|
|
||||||
F-Niederrad 50.08096,8.63768
|
|
||||||
Walldorf 50.00159,8.58155
|
|
||||||
Moerfelden 49.97940,8.56534
|
|
||||||
Gross-G-Dornberg 49.91241,8.49420
|
|
||||||
Riedstatt-Goddelau 49.83462,8.49087
|
|
||||||
Stockstadt-Rhein 49.80906,8.47207
|
|
||||||
Biebesheim 49.78091,8.47350
|
|
||||||
Gernsheim 49.75359,8.49062
|
|
||||||
Gross-Rohrheim 49.71415,8.47826
|
|
||||||
Biblis 49.68821,8.45130
|
|
||||||
Buerstadt 49.64535,8.45888
|
|
||||||
Lampertheim 49.59978,8.47715
|
|
||||||
Mannheim-Waldhof 49.52538,8.48217
|
|
||||||
MannheimHbf 49.47983,8.47003
|
|
||||||
WiesbadenHbf 50.07118,8.24377
|
|
||||||
MainzHbf 50.00159,8.25939
|
|
||||||
Mainz-Roemisches-Th 49.99350,8.27852
|
|
||||||
Mainz-Bischofsheim 49.99228,8.35835
|
|
||||||
Nauheim 49.94192,8.45068
|
|
||||||
Gross-Gerau 49.92421,8.48589
|
|
||||||
Klein-Gerau 49.92004,8.51734
|
|
||||||
Weiterstadt 49.91027,8.57899
|
|
||||||
DarmstadtHbf 49.87254,8.63142
|
|
||||||
Darmstadt-Sued 49.85558,8.63703
|
|
||||||
Wiesbaden-Ost 50.04053,8.25616
|
|
||||||
Mainz-Nord 50.01062,8.24626
|
|
||||||
Mainz-Gustavsburg 49.99454,8.31414
|
|
||||||
Ruesselsheim-Opel 49.98780,8.40201
|
|
||||||
Ruesselsheim 49.99205,8.41375
|
|
||||||
Raunheim 50.01000,8.45704
|
|
||||||
Kelsterbach 50.06329,8.53006
|
|
||||||
F-Stadion 50.06778,8.63561
|
|
||||||
Bad-Soden 50.14283,8.50455 50.14339,8.50457 50.14308,8.50359
|
|
||||||
Sulzbach-Nord 50.13905,8.51788 50.13812,8.51823
|
|
||||||
Schwalbach-Limes 50.15444,8.52819 50.15485,8.52801
|
|
||||||
Schwalbach-Nord 50.15976,8.53459 50.15965,8.53487
|
|
||||||
Niederhoechsstadt 50.15439,8.54707 50.15476,8.54627
|
|
||||||
Eschborn 50.14371,8.56076 50.14323,8.56112
|
|
||||||
Eschborn-Sued 50.13352,8.57863 50.13327,8.57807 50.13441,8.57850
|
|
||||||
Frankfurt-Roedelheim 50.12457,8.60708 50.12431,8.60628 50.12473,8.60800
|
|
||||||
Frankfurt-West 50.11885,8.63981 50.11876,8.63932
|
|
||||||
Frankfurt-Messe 50.11190,8.64354 50.11271,8.64371
|
|
||||||
Frankfurt-Galluswarte 50.10384,8.64487 50.10384,8.64487
|
|
||||||
F-Taunusanlage 50.11325,8.66906
|
|
||||||
F-Hauptwache 50.11393,8.67835
|
|
||||||
F-Konstablerwache 50.11423,8.68621
|
|
||||||
F-Ostendstrasse 50.11226,8.69710
|
|
||||||
F-Lokalbahnhof 50.10198,8.69294
|
|
||||||
Frankfurt-Süd 50.09907,8.68627
|
|
||||||
F-Stresemannallee 50.09455,8.67173
|
|
||||||
F-Louisa 50.08320,8.66947
|
|
||||||
Neu-Isenburg 50.05303,8.66450
|
|
||||||
Dreieich-Buchschlag 50.02230,8.66256
|
|
||||||
Langen-Flugsicherung 50.00559,8.65843
|
|
||||||
Langen 49.99373,8.65786
|
|
||||||
Egelsbach 49.96785,8.65403
|
|
||||||
Erzhausen 49.95128,8.65086
|
|
||||||
D-Wixhausen 49.92889,8.64724
|
|
||||||
D-Arheiligen 49.91424,8.64625
|
|
||||||
F-ZusckschwerdStr 50.10290,8.55181
|
|
||||||
F-Bolognaropalast 50.10140,8.55297
|
|
||||||
F-TillyStr 50.10173,8.56031
|
|
||||||
F-NiedKirche 50.09823,8.56726
|
|
||||||
F-LuthmerStr 50.09907,8.57265
|
|
||||||
F-BirminghamStr 50.10009,8.57790
|
|
||||||
F-Jaegerallee 50.10024,8.59112
|
|
||||||
F-Linnegraben 50.10017,8.59781
|
|
||||||
F-WaldschulStr 50.10061,8.60305
|
|
||||||
F-MoenchhofStr 50.10079,8.61903
|
|
||||||
F-WickererStr 50.10057,8.62240
|
|
||||||
F-RebstoeckerStr 50.10154,8.62866
|
|
||||||
F-SchwalbacherStr 50.10235,8.63628
|
|
||||||
F-SpeyererStr 50.10542,8.65043
|
|
||||||
F-Gueterplatz 50.10751,8.65557
|
|
||||||
F-PlatzDerRepublik 50.10929,8.66073
|
|
||||||
Kronberg 50.17983,8.51726 50.17949,8.51744
|
|
||||||
Kronberg-Sued 50.17289,8.52965 50.17368,8.52843
|
|
||||||
Sulzbach(Taunus) 50.12957,8.52836 50.13012,8.52812
|
|
||||||
Weisskirchen-Steinb 50.17346,8.58782
|
|
||||||
Stierstadt 50.18486,8.58486
|
|
||||||
Oberursel 50.19853,8.58769
|
|
||||||
Bad-Homburg 50.21945,8.62057
|
|
||||||
Seulberg 50.24286,8.64559
|
|
||||||
Friedrichsdorf 50.25200,8.64432
|
|
||||||
Diez-Ost 50.37614,8.03851 50.37512,8.03780
|
|
||||||
Staffel 50.39929,8.04620 50.39838,8.04605
|
|
||||||
Elz 50.41450,8.03848 50.41490,8.03826
|
|
||||||
Niederhadamar 50.43309,8.03737 50.43340,8.03737
|
|
||||||
Hadamar 50.44710,8.04351 50.44771,8.04416
|
|
||||||
Niederzeuzheim 50.46802,8.03966 50.46880,8.03954
|
|
||||||
Frickofen 50.50485,8.03060 50.50485,8.03002
|
|
||||||
Wilsenroth 50.53191,8.03165 50.53076,8.03371
|
|
||||||
Wilmenroth 50.53940,7.98573 50.53940,7.98477
|
|
||||||
Westerburg 50.55747,7.96709 50.55674,7.96668
|
|
||||||
Langenhahn 50.58733,7.91494 50.58761,7.91676
|
|
||||||
Elz-Sued 50.40851,8.03211 50.40845,8.03290
|
|
||||||
Niedererbach 50.42488,7.97713
|
|
||||||
Dreikirchen 50.44986,7.95879
|
|
||||||
Steinefrenz 50.44778,7.93970,50.44737,7.94294
|
|
||||||
Girod 50.45439,7.90259
|
|
||||||
Goldhausen 50.46131,7.87345 50.46185,7.87268
|
|
||||||
Montabaur 50.44479,7.82520 50.44415,7.82538 50.44504,7.83017
|
|
||||||
Dernbach 50.45672,7.78386
|
|
||||||
Wirges 50.46728,7.78485
|
|
||||||
Sirshahn 50.48593,7.77162 50.48597,7.77052
|
|
||||||
Limburg(Lahn) 50.38426,8.06296 50.38495,8.06215
|
|
||||||
Eschofen 50.39337,8.10427 50.39396,8.10594
|
|
||||||
Lindenholzhausen 50.37840,8.13335 50.37768,8.13321
|
|
||||||
Niederbrechen 50.35957,8.15976 50.36031,8.15854
|
|
||||||
Oberbrechen 50.35454,8.18765 50.35524,8.18722
|
|
||||||
Niederselters 50.33249,8.22995 50.33283,8.23066
|
|
||||||
Bad-Camberg 50.29644,8.25516 50.29597,8.25585
|
|
||||||
Woersdorf 50.24550,8.24926 50.24614,8.24910
|
|
||||||
Idstein 50.21591,8.25755 50.21665,8.25795
|
|
||||||
Niedernhausen 50.15972,8.31272 50.16053,8.31281
|
|
||||||
Hofheim 50.08406,8.44493 50.08502,8.44549
|
|
||||||
Unterliederbach 50.10796,8.52798 50.10842,8.52797
|
|
||||||
Liederbach-Sued 50.11538,8.50159 50.11571,8.50118
|
|
||||||
Liederbach 50.11861,8.48631 50.11823,8.48802
|
|
||||||
Kelkheim-Muenster 50.12477,8.46183 50.12517,8.46202
|
|
||||||
Kelkheim 50.13694,8.44781 50.13730,8.44812
|
|
||||||
Kelkheim-Hornau 50.14692,8.44459 50.14770,8.44509
|
|
||||||
Schneidhain 50.17261,8.45209 50.17245,8.45160
|
|
||||||
Koenigstein 50.17758,8.46892 50.17738,8.46989
|
|
||||||
FfmFlughfFbf 50.05297,8.57086 50.05202,8.57030
|
|
||||||
Limburg-Sued 50.38241,8.09621 50.38283,8.09445 50.38349,8.09597
|
|
||||||
Siegburg-Bonn 50.79377,7.20269 50.79446,7.20331
|
|
||||||
Koeln 50.94299,6.95907 50.94220,6.95798
|
|
||||||
Kerkerbach 50.40172,8.13610
|
|
||||||
Runkel 50.40502,8.15961
|
|
||||||
Villmar 50.39477,8.18734
|
|
||||||
Arfurt(Lahn) 50.40644,8.21076
|
|
||||||
Aumenau 50.39990,8.24952
|
|
||||||
Fürfurt 50.42938,8.25411
|
|
||||||
Gräveneck 50.45194,8.25117
|
|
||||||
Weilburg 50.48731,8.26838
|
|
||||||
Löhnberg 50.50920,8.27367
|
|
||||||
Stockhausen(Lahn) 50.54039,8.32492
|
|
||||||
Leun/Braunfels 50.53957,8.36628
|
|
||||||
Solms 50.54520,8.40752
|
|
||||||
Albshausen 50.54561,8.43360
|
|
||||||
Wetzlar 50.56509,8.50360
|
|
||||||
Dutenhofen(Wetzlar) 50.56379,8.59979
|
|
||||||
Gießen 50.57804,8.66280
|
|
||||||
DA-RheinNeckarStrasse 49.87183,8.64463
|
|
||||||
DA-EscholbrueckerStr 49.86537,8.64717
|
|
||||||
DA-PrinzEmilGarten 49.86154,8.64708
|
|
||||||
DA-BessungerStr 49.85836,8.64698
|
|
||||||
DA-LandskronenStr 49.85335,8.64668
|
|
||||||
DA-Marienhoehe 49.84355,8.64609
|
|
||||||
Eberstadt-FriedrichEbert 49.83855,8.64559
|
|
||||||
Eberstadt-CarlUlrich 49.83321,8.64496
|
|
||||||
Eberstadt-VonKetteler 49.82911,8.64520
|
|
||||||
Eberstadt-KatharinenStr 49.82476,8.64482
|
|
||||||
Eberstadt-Wartehalle 49.82027,8.64411
|
|
||||||
Eberstadt-Modaubruecke 49.81739,8.64425
|
|
||||||
Eberstadt-Kirche 49.81429,8.64600
|
|
||||||
Eberstadt-Friedhof 49.81108,8.64603
|
|
||||||
Eberstadt-Frankenstein 49.80744,8.64542
|
|
||||||
Eberstadt-Mittelschneise 49.80234,8.64689
|
|
||||||
Malchen-Seeheim 49.79020,8.64875
|
|
||||||
Seeheim-Wingert 49.77274,8.64814
|
|
||||||
Seeheim-NeuesRathaus 49.76666,8.64623
|
|
||||||
Seeheim-Tannenberg 49.76287,8.64482
|
|
||||||
Jugenheim-LudwigStr 49.75676,8.63487
|
|
||||||
Jugenheim-Bickenbacher 49.75357,8.62937
|
|
||||||
Alsbach-Beuneweg 49.74641,8.62061
|
|
||||||
Alsbach-Hikelstein 49.74028,8.61432
|
|
||||||
|
|
||||||
-- trainline STR11
|
|
||||||
Frankfurt(M)Hbf + 4,14,24,34,44,54_6-22_*
|
|
||||||
F-PlatzDerRepublik +2
|
|
||||||
F-Gueterplatz +4
|
|
||||||
F-SpeyererStr +5
|
|
||||||
Frankfurt-Galluswarte +7
|
|
||||||
F-SchwalbacherStr +9
|
|
||||||
F-RebstoeckerStr +11
|
|
||||||
F-WickererStr +12
|
|
||||||
F-MoenchhofStr +13
|
|
||||||
F-WaldschulStr +15
|
|
||||||
F-Linnegraben +16
|
|
||||||
F-Jaegerallee +17
|
|
||||||
F-BirminghamStr +19
|
|
||||||
F-LuthmerStr +20
|
|
||||||
F-NiedKirche +22
|
|
||||||
F-TillyStr +24
|
|
||||||
F-ZusckschwerdStr +26
|
|
||||||
|
|
||||||
-- trainline STR11
|
|
||||||
F-ZusckschwerdStr + 0,10,20,30,40,50_6-22_*
|
|
||||||
F-Bolognaropalast +1
|
|
||||||
F-TillyStr +3
|
|
||||||
F-NiedKirche +4
|
|
||||||
F-LuthmerStr +5
|
|
||||||
F-BirminghamStr +7
|
|
||||||
F-Jaegerallee +9
|
|
||||||
F-Linnegraben +10
|
|
||||||
F-WaldschulStr +11
|
|
||||||
F-MoenchhofStr +13
|
|
||||||
F-WickererStr +14
|
|
||||||
F-RebstoeckerStr +15
|
|
||||||
F-SchwalbacherStr +17
|
|
||||||
Frankfurt-Galluswarte +19
|
|
||||||
F-SpeyererStr +20
|
|
||||||
F-Gueterplatz +22
|
|
||||||
F-PlatzDerRepublik +24
|
|
||||||
Frankfurt(M)Hbf +26
|
|
||||||
|
|
||||||
-- trainline S3
|
|
||||||
Bad-Soden + 20,50_0,5-23_*
|
|
||||||
Sulzbach-Nord +2
|
|
||||||
Schwalbach-Limes +5
|
|
||||||
Schwalbach-Nord +6
|
|
||||||
Niederhoechsstadt +9
|
|
||||||
Eschborn +11
|
|
||||||
Eschborn-Sued +13
|
|
||||||
Frankfurt-Roedelheim +17
|
|
||||||
Frankfurt-West +20
|
|
||||||
Frankfurt-Messe +22
|
|
||||||
Frankfurt-Galluswarte +24
|
|
||||||
Frankfurt(M)Hbf +26
|
|
||||||
F-Taunusanlage +28
|
|
||||||
F-Hauptwache +30
|
|
||||||
F-Konstablerwache +31
|
|
||||||
F-Ostendstrasse +33
|
|
||||||
F-Lokalbahnhof +35
|
|
||||||
Frankfurt-Süd +37
|
|
||||||
F-Stresemannallee +39
|
|
||||||
F-Louisa +41
|
|
||||||
Neu-Isenburg +44
|
|
||||||
Dreieich-Buchschlag +47
|
|
||||||
Langen-Flugsicherung +49
|
|
||||||
Langen +51
|
|
||||||
Egelsbach +54
|
|
||||||
Erzhausen +56
|
|
||||||
D-Wixhausen +58
|
|
||||||
D-Arheiligen +61
|
|
||||||
DarmstadtHbf +64
|
|
||||||
|
|
||||||
-- trainline S4
|
|
||||||
Kronberg + 8,38_0,5-23_*
|
|
||||||
Kronberg-Sued +2
|
|
||||||
Niederhoechsstadt +6
|
|
||||||
Eschborn +8
|
|
||||||
Eschborn-Sued +10
|
|
||||||
Frankfurt-Roedelheim +14
|
|
||||||
Frankfurt-West +17
|
|
||||||
Frankfurt-Messe +19
|
|
||||||
Frankfurt-Galluswarte +21
|
|
||||||
Frankfurt(M)Hbf +23
|
|
||||||
F-Taunusanlage +25
|
|
||||||
F-Hauptwache +27
|
|
||||||
F-Konstablerwache +28
|
|
||||||
F-Ostendstrasse +30
|
|
||||||
F-Lokalbahnhof +32
|
|
||||||
Frankfurt-Süd +34
|
|
||||||
F-Stresemannallee +36
|
|
||||||
F-Louisa +38
|
|
||||||
Neu-Isenburg +41
|
|
||||||
Dreieich-Buchschlag +44
|
|
||||||
Langen-Flugsicherung +46
|
|
||||||
Langen +48
|
|
||||||
|
|
||||||
-- trainline S3
|
|
||||||
Frankfurt(M)Hbf + 14,44_0,5-23_*
|
|
||||||
Frankfurt-Galluswarte +3
|
|
||||||
Frankfurt-Messe +4
|
|
||||||
Frankfurt-West +6
|
|
||||||
Frankfurt-Roedelheim +9
|
|
||||||
Eschborn-Sued +12
|
|
||||||
Eschborn +15
|
|
||||||
Bad-Soden +26
|
|
||||||
|
|
||||||
-- trainline S4
|
|
||||||
Frankfurt(M)Hbf + 29,59_0,5-23_*
|
|
||||||
Frankfurt-Galluswarte +3
|
|
||||||
Frankfurt-Messe +4
|
|
||||||
Frankfurt-West +6
|
|
||||||
Frankfurt-Roedelheim +9
|
|
||||||
Eschborn-Sued +12
|
|
||||||
Eschborn +15
|
|
||||||
Niederhoechsstadt +17
|
|
||||||
Kronberg-Sued +20
|
|
||||||
Kronberg +22
|
|
||||||
|
|
||||||
-- trainline S5
|
|
||||||
Frankfurt(M)Hbf + 24,54_0,5-23_*
|
|
||||||
Frankfurt-Galluswarte +3
|
|
||||||
Frankfurt-Messe +4
|
|
||||||
Frankfurt-West +6
|
|
||||||
Frankfurt-Roedelheim +9
|
|
||||||
Weisskirchen-Steinb +13
|
|
||||||
Stierstadt +15
|
|
||||||
Oberursel +18
|
|
||||||
Bad-Homburg +21
|
|
||||||
Seulberg +25
|
|
||||||
Friedrichsdorf +26
|
|
||||||
|
|
||||||
-- trainline S5
|
|
||||||
Friedrichsdorf + 8,38_0,5-23_*
|
|
||||||
Seulberg +3
|
|
||||||
Bad-Homburg +7
|
|
||||||
Oberursel +11
|
|
||||||
Stierstadt +13
|
|
||||||
Weisskirchen-Steinb +15
|
|
||||||
Frankfurt-Roedelheim +19
|
|
||||||
Frankfurt-West +22
|
|
||||||
Frankfurt-Messe +24
|
|
||||||
Frankfurt-Galluswarte +26
|
|
||||||
Frankfurt(M)Hbf +29
|
|
||||||
|
|
||||||
-- trainline S8
|
|
||||||
WiesbadenHbf + 19,49_0,5-23_*
|
|
||||||
Wiesbaden-Ost +4
|
|
||||||
Mainz-Nord +8
|
|
||||||
MainzHbf +14
|
|
||||||
Mainz-Roemisches-Th +17
|
|
||||||
Mainz-Gustavsburg +20
|
|
||||||
Mainz-Bischofsheim +23
|
|
||||||
Ruesselsheim-Opel +26
|
|
||||||
Ruesselsheim +29
|
|
||||||
Raunheim +32
|
|
||||||
Kelsterbach +37
|
|
||||||
FfmFlughfFbf +43
|
|
||||||
F-Stadion +47
|
|
||||||
F-Niederrad +50
|
|
||||||
Frankfurt(M)Hbf +54
|
|
||||||
|
|
||||||
|
|
||||||
-- trainline HLB
|
|
||||||
Frankfurt/Hoechst + 28,58_6-23_*
|
|
||||||
Frankfurt(M)Hbf +11
|
|
||||||
|
|
||||||
-- trainline HLB
|
|
||||||
Frankfurt(M)Hbf + 17,47_6-23_*
|
|
||||||
Frankfurt/Hoechst +12
|
|
||||||
Unterliederbach +14
|
|
||||||
Liederbach-Sued +16
|
|
||||||
Liederbach +18
|
|
||||||
Kelkheim-Muenster +21
|
|
||||||
Kelkheim +25
|
|
||||||
Kelkheim-Hornau +27
|
|
||||||
Schneidhain +31
|
|
||||||
Koenigstein +35
|
|
||||||
|
|
||||||
-- trainline HLB
|
|
||||||
Frankfurt/Hoechst + 0,30_5-20_* - 0_9-15_*
|
|
||||||
Sulzbach(Taunus) +6
|
|
||||||
Bad-Soden +9
|
|
||||||
|
|
||||||
-- trainline HLB
|
|
||||||
Bad-Soden + 14,44_5-20_* - 14_9-15_*
|
|
||||||
Sulzbach(Taunus) +3
|
|
||||||
Frankfurt/Hoechst +9
|
|
||||||
|
|
||||||
-- trainline SE60
|
|
||||||
Frankfurt(M)Hbf + 6_0,5-23_*
|
|
||||||
DarmstadtHbf +24
|
|
||||||
Darmstadt-Sued +27
|
|
||||||
Bensheim-Auerbach +50
|
|
||||||
Bensheim +53
|
|
||||||
|
|
||||||
-- trainline SE60
|
|
||||||
Bensheim + 0_0,5-23_*
|
|
||||||
Bensheim-Auerbach +3
|
|
||||||
DarmstadtHbf +30
|
|
||||||
Frankfurt(M)Hbf +48
|
|
||||||
|
|
||||||
-- trainline RE60
|
|
||||||
Frankfurt(M)Hbf + 34_6,8,12,14,16,18,20_1-5
|
|
||||||
Langen +10
|
|
||||||
DarmstadtHbf +20
|
|
||||||
#Bickenbach +29
|
|
||||||
Bensheim +34
|
|
||||||
|
|
||||||
-- trainline RB15707
|
|
||||||
WiesbadenHbf + 38_6-23_*
|
|
||||||
MainzHbf +11
|
|
||||||
Mainz-Roemisches-Th +14
|
|
||||||
Mainz-Bischofsheim +19
|
|
||||||
Nauheim +26
|
|
||||||
Gross-Gerau +29
|
|
||||||
Klein-Gerau +32
|
|
||||||
Weiterstadt +36
|
|
||||||
DarmstadtHbf +43
|
|
||||||
|
|
||||||
-- trainline RE50
|
|
||||||
Frankfurt(M)Hbf + 10_6-23_*
|
|
||||||
F-Niederrad +6
|
|
||||||
Walldorf +15
|
|
||||||
Moerfelden +18
|
|
||||||
Gross-G-Dornberg +24
|
|
||||||
Riedstatt-Goddelau +30
|
|
||||||
Stockstadt-Rhein +33
|
|
||||||
Biebesheim +37
|
|
||||||
Gernsheim +41
|
|
||||||
Gross-Rohrheim +45
|
|
||||||
Biblis +48
|
|
||||||
Buerstadt +53
|
|
||||||
Lampertheim +57
|
|
||||||
Mannheim-Waldhof +62
|
|
||||||
MannheimHbf +69
|
|
||||||
|
|
||||||
-- trainline Vectus
|
|
||||||
Frickofen + 49_6,7,9,11,13,15,17,19_* + 19_6_* + 13_9_* + 6_15_*
|
|
||||||
Niederzeuzheim +6
|
|
||||||
Hadamar +4
|
|
||||||
Niederhadamar +13
|
|
||||||
Elz +16
|
|
||||||
Staffel +19
|
|
||||||
Diez-Ost +23
|
|
||||||
Limburg(Lahn) +26
|
|
||||||
|
|
||||||
-- trainline Vectus
|
|
||||||
Limburg(Lahn) + 8_8,10,12,14,16,18,20_*
|
|
||||||
Diez-Ost +3
|
|
||||||
Staffel +8
|
|
||||||
Elz +11
|
|
||||||
Niederhadamar +14
|
|
||||||
Hadamar +16
|
|
||||||
Niederzeuzheim +20
|
|
||||||
Frickofen +26
|
|
||||||
Wilsenroth +31
|
|
||||||
Wilmenroth +36
|
|
||||||
Westerburg +42
|
|
||||||
Langenhahn +49
|
|
||||||
|
|
||||||
-- trainline Vectus
|
|
||||||
Limburg(Lahn) + 54_7,9,10,11,12,13,14,15,16,17,18,19,20_*
|
|
||||||
Diez-Ost +4
|
|
||||||
Staffel +7
|
|
||||||
Elz-Sued +10
|
|
||||||
Niedererbach +17
|
|
||||||
Dreikirchen +23
|
|
||||||
Steinefrenz +26
|
|
||||||
Girod +30
|
|
||||||
Goldhausen +38
|
|
||||||
Montabaur +46
|
|
||||||
Dernbach +53
|
|
||||||
Wirges +55
|
|
||||||
Sirshahn +59
|
|
||||||
|
|
||||||
|
|
||||||
-- trainline Vectus
|
|
||||||
Sirshahn + 9_7,9,10,11,12,13,14,15,16,17,18,19,20_*
|
|
||||||
Wirges +4
|
|
||||||
Dernbach +7
|
|
||||||
Montabaur +14
|
|
||||||
Goldhausen +21
|
|
||||||
Girod +25
|
|
||||||
Steinefrenz +29
|
|
||||||
Dreikirchen +32
|
|
||||||
Niedererbach +38
|
|
||||||
Elz-Sued +45
|
|
||||||
Staffel +48
|
|
||||||
Diez-Ost +52
|
|
||||||
Limburg(Lahn) +55
|
|
||||||
|
|
||||||
-- trainline SE10
|
|
||||||
Limburg(Lahn) + 18_7-21_*
|
|
||||||
Eschofen +4
|
|
||||||
Lindenholzhausen +8
|
|
||||||
Niederbrechen +12
|
|
||||||
Oberbrechen +15
|
|
||||||
Niederselters +19
|
|
||||||
Bad-Camberg +24
|
|
||||||
Woersdorf +29
|
|
||||||
Idstein +33
|
|
||||||
Niedernhausen +40
|
|
||||||
Hofheim +52
|
|
||||||
Frankfurt/Hoechst +59
|
|
||||||
Frankfurt(M)Hbf +70
|
|
||||||
|
|
||||||
-- trainline HLB
|
|
||||||
Limburg(Lahn) + 23_7-21_*
|
|
||||||
Eschofen +4
|
|
||||||
Kerkerbach +7
|
|
||||||
Runkel +10
|
|
||||||
Villmar +14
|
|
||||||
Arfurt(Lahn) +19
|
|
||||||
Aumenau +23
|
|
||||||
Fürfurt +27
|
|
||||||
Gräveneck +31
|
|
||||||
Weilburg +37
|
|
||||||
Löhnberg +41
|
|
||||||
Stockhausen(Lahn) +46
|
|
||||||
Leun/Braunfels +50
|
|
||||||
Solms +54
|
|
||||||
Albshausen +57
|
|
||||||
Wetzlar +63
|
|
||||||
Dutenhofen(Wetzlar) +69
|
|
||||||
Gießen +75
|
|
||||||
|
|
||||||
-- trainline SE10
|
|
||||||
Frankfurt(M)Hbf + 31_7-22_*
|
|
||||||
Frankfurt/Hoechst +10
|
|
||||||
Hofheim +18
|
|
||||||
Niedernhausen +30
|
|
||||||
Idstein +37
|
|
||||||
Woersdorf +41
|
|
||||||
Bad-Camberg +46
|
|
||||||
Niederselters +50
|
|
||||||
Oberbrechen +54
|
|
||||||
Niederbrechen +57
|
|
||||||
Lindenholzhausen +61
|
|
||||||
Eschofen +65
|
|
||||||
Limburg(Lahn) +69
|
|
||||||
|
|
||||||
|
|
||||||
-- trainline RE1530x
|
|
||||||
Limburg(Lahn) + 55_6,7_*
|
|
||||||
Eschofen +4
|
|
||||||
Frankfurt/Hoechst +52
|
|
||||||
Frankfurt(M)Hbf +63
|
|
||||||
|
|
||||||
-- trainline STR8
|
|
||||||
DA-RheinNeckarStrasse + 16,46_6-23_*
|
|
||||||
DA-EscholbrueckerStr +2
|
|
||||||
DA-PrinzEmilGarten +3
|
|
||||||
DA-BessungerStr +4
|
|
||||||
DA-LandskronenStr +6
|
|
||||||
DA-Marienhoehe +8
|
|
||||||
Eberstadt-FriedrichEbert +9
|
|
||||||
Eberstadt-CarlUlrich +10
|
|
||||||
Eberstadt-VonKetteler +12
|
|
||||||
Eberstadt-KatharinenStr +13
|
|
||||||
Eberstadt-Wartehalle +14
|
|
||||||
Eberstadt-Modaubruecke +16
|
|
||||||
Eberstadt-Kirche +17
|
|
||||||
Eberstadt-Friedhof +18
|
|
||||||
Eberstadt-Frankenstein +20
|
|
||||||
Eberstadt-Mittelschneise +21
|
|
||||||
Malchen-Seeheim +23
|
|
||||||
Seeheim-Wingert +26
|
|
||||||
Seeheim-NeuesRathaus +28
|
|
||||||
Seeheim-Tannenberg +29
|
|
||||||
Jugenheim-LudwigStr +31
|
|
||||||
Jugenheim-Bickenbacher +32
|
|
||||||
Alsbach-Beuneweg +34
|
|
||||||
Alsbach-Hikelstein +36
|
|
1
pom.xml
1
pom.xml
|
@ -17,7 +17,6 @@
|
||||||
<module>brouter-mapaccess</module>
|
<module>brouter-mapaccess</module>
|
||||||
<module>brouter-core</module>
|
<module>brouter-core</module>
|
||||||
<module>brouter-map-creator</module>
|
<module>brouter-map-creator</module>
|
||||||
<module>brouter-mem-router</module>
|
|
||||||
<module>brouter-server</module>
|
<module>brouter-server</module>
|
||||||
<module>brouter-routing-app</module>
|
<module>brouter-routing-app</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
Loading…
Reference in a new issue