performance patches
This commit is contained in:
parent
176beba6f6
commit
46db0104e5
13 changed files with 469 additions and 280 deletions
|
@ -1,172 +0,0 @@
|
|||
/**
|
||||
* Implementation for the open-set
|
||||
* that should be somewhat faster
|
||||
* and memory-efficient than the original
|
||||
* version based on java.util.TreeSet
|
||||
*
|
||||
* It relies on the two double-linked
|
||||
* lists implemented in OsmPath
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.router;
|
||||
|
||||
import btools.mapaccess.OsmNode;
|
||||
|
||||
public class OpenSet
|
||||
{
|
||||
private OsmPath start = new OsmPath();
|
||||
private OsmPath index2 = new OsmPath();
|
||||
|
||||
private int addCount = 0;
|
||||
|
||||
private int size = 0;
|
||||
|
||||
public void clear()
|
||||
{
|
||||
start.nextInSet = null;
|
||||
start.nextInIndexSet = null;
|
||||
index2.nextInIndexSet = null;
|
||||
size = 0;
|
||||
addCount = 0;
|
||||
}
|
||||
|
||||
public void add( OsmPath path )
|
||||
{
|
||||
int ac = path.adjustedCost;
|
||||
OsmPath p1 = index2;
|
||||
|
||||
// fast forward along index2
|
||||
while( p1.nextInIndexSet != null && p1.nextInIndexSet.adjustedCost < ac )
|
||||
{
|
||||
p1 = p1.nextInIndexSet;
|
||||
}
|
||||
if ( p1 == index2 )
|
||||
{
|
||||
p1 = start;
|
||||
}
|
||||
|
||||
// search using index1
|
||||
for(;;)
|
||||
{
|
||||
if ( p1.nextInIndexSet != null && p1.nextInIndexSet.adjustedCost < ac )
|
||||
{
|
||||
p1 = p1.nextInIndexSet;
|
||||
}
|
||||
else if ( p1.nextInSet != null && p1.nextInSet.adjustedCost < ac )
|
||||
{
|
||||
p1 = p1.nextInSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
OsmPath p2 = p1.nextInSet;
|
||||
|
||||
p1.nextInSet = path;
|
||||
path.prevInSet = p1;
|
||||
path.nextInSet = p2;
|
||||
if ( p2 != null ) { p2.prevInSet = path; }
|
||||
size++;
|
||||
|
||||
addCount++;
|
||||
|
||||
// feed random samples to the indices
|
||||
if ( (addCount & 31) == 0 )
|
||||
{
|
||||
addIndex( path, start );
|
||||
}
|
||||
else if ( (addCount & 1023) == 1023 )
|
||||
{
|
||||
addIndex( path, index2 );
|
||||
}
|
||||
}
|
||||
|
||||
public void remove( OsmPath path )
|
||||
{
|
||||
OsmPath p1 = path.prevInSet;
|
||||
OsmPath p2 = path.nextInSet;
|
||||
if ( p1 == null )
|
||||
{
|
||||
return; // not in set
|
||||
}
|
||||
path.prevInSet = null;
|
||||
path.nextInSet = null;
|
||||
if ( p2 != null )
|
||||
{
|
||||
p2.prevInSet = p1;
|
||||
}
|
||||
p1.nextInSet = p2;
|
||||
|
||||
removeIndex( path );
|
||||
|
||||
size--;
|
||||
}
|
||||
|
||||
public OsmPath first()
|
||||
{
|
||||
return start.nextInSet;
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
public int[] getExtract()
|
||||
{
|
||||
int div = size / 1000 + 1;
|
||||
|
||||
int[] res = new int[size/div * 2];
|
||||
int i = 0;
|
||||
int cnt = 0;
|
||||
for( OsmPath p = start.nextInSet; p != null; p = p.nextInSet )
|
||||
{
|
||||
if ( (++cnt) % div == 0 )
|
||||
{
|
||||
OsmNode n = p.getLink().targetNode;
|
||||
res[i++] = n.ilon;
|
||||
res[i++] = n.ilat;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// index operations
|
||||
|
||||
private void addIndex( OsmPath path, OsmPath index )
|
||||
{
|
||||
int ac = path.adjustedCost;
|
||||
OsmPath p1 = index;
|
||||
OsmPath p2 = p1.nextInIndexSet;
|
||||
while( p2 != null && p2.adjustedCost < ac )
|
||||
{
|
||||
p1 = p2;
|
||||
p2 = p2.nextInIndexSet;
|
||||
}
|
||||
p1.nextInIndexSet = path;
|
||||
path.prevInIndexSet = p1;
|
||||
path.nextInIndexSet = p2;
|
||||
if ( p2 != null ) { p2.prevInIndexSet = path; }
|
||||
}
|
||||
|
||||
|
||||
private void removeIndex( OsmPath path )
|
||||
{
|
||||
OsmPath p1 = path.prevInIndexSet;
|
||||
OsmPath p2 = path.nextInIndexSet;
|
||||
if ( p1 == null )
|
||||
{
|
||||
return; // not in set
|
||||
}
|
||||
path.prevInIndexSet = null;
|
||||
path.nextInIndexSet = null;
|
||||
if ( p2 != null )
|
||||
{
|
||||
p2.prevInIndexSet = p1;
|
||||
}
|
||||
p1.nextInIndexSet = p2;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,12 +9,6 @@ import btools.mapaccess.*;
|
|||
|
||||
final class OsmPath implements OsmLinkHolder
|
||||
{
|
||||
// double-linked lists for the openSet
|
||||
public OsmPath nextInSet;
|
||||
public OsmPath prevInSet;
|
||||
public OsmPath nextInIndexSet;
|
||||
public OsmPath prevInIndexSet;
|
||||
|
||||
/**
|
||||
* The cost of that path (a modified distance)
|
||||
*/
|
||||
|
@ -30,12 +24,7 @@ final class OsmPath implements OsmLinkHolder
|
|||
// if the corresponding node has not
|
||||
public short selev;
|
||||
|
||||
public int adjustedCost = 0;
|
||||
|
||||
public void setAirDistanceCostAdjustment( int costAdjustment )
|
||||
{
|
||||
adjustedCost = cost + costAdjustment;
|
||||
}
|
||||
public int airdistance = 0; // distance to endpos
|
||||
|
||||
private OsmNode sourcenode;
|
||||
private OsmLink link;
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.io.FileOutputStream;
|
|||
import java.io.FileWriter;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import btools.mapaccess.OsmPos;
|
||||
import btools.util.CompactLongMap;
|
||||
|
@ -219,7 +220,7 @@ public final class OsmTrack
|
|||
sb.append( " xmlns=\"http://www.topografix.com/GPX/1/1\" \n" );
|
||||
sb.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" );
|
||||
sb.append( " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n" );
|
||||
sb.append( " creator=\"BRouter-1.0.4\" version=\"1.1\">\n" );
|
||||
sb.append( " creator=\"BRouter-1.1\" version=\"1.1\">\n" );
|
||||
sb.append( " <trk>\n" );
|
||||
sb.append(" <name>").append(name).append("</name>\n");
|
||||
sb.append( " <trkseg>\n" );
|
||||
|
@ -286,6 +287,8 @@ public final class OsmTrack
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public List<String> iternity;
|
||||
|
||||
public String formatAsGeoJson()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(8192);
|
||||
|
@ -296,13 +299,24 @@ public final class OsmTrack
|
|||
sb.append( " {\n" );
|
||||
sb.append( " \"type\": \"Feature\",\n" );
|
||||
sb.append( " \"properties\": {\n" );
|
||||
sb.append( " \"creator\": \"BRouter-1.0.4\",\n" );
|
||||
sb.append( " \"creator\": \"BRouter-1.1\",\n" );
|
||||
sb.append( " \"name\": \"" ).append( name ).append( "\",\n" );
|
||||
sb.append( " \"track-length\": \"" ).append( distance ).append( "\",\n" );
|
||||
sb.append( " \"filtered ascend\": \"" ).append( ascend ).append( "\",\n" );
|
||||
sb.append( " \"plain-ascend\": \"" ).append( plainAscend ).append( "\",\n" );
|
||||
sb.append( " \"cost\": \"" ).append( cost ).append( "\"\n" );
|
||||
sb.append( " },\n" );
|
||||
|
||||
if ( iternity != null )
|
||||
{
|
||||
sb.append( " \"iternity\": [\n" );
|
||||
for( String s : iternity )
|
||||
{
|
||||
sb.append( " \"").append( s ).append( "\",\n" );
|
||||
}
|
||||
sb.deleteCharAt( sb.lastIndexOf( "," ) );
|
||||
sb.append( " ],\n" );
|
||||
}
|
||||
sb.append( " \"geometry\": {\n" );
|
||||
sb.append( " \"type\": \"LineString\",\n" );
|
||||
sb.append( " \"coordinates\": [\n" );
|
||||
|
|
|
@ -40,6 +40,8 @@ public final class RoutingContext implements DistanceChecker
|
|||
public BExpressionContext expctxWay;
|
||||
public BExpressionContext expctxNode;
|
||||
|
||||
public boolean serversizing = false;
|
||||
|
||||
public int downhillcostdiv;
|
||||
public int downhillcutoff;
|
||||
public int uphillcostdiv;
|
||||
|
@ -52,6 +54,12 @@ public final class RoutingContext implements DistanceChecker
|
|||
public int elevationmaxbuffer;
|
||||
public int elevationbufferreduce;
|
||||
|
||||
public double cost1speed;
|
||||
public double additionalcostfactor;
|
||||
public double changetime;
|
||||
public double buffertime;
|
||||
public double waittimeadjustment;
|
||||
|
||||
public void readGlobalConfig( BExpressionContext expctxGlobal )
|
||||
{
|
||||
downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
|
||||
|
@ -67,6 +75,12 @@ public final class RoutingContext implements DistanceChecker
|
|||
elevationpenaltybuffer = (int)(expctxGlobal.getVariableValue( "elevationpenaltybuffer", 5.f )*1000000);
|
||||
elevationmaxbuffer = (int)(expctxGlobal.getVariableValue( "elevationmaxbuffer", 10.f )*1000000);
|
||||
elevationbufferreduce = (int)(expctxGlobal.getVariableValue( "elevationbufferreduce", 0.f )*10000);
|
||||
|
||||
cost1speed = expctxGlobal.getVariableValue( "cost1speed", 22.f );
|
||||
additionalcostfactor = expctxGlobal.getVariableValue( "additionalcostfactor", 1.5f );
|
||||
changetime = expctxGlobal.getVariableValue( "changetime", 180.f );
|
||||
buffertime = expctxGlobal.getVariableValue( "buffertime", 120.f );
|
||||
waittimeadjustment = expctxGlobal.getVariableValue( "waittimeadjustment", 0.9f );
|
||||
}
|
||||
|
||||
public RoutingMessageHandler messageHandler = new RoutingMessageHandler();
|
||||
|
|
|
@ -15,32 +15,33 @@ import btools.mapaccess.OsmLink;
|
|||
import btools.mapaccess.OsmLinkHolder;
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmNodesMap;
|
||||
import btools.util.SortedHeap;
|
||||
|
||||
public class RoutingEngine extends Thread
|
||||
{
|
||||
private OsmNodesMap nodesMap;
|
||||
private NodesCache nodesCache;
|
||||
private OpenSet openSet = new OpenSet();
|
||||
private SortedHeap<OsmPath> openSet = new SortedHeap<OsmPath>();
|
||||
private boolean finished = false;
|
||||
|
||||
private List<OsmNodeNamed> waypoints = null;
|
||||
protected List<OsmNodeNamed> waypoints = null;
|
||||
private int linksProcessed = 0;
|
||||
|
||||
private OsmTrack foundTrack = new OsmTrack();
|
||||
protected OsmTrack foundTrack = new OsmTrack();
|
||||
private OsmTrack foundRawTrack = null;
|
||||
private int alternativeIndex = 0;
|
||||
|
||||
private String errorMessage = null;
|
||||
protected String errorMessage = null;
|
||||
|
||||
private volatile boolean terminated;
|
||||
|
||||
private File profileDir;
|
||||
private String segmentDir;
|
||||
protected File profileDir;
|
||||
protected String segmentDir;
|
||||
private String outfileBase;
|
||||
private String logfileBase;
|
||||
private boolean infoLogEnabled;
|
||||
private Writer infoLogWriter;
|
||||
private RoutingContext routingContext;
|
||||
protected RoutingContext routingContext;
|
||||
|
||||
private double airDistanceCostFactor;
|
||||
private OsmTrack guideTrack;
|
||||
|
@ -80,8 +81,8 @@ public class RoutingEngine extends Thread
|
|||
BExpressionMetaData meta = new BExpressionMetaData();
|
||||
|
||||
BExpressionContext expctxGlobal = new BExpressionContext( "global", meta );
|
||||
rc.expctxWay = new BExpressionContext( "way", meta );
|
||||
rc.expctxNode = new BExpressionContext( "node", 1024, meta );
|
||||
rc.expctxWay = new BExpressionContext( "way", rc.serversizing ? 262144 : 4096, meta );
|
||||
rc.expctxNode = new BExpressionContext( "node", rc.serversizing ? 16384 : 1024, meta );
|
||||
|
||||
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
|
||||
|
||||
|
@ -504,7 +505,6 @@ public class RoutingEngine extends Thread
|
|||
if ( pe.cost >= costdelta )
|
||||
{
|
||||
pe.cost -= costdelta;
|
||||
pe.adjustedCost -= costdelta;
|
||||
|
||||
if ( guideTrack != null )
|
||||
{
|
||||
|
@ -559,7 +559,7 @@ public class RoutingEngine extends Thread
|
|||
|
||||
wp.radius = 1e9;
|
||||
OsmPath testPath = new OsmPath( null, startPath, link, null, guideTrack != null, routingContext );
|
||||
testPath.setAirDistanceCostAdjustment( (int)( nextNode.calcDistance( endPos ) * airDistanceCostFactor ) );
|
||||
testPath.airdistance = nextNode.calcDistance( endPos );
|
||||
if ( wp.radius < minradius )
|
||||
{
|
||||
bestPath = testPath;
|
||||
|
@ -593,10 +593,7 @@ public class RoutingEngine extends Thread
|
|||
|
||||
if ( wp != null ) wp.radius = 1e-5;
|
||||
|
||||
OsmPath testPath = new OsmPath( n1, startPath, link, null, guideTrack != null, routingContext );
|
||||
testPath.setAirDistanceCostAdjustment( 0 );
|
||||
|
||||
return testPath;
|
||||
return new OsmPath( n1, startPath, link, null, guideTrack != null, routingContext );
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -636,8 +633,6 @@ public class RoutingEngine extends Thread
|
|||
OsmPath startPath1 = getStartPath( start1, start2, startWp, endWp, sameSegmentSearch );
|
||||
OsmPath startPath2 = getStartPath( start2, start1, startWp, endWp, sameSegmentSearch );
|
||||
|
||||
int maxAdjCostFromQueue = 0;
|
||||
|
||||
// check for an INITIAL match with the cost-cutting-track
|
||||
if ( costCuttingTrack != null )
|
||||
{
|
||||
|
@ -655,8 +650,8 @@ public class RoutingEngine extends Thread
|
|||
synchronized( openSet )
|
||||
{
|
||||
openSet.clear();
|
||||
if ( startPath1.cost >= 0 ) openSet.add( startPath1 );
|
||||
if ( startPath2.cost >= 0 ) openSet.add( startPath2 );
|
||||
addToOpenset( startPath1 );
|
||||
addToOpenset( startPath2 );
|
||||
}
|
||||
while(!terminated)
|
||||
{
|
||||
|
@ -671,16 +666,10 @@ public class RoutingEngine extends Thread
|
|||
OsmPath path = null;
|
||||
synchronized( openSet )
|
||||
{
|
||||
if ( openSet.size() == 0 ) break;
|
||||
path = openSet.first();
|
||||
openSet.remove( path );
|
||||
path = openSet.popLowestKeyValue();
|
||||
}
|
||||
|
||||
if ( path.adjustedCost < maxAdjCostFromQueue && airDistanceCostFactor == 0.)
|
||||
{
|
||||
throw new RuntimeException( "assertion failed: path.adjustedCost < maxAdjCostFromQueue: " + path.adjustedCost + "<" + maxAdjCostFromQueue );
|
||||
}
|
||||
maxAdjCostFromQueue = path.adjustedCost;
|
||||
if ( path == null ) break;
|
||||
if ( path.airdistance == -1 ) continue;
|
||||
|
||||
if ( matchPath != null && fastPartialRecalc && firstMatchCost < 500 && path.cost > 30L*firstMatchCost )
|
||||
{
|
||||
|
@ -740,8 +729,7 @@ public class RoutingEngine extends Thread
|
|||
}
|
||||
|
||||
// recheck cutoff before doing expensive stuff
|
||||
int airDistance2 = currentNode.calcDistance( endPos );
|
||||
if ( path.cost + airDistance2 > maxTotalCost + 10 )
|
||||
if ( path.cost + path.airdistance > maxTotalCost + 10 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -824,18 +812,14 @@ public class RoutingEngine extends Thread
|
|||
}
|
||||
if ( otherPath != path )
|
||||
{
|
||||
synchronized( openSet )
|
||||
{
|
||||
openSet.remove( otherPath );
|
||||
}
|
||||
otherPath.airdistance = -1; // invalidate the entry in the open set
|
||||
}
|
||||
}
|
||||
if ( bestPath != null )
|
||||
{
|
||||
int airDistance = isFinalLink ? 0 : nextNode.calcDistance( endPos );
|
||||
bestPath.setAirDistanceCostAdjustment( (int)( airDistance * airDistanceCostFactor ) );
|
||||
bestPath.airdistance = isFinalLink ? 0 : nextNode.calcDistance( endPos );
|
||||
|
||||
if ( isFinalLink || bestPath.cost + airDistance <= maxTotalCost + 10 )
|
||||
if ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 10 )
|
||||
{
|
||||
// add only if this may beat an existing path for that link
|
||||
OsmLinkHolder dominator = link.firstlinkholder;
|
||||
|
@ -854,7 +838,7 @@ public class RoutingEngine extends Thread
|
|||
link.addLinkHolder( bestPath );
|
||||
synchronized( openSet )
|
||||
{
|
||||
openSet.add( bestPath );
|
||||
addToOpenset( bestPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -870,6 +854,14 @@ public class RoutingEngine extends Thread
|
|||
return null;
|
||||
}
|
||||
|
||||
private void addToOpenset( OsmPath path )
|
||||
{
|
||||
if ( path.cost >= 0 )
|
||||
{
|
||||
openSet.add( path.cost + (int)(path.airdistance*airDistanceCostFactor), path );
|
||||
}
|
||||
}
|
||||
|
||||
private void preloadPosition( OsmNode n, int minRingWidth, int minCount )
|
||||
{
|
||||
int c = 0;
|
||||
|
@ -991,7 +983,16 @@ public class RoutingEngine extends Thread
|
|||
{
|
||||
synchronized( openSet )
|
||||
{
|
||||
return openSet.getExtract();
|
||||
List<OsmPath> extract = openSet.getExtract();
|
||||
int[] res = new int[extract.size() * 2];
|
||||
int i = 0;
|
||||
for( OsmPath p : extract )
|
||||
{
|
||||
OsmNode n = p.getLink().targetNode;
|
||||
res[i++] = n.ilon;
|
||||
res[i++] = n.ilat;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1044,4 +1045,10 @@ public class RoutingEngine extends Thread
|
|||
{
|
||||
terminated = true;
|
||||
}
|
||||
|
||||
public boolean isTerminated()
|
||||
{
|
||||
return terminated;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,8 +50,12 @@ public final class BExpressionContext
|
|||
|
||||
// hash-cache for function results
|
||||
private byte[][] _arrayBitmap;
|
||||
private boolean[] _arrayInverse;
|
||||
private int[] _arrayCrc;
|
||||
|
||||
private int currentHashBucket = -1;
|
||||
private byte[] currentByteArray = null;
|
||||
private boolean currentInverseDirection= false;
|
||||
|
||||
public List<BExpression> expressionList;
|
||||
|
||||
|
@ -105,6 +109,8 @@ public final class BExpressionContext
|
|||
if ( Boolean.getBoolean( "disableExpressionCache" ) ) hashSize = 1;
|
||||
|
||||
_arrayBitmap = new byte[hashSize][];
|
||||
_arrayInverse = new boolean[hashSize];
|
||||
_arrayCrc = new int[hashSize];
|
||||
|
||||
_arrayCostfactor = new float[hashSize];
|
||||
_arrayTurncost = new float[hashSize];
|
||||
|
@ -163,7 +169,7 @@ public final class BExpressionContext
|
|||
|
||||
// crosscheck: decode and compare
|
||||
int[] ld2 = new int[lookupValues.size()];
|
||||
decode( ld2, ab );
|
||||
decode( ld2, false, ab );
|
||||
for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names
|
||||
{
|
||||
if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding " + getKeyValueDescription(false, ab) );
|
||||
|
@ -208,21 +214,21 @@ public final class BExpressionContext
|
|||
*/
|
||||
public void decode( byte[] ab )
|
||||
{
|
||||
decode( lookupData, ab );
|
||||
decode( lookupData, false, ab );
|
||||
lookupDataValid = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* decode a byte-array into a lookup data array
|
||||
*/
|
||||
public void decode( int[] ld, byte[] ab )
|
||||
private void decode( int[] ld, boolean inverseDirection, byte[] ab )
|
||||
{
|
||||
if ( !meta.readVarLength ) { decodeFix( ld, ab ); return; }
|
||||
|
||||
BitCoderContext ctx = new BitCoderContext(ab);
|
||||
|
||||
// start with first bit hardwired ("reversedirection")
|
||||
ld[0] = ctx.decodeBit() ? 2 : 0;
|
||||
ld[0] = inverseDirection ^ ctx.decodeBit() ? 2 : 0;
|
||||
|
||||
// all others are generic
|
||||
int inum = 1;
|
||||
|
@ -276,13 +282,10 @@ public final class BExpressionContext
|
|||
public String getKeyValueDescription( boolean inverseDirection, byte[] ab )
|
||||
{
|
||||
int inverseBitByteIndex = meta.readVarLength ? 0 : 7;
|
||||
int abLen = ab.length;
|
||||
byte[] ab_copy = new byte[abLen];
|
||||
System.arraycopy( ab, 0, ab_copy, 0 , abLen );
|
||||
if ( inverseDirection ) ab_copy[inverseBitByteIndex] ^= 1;
|
||||
// int abLen = ab.length;
|
||||
|
||||
StringBuilder sb = new StringBuilder( 200 );
|
||||
decode( lookupData, ab_copy );
|
||||
decode( lookupData, inverseDirection, ab );
|
||||
for( int inum = 0; inum < lookupValues.size(); inum++ ) // loop over lookup names
|
||||
{
|
||||
BExpressionLookupValue[] va = lookupValues.get(inum);
|
||||
|
@ -344,7 +347,9 @@ public final class BExpressionContext
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public long requests;
|
||||
public long requests2;
|
||||
public long cachemisses;
|
||||
|
||||
/**
|
||||
* evaluates the data in the given byte array
|
||||
|
@ -353,55 +358,53 @@ public final class BExpressionContext
|
|||
*/
|
||||
public boolean evaluate( boolean inverseDirection, byte[] ab, BExpressionReceiver receiver )
|
||||
{
|
||||
requests ++;
|
||||
lookupDataValid = false; // this is an assertion for a nasty pifall
|
||||
|
||||
int inverseBitByteIndex = meta.readVarLength ? 0 : 7;
|
||||
|
||||
int abLen = ab.length;
|
||||
boolean equalsCurrent = currentHashBucket >= 0 && abLen == currentByteArray.length;
|
||||
if ( equalsCurrent )
|
||||
// calc hash bucket from crc
|
||||
int lastHashBucket = currentHashBucket;
|
||||
int crc = Crc32.crcWithInverseBit(ab, inverseDirection ? inverseBitByteIndex : -1 );
|
||||
int hashSize = _arrayBitmap.length;
|
||||
currentHashBucket = (crc & 0xfffffff) % hashSize;
|
||||
currentByteArray = ab;
|
||||
currentInverseDirection = inverseDirection;
|
||||
byte[] abBucket = _arrayBitmap[currentHashBucket];
|
||||
boolean inverseBucket = _arrayInverse[currentHashBucket];
|
||||
if ( ab == abBucket && inverseBucket == inverseDirection ) // fast identity check
|
||||
{
|
||||
return lastHashBucket == currentHashBucket;
|
||||
}
|
||||
requests2++;
|
||||
|
||||
// compare input value to hash bucket content
|
||||
boolean hashBucketEquals = false;
|
||||
if ( crc == _arrayCrc[currentHashBucket] )
|
||||
{
|
||||
int abLen = ab.length;
|
||||
if ( abBucket != null && abBucket.length == ab.length )
|
||||
{
|
||||
hashBucketEquals = true;
|
||||
boolean isInverse = inverseDirection ^ inverseBucket;
|
||||
for( int i=0; i<abLen; i++ )
|
||||
{
|
||||
byte b = ab[i];
|
||||
if ( i == inverseBitByteIndex && inverseDirection ) b ^= 1;
|
||||
if ( b != currentByteArray[i] ) { equalsCurrent = false; break; }
|
||||
if ( isInverse && i == inverseBitByteIndex ) b ^= 1;
|
||||
if ( abBucket[i] != b ) { hashBucketEquals = false; break; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( equalsCurrent )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calc hash bucket from crc
|
||||
int crc = Crc32.crc( abBuf, 0, abLen );
|
||||
int hashSize = _arrayBitmap.length;
|
||||
currentHashBucket = (crc & 0xfffffff) % hashSize;
|
||||
currentByteArray = new byte[abLen];
|
||||
System.arraycopy( ab, 0, currentByteArray, 0 , abLen );
|
||||
if ( inverseDirection ) currentByteArray[inverseBitByteIndex] ^= 1;
|
||||
}
|
||||
|
||||
boolean hashBucketEquals = false;
|
||||
byte[] abBucket = _arrayBitmap[currentHashBucket];
|
||||
if ( abBucket != null && abBucket.length == abLen )
|
||||
{
|
||||
hashBucketEquals = true;
|
||||
for( int i=0; i<abLen; i++ )
|
||||
{
|
||||
if ( abBucket[i] != currentByteArray[i] ) { hashBucketEquals = false; break; }
|
||||
}
|
||||
}
|
||||
if ( hashBucketEquals ) return false;
|
||||
if ( hashBucketEquals ) return lastHashBucket == currentHashBucket;
|
||||
cachemisses++;
|
||||
|
||||
_arrayBitmap[currentHashBucket] = currentByteArray;
|
||||
_arrayInverse[currentHashBucket] = currentInverseDirection;
|
||||
_arrayCrc[currentHashBucket] = crc;
|
||||
|
||||
_receiver = receiver;
|
||||
|
||||
decode( lookupData, currentByteArray );
|
||||
decode( lookupData, currentInverseDirection, currentByteArray );
|
||||
evaluate( lookupData );
|
||||
|
||||
_arrayCostfactor[currentHashBucket] = variableData[costfactorIdx];
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="4"
|
||||
android:versionName="1.0.2" package="btools.routingapp">
|
||||
android:versionCode="5"
|
||||
android:versionName="1.1" package="btools.routingapp">
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||
<activity android:name=".BRouterActivity"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -536,7 +536,7 @@ private long startTime = 0L;
|
|||
}
|
||||
else
|
||||
{
|
||||
String result = "version = BRouter-1.0.4\n"
|
||||
String result = "version = BRouter-1.1\n"
|
||||
+ "distance = " + cr.getDistance()/1000. + " km\n"
|
||||
+ "filtered ascend = " + cr.getAscend() + " m\n"
|
||||
+ "plain ascend = " + cr.getPlainAscend();
|
||||
|
|
|
@ -2,19 +2,16 @@ package btools.server;
|
|||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmTrack;
|
||||
|
@ -129,7 +126,7 @@ public class RouteServer extends Thread
|
|||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
System.out.println("BRouter 1.0.4 / 28092014");
|
||||
System.out.println("BRouter 1.1 / 27122014");
|
||||
if ( args.length != 5 )
|
||||
{
|
||||
System.out.println("serve BRouter protocol");
|
||||
|
|
|
@ -41,6 +41,7 @@ public class ServerHandler extends RequestHandler {
|
|||
public RoutingContext readRoutingContext()
|
||||
{
|
||||
rc = new RoutingContext();
|
||||
rc.serversizing = true;
|
||||
|
||||
String profile = params.get( "profile" );
|
||||
// when custom profile replace prefix with directory path
|
||||
|
|
|
@ -30,6 +30,19 @@ public class Crc32
|
|||
return crc;
|
||||
}
|
||||
|
||||
public static int crcWithInverseBit( byte[] ab, int inverseBitByteIndex )
|
||||
{
|
||||
int crc = 0xFFFFFFFF;
|
||||
int end = ab.length;
|
||||
for( int j=0; j<end; j++ )
|
||||
{
|
||||
byte b = ab[j];
|
||||
if ( j == inverseBitByteIndex ) b ^= 1;
|
||||
crc = (crc >>> 8) ^ crctable[(crc ^ b) & 0xff];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
private static int[] crctable = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
|
|
258
brouter-util/src/main/java/btools/util/SortedHeap.java
Normal file
258
brouter-util/src/main/java/btools/util/SortedHeap.java
Normal file
|
@ -0,0 +1,258 @@
|
|||
package btools.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Memory efficient Heap to get the lowest-key value
|
||||
* of a set of key-object pairs
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class SortedHeap<V>
|
||||
{
|
||||
private int[][] al;
|
||||
private int[] pa;
|
||||
private int[] lp; // the low pointers
|
||||
|
||||
private Object[][] vla; // value list array
|
||||
|
||||
protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE
|
||||
|
||||
private int size;
|
||||
private boolean isClear = false;
|
||||
|
||||
public SortedHeap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lowest key value, or null if none
|
||||
*/
|
||||
public V popLowestKeyValue()
|
||||
{
|
||||
int minId = 0;
|
||||
int minIdx = -1;
|
||||
for ( int i=1;; i++ )
|
||||
{
|
||||
int[] ali = al[i];
|
||||
if ( ali == null ) break;
|
||||
int lpi = lp[i];
|
||||
if ( lpi < ali.length )
|
||||
{
|
||||
int currentId = ali[lpi];
|
||||
if ( minIdx < 0 || currentId < minId )
|
||||
{
|
||||
minIdx = i;
|
||||
minId = currentId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( minIdx == -1 ) return null;
|
||||
|
||||
int lp_minIdx = lp[minIdx]++;
|
||||
Object[] vla_minIdx = vla[minIdx];
|
||||
V res =(V)vla_minIdx[lp_minIdx];
|
||||
vla_minIdx[lp_minIdx] = null;
|
||||
size--;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a key value pair to the heap
|
||||
*
|
||||
* @param id the key to insert
|
||||
* @param value the value to insert object
|
||||
*/
|
||||
public void add( int key, V value )
|
||||
{
|
||||
isClear = false;
|
||||
size++;
|
||||
|
||||
// trivial shortcut if first array empty
|
||||
if ( lp[1] == 1)
|
||||
{
|
||||
al[1][0] = key;
|
||||
vla[1][0] = value;
|
||||
lp[1] = 0;
|
||||
return;
|
||||
}
|
||||
// trivial shortcut if second array empty
|
||||
if ( lp[2] > 0 )
|
||||
{
|
||||
int[] al2 = al[2];
|
||||
Object[] vla2 = vla[2];
|
||||
int key1;
|
||||
Object val1;
|
||||
if ( lp[2] == 2 )
|
||||
{
|
||||
key1 = al[1][0];
|
||||
val1 = vla[1][0];
|
||||
lp[1] = 1;
|
||||
}
|
||||
else // == 1
|
||||
{
|
||||
key1 = al2[1];
|
||||
val1 = vla2[1];
|
||||
}
|
||||
lp[2] = 0;
|
||||
if ( key1 < key )
|
||||
{
|
||||
al2[0] = key1;
|
||||
vla2[0] = val1;
|
||||
al2[1] = key;
|
||||
vla2[1] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
al2[1] = key1;
|
||||
vla2[1] = val1;
|
||||
al2[0] = key;
|
||||
vla2[0] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// put the new entry in the first array
|
||||
al[0][0] = key;
|
||||
vla[0][0] = value;
|
||||
|
||||
pa[0] = 1;
|
||||
pa[1] = 1;
|
||||
pa[2] = 2;
|
||||
|
||||
// determine the first array big enough to take them all
|
||||
int cnt = 4; // value count up to idx
|
||||
int idx = 3;
|
||||
int n = 4;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
cnt += n-lp[idx];
|
||||
if ( cnt <= n ) break;
|
||||
pa[idx++] = n;
|
||||
n <<= 1;
|
||||
}
|
||||
|
||||
if ( idx == MAXLISTS )
|
||||
{
|
||||
throw new IllegalArgumentException( "overflow" );
|
||||
}
|
||||
|
||||
// create it if not existant
|
||||
if ( al[idx] == null )
|
||||
{
|
||||
al[idx] = new int[n];
|
||||
vla[idx] = new Object[n];
|
||||
}
|
||||
|
||||
int[] al_t = al[idx];
|
||||
Object[] vla_t = vla[idx];
|
||||
int lp_t = lp[idx];
|
||||
|
||||
// shift down content if any
|
||||
if ( lp_t < n )
|
||||
{
|
||||
System.arraycopy(al_t, lp_t, al_t, 0, n-lp_t);
|
||||
System.arraycopy(vla_t, lp_t, vla_t, 0, n-lp_t);
|
||||
}
|
||||
lp[idx] = 0;
|
||||
pa[idx] = n - lp_t;
|
||||
|
||||
|
||||
// now merge the contents of arrays 0...idx-1 into idx
|
||||
while ( cnt > 0 )
|
||||
{
|
||||
int maxId = 0;
|
||||
int maxIdx = -1;
|
||||
|
||||
for ( int i=0; i<=idx; i++ )
|
||||
{
|
||||
int p = pa[i];
|
||||
if ( p > lp[i] )
|
||||
{
|
||||
int currentId = al[i][p-1];
|
||||
if ( maxIdx < 0 || currentId > maxId )
|
||||
{
|
||||
maxIdx = i;
|
||||
maxId = currentId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// current maximum found, copy to target array
|
||||
--n;
|
||||
al[idx][n] = maxId;
|
||||
vla[idx][n] = vla[maxIdx][pa[maxIdx]-1];
|
||||
|
||||
--cnt;
|
||||
--pa[maxIdx];
|
||||
}
|
||||
lp[idx] = n;
|
||||
while(--idx > 0) lp[idx] = al[idx].length;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
if ( !isClear )
|
||||
{
|
||||
isClear = true;
|
||||
size = 0;
|
||||
|
||||
// pointer array
|
||||
pa = new int[MAXLISTS];
|
||||
|
||||
lp = new int[MAXLISTS];
|
||||
|
||||
// allocate key lists
|
||||
al = new int[MAXLISTS][];
|
||||
al[0] = new int[1]; // make the first arrays
|
||||
al[1] = new int[1];
|
||||
al[2] = new int[2];
|
||||
|
||||
// same for the values
|
||||
vla = new Object[MAXLISTS][];
|
||||
vla[0] = new Object[1];
|
||||
vla[1] = new Object[1];
|
||||
vla[2] = new Object[2];
|
||||
|
||||
int n = 1;
|
||||
lp[0] = 0;
|
||||
for( int idx=1; idx < MAXLISTS; idx++ )
|
||||
{
|
||||
lp[idx] = n;
|
||||
n <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<V> getExtract()
|
||||
{
|
||||
int div = size / 1000 + 1;
|
||||
|
||||
ArrayList<V> res = new ArrayList<V>(size / div );
|
||||
int cnt = 0;
|
||||
for ( int i=1;; i++ )
|
||||
{
|
||||
int[] ali = al[i];
|
||||
if ( ali == null ) break;
|
||||
int lpi = lp[i];
|
||||
Object[] vlai = vla[i];
|
||||
int n = ali.length;
|
||||
while ( lpi < n )
|
||||
{
|
||||
if ( (++cnt) % div == 0 )
|
||||
{
|
||||
res.add( (V)vla[i][lpi] );
|
||||
}
|
||||
lpi++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
65
brouter-util/src/test/java/btools/util/SortedHeapTest.java
Normal file
65
brouter-util/src/test/java/btools/util/SortedHeapTest.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package btools.util;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SortedHeapTest
|
||||
{
|
||||
@Test
|
||||
public void sortedHeapTest1()
|
||||
{
|
||||
SortedHeap<String> sh = new SortedHeap<String>();
|
||||
Random rnd = new Random();
|
||||
for( int i = 0; i< 100000; i++ )
|
||||
{
|
||||
int val = rnd.nextInt( 1000000 );
|
||||
sh.add( val, "" + val );
|
||||
val = rnd.nextInt( 1000000 );
|
||||
sh.add( val, "" + val );
|
||||
sh.popLowestKeyValue();
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
int lastval = 0;
|
||||
for(;;)
|
||||
{
|
||||
String s = sh.popLowestKeyValue();
|
||||
if ( s == null ) break;
|
||||
cnt ++;
|
||||
int val = Integer.parseInt( s );
|
||||
Assert.assertTrue( "sorting test", val >= lastval );
|
||||
lastval = val;
|
||||
}
|
||||
Assert.assertTrue( "total count test", cnt == 100000 );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortedHeapTest2()
|
||||
{
|
||||
SortedHeap<String> sh = new SortedHeap<String>();
|
||||
Random rnd = new Random();
|
||||
for( int i = 0; i< 100000; i++ )
|
||||
{
|
||||
sh.add( i, "" + i );
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
int expected = 0;
|
||||
for(;;)
|
||||
{
|
||||
String s = sh.popLowestKeyValue();
|
||||
if ( s == null ) break;
|
||||
cnt ++;
|
||||
int val = Integer.parseInt( s );
|
||||
Assert.assertTrue( "sequence test", val == expected );
|
||||
expected++;
|
||||
}
|
||||
Assert.assertTrue( "total count test", cnt == 100000 );
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue