diff --git a/brouter-codec/pom.xml b/brouter-codec/pom.xml index 6436e1e..daf0ce4 100644 --- a/brouter-codec/pom.xml +++ b/brouter-codec/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-codec diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java index 01675d0..79291bb 100644 --- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java +++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java @@ -56,6 +56,8 @@ public final class MicroCache2 extends MicroCache faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2; fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3; + + int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon; int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat; @@ -73,7 +75,9 @@ public final class MicroCache2 extends MicroCache int netdatasize = bc.decodeNoisyNumber( 10 ); ab = netdatasize > dataBuffers.bbuf1.length ? new byte[netdatasize] : dataBuffers.bbuf1; aboffset = 0; - BitSet validNodes = new BitSet( size ); + + int[] validBits = new int[(size+31)>>5]; + int finaldatasize = 0; LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 ); @@ -130,12 +134,12 @@ public final class MicroCache2 extends MicroCache writeVarLengthSigned( dlon_remaining ); writeVarLengthSigned( dlat_remaining ); - validNodes.set( n, true ); // mark source-node valid + validBits[ n >> 5 ] |= 1 << n; // mark source-node valid if ( nodeIdx != n ) // valid internal (forward-) link { reverseLinks.addDataElement( nodeIdx, n ); // register reverse link finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse - validNodes.set( nodeIdx, true ); // mark target-node valid + validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid } writeModeAndDesc( isReverse, wayTags.data ); } @@ -179,15 +183,16 @@ public final class MicroCache2 extends MicroCache // calculate final data size int finalsize = 0; + int startpos = 0; for( int i=0; i 0 ? fapos[i-1] : 0; int endpos = fapos[i]; - if ( validNodes.get( i ) ) + if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 ) { finaldatasize += endpos-startpos; finalsize++; } + startpos = endpos; } // append the reverse links at the end of each node byte[] abOld = ab; @@ -200,34 +205,36 @@ public final class MicroCache2 extends MicroCache aboffset = 0; size = 0; - for( int n=0; n 0 ? faposOld[n-1] : 0; int endpos = faposOld[n]; - int len = endpos-startpos; - System.arraycopy( abOld, startpos, ab, aboffset, len ); - if ( debug ) System.out.println( "*** copied " + len + " bytes from " + aboffset + " for node " + n ); - aboffset += len; - - int cnt = reverseLinks.initList( n ); - if ( debug ) System.out.println( "*** appending " + cnt + " reverse links for node " + n ); - - for( int ri = 0; ri < cnt; ri++ ) + if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 ) { - int nodeIdx = reverseLinks.getDataElement(); - int sizeoffset = writeSizePlaceHolder(); - writeVarLengthSigned( alon[nodeIdx] - alon[n] ); - writeVarLengthSigned( alat[nodeIdx] - alat[n] ); - writeModeAndDesc( true, null ); - injectSize( sizeoffset ); + int len = endpos - startpos; + System.arraycopy( abOld, startpos, ab, aboffset, len ); + if ( debug ) + System.out.println( "*** copied " + len + " bytes from " + aboffset + " for node " + n ); + aboffset += len; + + int cnt = reverseLinks.initList( n ); + if ( debug ) + System.out.println( "*** appending " + cnt + " reverse links for node " + n ); + + for ( int ri = 0; ri < cnt; ri++ ) + { + int nodeIdx = reverseLinks.getDataElement(); + int sizeoffset = writeSizePlaceHolder(); + writeVarLengthSigned( alon[nodeIdx] - alon[n] ); + writeVarLengthSigned( alat[nodeIdx] - alat[n] ); + writeModeAndDesc( true, null ); + injectSize( sizeoffset ); + } + faid[size] = faidOld[n]; + fapos[size] = aboffset; + size++; } - faid[size] = faidOld[n]; - fapos[size] = aboffset; - size++; + startpos = endpos; } init( size ); } diff --git a/brouter-codec/src/main/java/btools/codec/TagValueValidator.java b/brouter-codec/src/main/java/btools/codec/TagValueValidator.java index b5a1a2f..6cfb54c 100644 --- a/brouter-codec/src/main/java/btools/codec/TagValueValidator.java +++ b/brouter-codec/src/main/java/btools/codec/TagValueValidator.java @@ -13,4 +13,5 @@ public interface TagValueValidator public boolean isLookupIdxUsed( int idx ); + public void setDecodeForbidden( boolean decodeForbidden ); } diff --git a/brouter-core/pom.xml b/brouter-core/pom.xml index 090dae2..2d87404 100644 --- a/brouter-core/pom.xml +++ b/brouter-core/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-core diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 3e853cc..1c9a443 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -31,7 +31,9 @@ final class OsmPath implements OsmLinkHolder public int airdistance = 0; // distance to endpos - private OsmNode sourcenode; + private OsmNode sourceNode; + private OsmNode targetNode; + private OsmLink link; public OsmPathElement originElement; public OsmPathElement myElement; @@ -86,10 +88,11 @@ final class OsmPath implements OsmLinkHolder { this(); this.link = link; - this.selev = link.targetNode.getSElev(); + targetNode = link.getTarget( null ); + selev = targetNode.getSElev(); } - OsmPath( OsmNode sourcenode, OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc ) + OsmPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc ) { this(); if ( origin.myElement == null ) @@ -98,7 +101,8 @@ final class OsmPath implements OsmLinkHolder } this.originElement = origin.myElement; this.link = link; - this.sourcenode = sourcenode; + this.sourceNode = origin.targetNode; + this.targetNode = link.getTarget( sourceNode ); this.cost = origin.cost; this.ehbd = origin.ehbd; this.ehbu = origin.ehbu; @@ -120,7 +124,7 @@ final class OsmPath implements OsmLinkHolder int lon0 = origin.originLon; int lat0 = origin.originLat; - OsmNode p1 = origin.link.targetNode; + OsmNode p1 = sourceNode; int lon1 = p1.getILon(); int lat1 = p1.getILat(); short ele1 = origin.selev; @@ -129,8 +133,10 @@ final class OsmPath implements OsmLinkHolder MessageData msgData = recordMessageData ? new MessageData() : null; + boolean isReverse = link.isReverse( sourceNode ); + // evaluate the way tags - rc.expctxWay.evaluate( rc.inverseDirection ^ link.counterLinkWritten, description ); + rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description ); // calculate the costfactor inputs boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f; @@ -163,9 +169,8 @@ final class OsmPath implements OsmLinkHolder // OsmTransferNode transferNode = link.decodeGeometry( p1, rc.byteDataReaderGeometry, rc.transferNodeCache ); OsmTransferNode transferNode = link.geometry == null ? null - : rc.geometryDecoder.decodeGeometry( link.geometry, p1, link.targetNode, link.counterLinkWritten ); + : rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse ); - OsmNode targetNode = link.targetNode; for(;;) { originLon = lon1; @@ -355,7 +360,7 @@ final class OsmPath implements OsmLinkHolder msgData.lon = lon2; msgData.lat = lat2; msgData.ele = ele2; - msgData.wayKeyValues = rc.expctxWay.getKeyValueDescription( link.counterLinkWritten, description ); + msgData.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description ); } if ( stopAtEndpoint ) @@ -379,7 +384,7 @@ final class OsmPath implements OsmLinkHolder if ( transferNode == null ) { // *** penalty for being part of the reference track - if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( origin.link.targetNode ) ) + if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) ) { int reftrackcost = linkdisttotal; cost += reftrackcost; @@ -465,7 +470,12 @@ final class OsmPath implements OsmLinkHolder public OsmNode getSourceNode() { - return sourcenode; + return sourceNode; + } + + public OsmNode getTargetNode() + { + return targetNode; } public OsmLink getLink() diff --git a/brouter-core/src/main/java/btools/router/OsmPathElement.java b/brouter-core/src/main/java/btools/router/OsmPathElement.java index f382479..d061ca7 100644 --- a/brouter-core/src/main/java/btools/router/OsmPathElement.java +++ b/brouter-core/src/main/java/btools/router/OsmPathElement.java @@ -67,7 +67,7 @@ public class OsmPathElement implements OsmPos // construct a path element from a path public static final OsmPathElement create( OsmPath path, boolean countTraffic ) { - OsmNode n = path.getLink().targetNode; + OsmNode n = path.getTargetNode(); OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic ); pe.cost = path.cost; pe.message = path.message; diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 5e872c5..2678a1d 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -390,7 +390,7 @@ public final class OsmTrack } else { - sb.append( " creator=\"BRouter-1.4.4\" version=\"1.1\">\n" ); + sb.append( " creator=\"BRouter-1.4.6\" version=\"1.1\">\n" ); } if ( turnInstructionMode == 3) // osmand style diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 053b954..59e9cc0 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -175,7 +175,7 @@ public final class RoutingContext } if ( goodGuy ) nogos.add( nogo ); } - nogopoints = nogos; + nogopoints = nogos.isEmpty() ? null : nogos; } public long[] getNogoChecksums() diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index b51df5d..04755c9 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -58,6 +58,8 @@ public class RoutingEngine extends Thread public boolean quite = false; + private Object[] extract; + public RoutingEngine( String outfileBase, String logfileBase, String segmentDir, List waypoints, RoutingContext rc ) { @@ -149,6 +151,7 @@ public class RoutingEngine extends Thread routingContext.cleanNogolist( waypoints ); startTime = System.currentTimeMillis(); + long startTime0 = startTime; this.maxRunningTime = maxRunningTime; int nsections = waypoints.size() - 1; OsmTrack[] refTracks = new OsmTrack[nsections]; // used ways for alternatives @@ -208,24 +211,21 @@ public class RoutingEngine extends Thread break; } long endTime = System.currentTimeMillis(); - logInfo( "execution time = " + (endTime-startTime)/1000. + " seconds" ); + logInfo( "execution time = " + (endTime-startTime0)/1000. + " seconds" ); } catch( IllegalArgumentException e) { - errorMessage = e.getMessage(); - logInfo( "Exception (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); } catch( Exception e) { - errorMessage = e instanceof IllegalArgumentException ? e.getMessage() : e.toString(); - logInfo( "Exception (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); logThrowable( e ); } catch( Error e) { cleanOnOOM(); - errorMessage = e.toString(); - logInfo( "Error (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); logThrowable( e ); } finally @@ -264,6 +264,13 @@ public class RoutingEngine extends Thread } } + private void logException( Throwable t ) + { + errorMessage = t instanceof IllegalArgumentException ? t.getMessage() : t.toString(); + logInfo( "Error (linksProcessed=" + linksProcessed + " open paths: " + openSet.getSize() + "): " + errorMessage ); + } + + public void doSearch() { try @@ -280,20 +287,17 @@ public class RoutingEngine extends Thread } catch( IllegalArgumentException e) { - errorMessage = e.getMessage(); - logInfo( "Exception (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); } catch( Exception e) { - errorMessage = e instanceof IllegalArgumentException ? e.getMessage() : e.toString(); - logInfo( "Exception (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); logThrowable( e ); } catch( Error e) { cleanOnOOM(); - errorMessage = e.toString(); - logInfo( "Error (linksProcessed=" + linksProcessed + ": " + errorMessage ); + logException( e ); logThrowable( e ); } finally @@ -404,7 +408,7 @@ public class RoutingEngine extends Thread // geometric position matching finding the nearest routable way-section private void matchWaypointsToNodes( List unmatchedWaypoints ) { - resetCache(); + resetCache( false ); nodesCache.waypointMatcher = new WaypointMatcherImpl( unmatchedWaypoints, 250. ); for( MatchedWaypoint mwp : unmatchedWaypoints ) { @@ -446,9 +450,9 @@ public class RoutingEngine extends Thread // expand hollow link targets and resolve reverse links private void expandHollowLinkTargets( OsmNode n ) { - for( OsmLink link = n.firstlink; link != null; link = link.next ) + for( OsmLink link = n.firstlink; link != null; link = link.getNext( n ) ) { - nodesCache.obtainNonHollowNode( link.targetNode ); + nodesCache.obtainNonHollowNode( link.getTarget( n ) ); } } @@ -567,7 +571,7 @@ public class RoutingEngine extends Thread } - private void resetCache() + private void resetCache( boolean detailed ) { if ( hasInfo() && nodesCache != null ) { @@ -575,9 +579,9 @@ public class RoutingEngine extends Thread } nodesMap = new OsmNodesMap(); - long maxmem = routingContext.memoryclass * 131072L; // 1/4 of total + long maxmem = routingContext.memoryclass * 131072L; // 1/8 of total - nodesCache = new NodesCache(segmentDir, nodesMap, routingContext.expctxWay, routingContext.forceSecondaryData, maxmem, nodesCache ); + nodesCache = new NodesCache(segmentDir, nodesMap, routingContext.expctxWay, routingContext.forceSecondaryData, maxmem, nodesCache, detailed ); } private OsmNode getStartNode( long startId ) @@ -645,21 +649,20 @@ public class RoutingEngine extends Thread routingContext.setWaypoint( wp, false ); OsmPath bestPath = null; OsmLink bestLink = null; - OsmLink startLink = new OsmLink(); - startLink.targetNode = n1; + OsmLink startLink = new OsmLink( null, n1 ); OsmPath startPath = new OsmPath( startLink ); - startLink.addLinkHolder( startPath ); + startLink.addLinkHolder( startPath, null ); double minradius = 1e10; - for( OsmLink link = n1.firstlink; link != null; link = link.next ) + for( OsmLink link = n1.firstlink; link != null; link = link.getNext( n1 ) ) { - OsmNode nextNode = link.targetNode; + OsmNode nextNode = link.getTarget( n1 ); if ( nextNode.isHollow() ) continue; // border node? if ( nextNode.firstlink == null ) continue; // don't care about dead ends if ( nextNode == n1 ) continue; // ? if ( nextNode != n2 ) continue; // just that link wp.radius = 1e9; - OsmPath testPath = new OsmPath( null, startPath, link, null, guideTrack != null, routingContext ); + OsmPath testPath = new OsmPath( startPath, link, null, guideTrack != null, routingContext ); testPath.airdistance = endPos == null ? 0 : nextNode.calcDistance( endPos ); if ( wp.radius < minradius ) { @@ -670,7 +673,7 @@ public class RoutingEngine extends Thread } if ( bestLink != null ) { - bestLink.addLinkHolder( bestPath ); + bestLink.addLinkHolder( bestPath, n1 ); } bestPath.treedepth = 1; @@ -687,14 +690,13 @@ public class RoutingEngine extends Thread try { if ( wp != null ) routingContext.setWaypoint( wp, true ); - OsmLink startLink = new OsmLink(); - startLink.targetNode = n1; + OsmLink startLink = new OsmLink( null, n1 ); OsmPath startPath = new OsmPath( startLink ); - startLink.addLinkHolder( startPath ); + startLink.addLinkHolder( startPath, null ); if ( wp != null ) wp.radius = 1e-5; - return new OsmPath( n1, startPath, link, null, guideTrack != null, routingContext ); + return new OsmPath( startPath, link, null, guideTrack != null, routingContext ); } finally { @@ -706,7 +708,8 @@ public class RoutingEngine extends Thread { try { - resetCache(); + boolean detailed = guideTrack != null; + resetCache( detailed ); return _findTrack( operationName, startWp, endWp, costCuttingTrack, refTrack, fastPartialRecalc ); } finally @@ -742,11 +745,11 @@ public class RoutingEngine extends Thread OsmNode start1 = getStartNode( startNodeId1 ); if ( start1 == null ) return null; OsmNode start2 = null; - for( OsmLink link = start1.firstlink; link != null; link = link.next ) + for( OsmLink link = start1.firstlink; link != null; link = link.getNext( start1 ) ) { - if ( link.targetNode.getIdFromPos() == startNodeId2 ) + if ( link.getTarget( start1 ).getIdFromPos() == startNodeId2 ) { - start2 = link.targetNode; + start2 = link.getTarget( start1 ); break; } } @@ -826,8 +829,8 @@ public class RoutingEngine extends Thread linksProcessed++; OsmLink currentLink = path.getLink(); - OsmNode currentNode = currentLink.targetNode; OsmNode sourceNode = path.getSourceNode(); + OsmNode currentNode = path.getTargetNode(); long currentNodeId = currentNode.getIdFromPos(); if ( sourceNode != null ) @@ -872,6 +875,21 @@ public class RoutingEngine extends Thread } } + OsmLinkHolder firstLinkHolder = currentLink.getFirstLinkHolder( sourceNode ); + for( OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink() ) + { + ((OsmPath)linkHolder).airdistance = -1; // invalidate the entry in the open set; + } + + boolean isBidir = currentLink.isBidirectional(); + sourceNode.unlinkLink ( currentLink ); + + // if the counterlink is alive and does not yet have a path, remove it + if ( isBidir && currentLink.getFirstLinkHolder( currentNode ) == null ) + { + currentNode.unlinkLink(currentLink); + } + // recheck cutoff before doing expensive stuff if ( path.cost + path.airdistance > maxTotalCost + 10 ) { @@ -879,19 +897,11 @@ public class RoutingEngine extends Thread continue; } - expandHollowLinkTargets( currentNode ); - - if ( sourceNode != null ) + for( OsmLink link = currentNode.firstlink; link != null; link = link.getNext( currentNode) ) { - sourceNode.unlinkLink ( currentLink ); - } + OsmNode nextNode = link.getTarget( currentNode ); - OsmLink counterLink = null; - for( OsmLink link = currentNode.firstlink; link != null; link = link.next ) - { - OsmNode nextNode = link.targetNode; - - if ( nextNode.isHollow() ) + if ( ! nodesCache.obtainNonHollowNode( nextNode ) ) { continue; // border node? } @@ -901,7 +911,6 @@ public class RoutingEngine extends Thread } if ( nextNode == sourceNode ) { - counterLink = link; continue; // border node? } @@ -919,7 +928,7 @@ public class RoutingEngine extends Thread // not along the guide-track, discard, but register for voice-hint processing if ( routingContext.turnInstructionMode > 0 ) { - OsmPath detour = new OsmPath( currentNode, path, link, refTrack, true, routingContext ); + OsmPath detour = new OsmPath( path, link, refTrack, true, routingContext ); if ( detour.cost >= 0. && nextId != startNodeId1 && nextId != startNodeId2 ) { guideTrack.registerDetourForId( currentNode.getIdFromPos(), OsmPathElement.create( detour, false ) ); @@ -932,7 +941,7 @@ public class RoutingEngine extends Thread OsmPath bestPath = null; boolean isFinalLink = false; - long targetNodeId = link.targetNode.getIdFromPos(); + long targetNodeId = nextNode.getIdFromPos(); if ( currentNodeId == endNodeId1 || currentNodeId == endNodeId2 ) { if ( targetNodeId == endNodeId1 || targetNodeId == endNodeId2 ) @@ -941,7 +950,7 @@ public class RoutingEngine extends Thread } } - for( OsmLinkHolder linkHolder = currentLink.firstlinkholder; linkHolder != null; linkHolder = linkHolder.getNextForLink() ) + for( OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink() ) { OsmPath otherPath = (OsmPath)linkHolder; try @@ -951,7 +960,7 @@ public class RoutingEngine extends Thread endPos.radius = 1e-5; routingContext.setWaypoint( endPos, true ); } - OsmPath testPath = new OsmPath( currentNode, otherPath, link, refTrack, guideTrack != null, routingContext ); + OsmPath testPath = new OsmPath( otherPath, link, refTrack, guideTrack != null, routingContext ); if ( testPath.cost >= 0 && ( bestPath == null || testPath.cost < bestPath.cost ) ) { bestPath = testPath; @@ -961,10 +970,6 @@ public class RoutingEngine extends Thread { routingContext.unsetWaypoint(); } - if ( otherPath != path ) - { - otherPath.airdistance = -1; // invalidate the entry in the open set - } } if ( bestPath != null ) { @@ -977,7 +982,7 @@ public class RoutingEngine extends Thread if ( inRadius && ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 10 ) ) { // add only if this may beat an existing path for that link - OsmLinkHolder dominator = link.firstlinkholder; + OsmLinkHolder dominator = link.getFirstLinkHolder( currentNode ); while( !trafficSim && dominator != null ) { if ( bestPath.definitlyWorseThan( (OsmPath)dominator, routingContext ) ) @@ -987,28 +992,24 @@ public class RoutingEngine extends Thread dominator = dominator.getNextForLink(); } - if ( dominator == null ) - { + if ( dominator == null ) + { if ( trafficSim && boundary != null && path.cost == 0 && bestPath.cost > 0 ) { bestPath.airdistance += boundary.getBoundaryDistance( nextNode ); } bestPath.treedepth = path.treedepth + 1; - link.addLinkHolder( bestPath ); + link.addLinkHolder( bestPath, currentNode ); synchronized( openSet ) { addToOpenset( bestPath ); } - } + } } } } - // if the counterlink does not yet have a path, remove it - if ( counterLink != null && counterLink.firstlinkholder == null ) - { - currentNode.unlinkLink(counterLink); - } + path.unregisterUpTree( routingContext ); } return null; @@ -1023,22 +1024,6 @@ public class RoutingEngine extends Thread } } - private int preloadRing( OsmNode n, int ring ) - { - int d = 12500; - int c = 0; - for( int idxLat=-ring; idxLat<=ring; idxLat++ ) - for( int idxLon=-ring; idxLon<=ring; idxLon++ ) - { - int absLat = idxLat < 0 ? -idxLat : idxLat; - int absLon = idxLon < 0 ? -idxLon : idxLon; - int max = absLat > absLon ? absLat : absLon; - if ( max < ring ) continue; - c += nodesCache.loadSegmentFor( n.ilon + d*idxLon , n.ilat +d*idxLat ); - } - return c; - } - private OsmTrack compileTrack( OsmPath path, boolean verbose ) { OsmPathElement element = OsmPathElement.create( path, false ); @@ -1137,8 +1122,21 @@ public class RoutingEngine extends Thread return track; } + public int getPathPeak() + { + synchronized( openSet ) + { + return openSet.getPeakSize(); + } + } + public int[] getOpenSet() { + if ( extract == null ) + { + extract = new Object[500]; + } + synchronized( openSet ) { if ( guideTrack != null ) @@ -1154,14 +1152,15 @@ public class RoutingEngine extends Thread return res; } - List extract = openSet.getExtract(); - int[] res = new int[extract.size() * 2]; - int i = 0; - for( OsmPath p : extract ) + int size = openSet.getExtract(extract); + int[] res = new int[size * 2]; + for( int i=0, j=0; i org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-expressions diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java index 77d3426..3adea96 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java @@ -8,10 +8,10 @@ package btools.expressions; import btools.codec.TagValueValidator; - - public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator { + private boolean decodeForbidden = true; + private static String[] buildInVariables = { "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask" }; @@ -61,6 +61,12 @@ public final class BExpressionContextWay extends BExpressionContext implements T minCostFactor = reverseCostFactor; } } - return minCostFactor < 9999.f ? 2 : minCostFactor < 10000.f ? 1 : 0; + return minCostFactor < 9999.f ? 2 : decodeForbidden ? (minCostFactor < 10000.f ? 1 : 0) : 0; + } + + @Override + public void setDecodeForbidden( boolean decodeForbidden ) + { + this.decodeForbidden= decodeForbidden; } } diff --git a/brouter-map-creator/pom.xml b/brouter-map-creator/pom.xml index a7499b2..6b7e10f 100644 --- a/brouter-map-creator/pom.xml +++ b/brouter-map-creator/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-map-creator diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java index bbeb89e..bce6451 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmLinkP.java @@ -68,7 +68,7 @@ public class OsmLinkP return next; } else if ( targetNode == source ) - { + { return previous; } else diff --git a/brouter-mapaccess/pom.xml b/brouter-mapaccess/pom.xml index 56ae527..8ee4a4f 100644 --- a/brouter-mapaccess/pom.xml +++ b/brouter-mapaccess/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-mapaccess diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java index b36cb2c..976a443 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java @@ -38,6 +38,7 @@ public final class NodesCache private long cacheSum = 0; private long maxmem; + private boolean detailed; private boolean garbageCollectionEnabled = false; private boolean ghostCleaningDone = false; @@ -52,7 +53,7 @@ public final class NodesCache return "collecting=" + garbageCollectionEnabled + " noGhosts=" + ghostCleaningDone + " cacheSum=" + cacheSum + " cacheSumClean=" + cacheSumClean + " ghostSum=" + ghostSum + " ghostWakeup=" + ghostWakeup ; } - public NodesCache( String segmentDir, OsmNodesMap nodesMap, BExpressionContextWay ctxWay, boolean forceSecondaryData, long maxmem, NodesCache oldCache ) + public NodesCache( String segmentDir, OsmNodesMap nodesMap, BExpressionContextWay ctxWay, boolean forceSecondaryData, long maxmem, NodesCache oldCache, boolean detailed ) { this.segmentDir = new File( segmentDir ); this.nodesMap = nodesMap; @@ -61,6 +62,12 @@ public final class NodesCache this.lookupMinorVersion = ctxWay.meta.lookupMinorVersion; this.forceSecondaryData = forceSecondaryData; this.maxmem = maxmem; + this.detailed = detailed; + + if ( ctxWay != null ) + { + ctxWay.setDecodeForbidden( detailed ); + } first_file_access_failed = false; first_file_access_name = null; @@ -74,17 +81,24 @@ public final class NodesCache dataBuffers = oldCache.dataBuffers; secondarySegmentsDir = oldCache.secondarySegmentsDir; - // re-use old, virgin caches - fileRows = oldCache.fileRows; - for ( OsmFile[] fileRow : fileRows ) + // re-use old, virgin caches (if same detail-mode) + if ( oldCache.detailed == detailed) { - if ( fileRow == null ) - continue; - for ( OsmFile osmf : fileRow ) + fileRows = oldCache.fileRows; + for ( OsmFile[] fileRow : fileRows ) { - cacheSum += osmf.setGhostState(); + if ( fileRow == null ) + continue; + for ( OsmFile osmf : fileRow ) + { + cacheSum += osmf.setGhostState(); + } } } + else + { + fileRows = new OsmFile[180][]; + } } else { @@ -113,7 +127,7 @@ public final class NodesCache // clean all ghosts and enable garbage collection private void checkEnableCacheCleaning() { - if ( cacheSum < maxmem || ghostCleaningDone ) + if ( cacheSum < maxmem ) { return; } @@ -127,7 +141,7 @@ public final class NodesCache } for ( OsmFile osmf : fileRow ) { - if ( garbageCollectionEnabled ) + if ( garbageCollectionEnabled && !ghostCleaningDone ) { cacheSum -= osmf.cleanGhosts(); } @@ -141,6 +155,7 @@ public final class NodesCache if ( garbageCollectionEnabled ) { ghostCleaningDone = true; + maxmem *= 2; } else { diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java index 11bfba6..bbaf9fe 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmLink.java @@ -10,36 +10,168 @@ package btools.mapaccess; public class OsmLink { /** - * The description bitmap is mainly the way description - * used to calculate the costfactor + * The description bitmap contains the waytags (valid for both directions) */ public byte[] descriptionBitmap; - public OsmNode targetNode; - - public OsmLink next; - - public OsmLinkHolder firstlinkholder = null; - + /** + * The geometry contains intermediate nodes, null for none (valid for both directions) + */ public byte[] geometry; - public boolean counterLinkWritten; + // a link logically knows only its target, but for the reverse link, source and target are swapped + protected OsmNode n1; + protected OsmNode n2; - public byte state; + // same for the next-link-for-node pointer: previous applies to the reverse link + protected OsmLink previous; + protected OsmLink next; - public void setGeometry( byte[] geometry ) + private OsmLinkHolder reverselinkholder = null; + private OsmLinkHolder firstlinkholder = null; + + protected OsmLink() { - this.geometry = geometry; } - final public void addLinkHolder( OsmLinkHolder holder ) - { - if ( firstlinkholder != null ) { holder.setNextForLink( firstlinkholder ); } - firstlinkholder = holder; - } + public OsmLink( OsmNode source, OsmNode target ) + { + n1 = source; + n2 = target; + } - public String toString() + /** + * Get the relevant target-node for the given source + */ + public final OsmNode getTarget( OsmNode source ) + { + return n2 != source && n2 != null ? n2 : n1; +/* if ( n2 != null && n2 != source ) + { + return n2; + } + else if ( n1 != null && n1 != source ) + { + return n1; + } + else + { + new Throwable( "ups" ).printStackTrace(); + throw new IllegalArgumentException( "internal error: getTarget: unknown source; " + source + " n1=" + n1 + " n2=" + n2 ); + } */ + } + + /** + * Get the relevant next-pointer for the given source + */ + public final OsmLink getNext( OsmNode source ) + { + return n2 != source && n2 != null ? next : previous; +/* if ( n2 != null && n2 != source ) + { + return next; + } + else if ( n1 != null && n1 != source ) + { + return previous; + } + else + { + throw new IllegalArgumentException( "internal error: gextNext: unknown source" ); + } */ + } + + /** + * Reset this link for the given direction + */ + protected final OsmLink clear( OsmNode source ) + { + OsmLink n; + if ( n2 != null && n2 != source ) + { + n = next; + next = null; + n2 = null; + firstlinkholder = null; + } + else if ( n1 != null && n1 != source ) + { + n = previous; + previous = null; + n1 = null; + reverselinkholder = null; + } + else + { + throw new IllegalArgumentException( "internal error: setNext: unknown source" ); + } + if ( n1 == null && n2 == null ) + { + descriptionBitmap = null; + geometry = null; + } + return n; + } + + public final void setFirstLinkHolder( OsmLinkHolder holder, OsmNode source ) + { + if ( n2 != null && n2 != source ) + { + firstlinkholder = holder; + } + else if ( n1 != null && n1 != source ) + { + reverselinkholder = holder; + } + else + { + throw new IllegalArgumentException( "internal error: setFirstLinkHolder: unknown source" ); + } + } + + public final OsmLinkHolder getFirstLinkHolder( OsmNode source ) + { + if ( n2 != null && n2 != source ) + { + return firstlinkholder; + } + else if ( n1 != null && n1 != source ) + { + return reverselinkholder; + } + else + { + throw new IllegalArgumentException( "internal error: getFirstLinkHolder: unknown source" ); + } + } + + public final boolean isReverse( OsmNode source ) + { + return n1 != source && n1 != null; +/* if ( n2 != null && n2 != source ) + { + return false; + } + else if ( n1 != null && n1 != source ) { - return "Link(target=" + targetNode.getIdFromPos() + " counterLinkWritten=" + counterLinkWritten + " state=" + state + ")"; - } + return true; + } + else + { + throw new IllegalArgumentException( "internal error: isReverse: unknown source" ); + } */ + } + + public final boolean isBidirectional() + { + return n1 != null && n2 != null; + } + + public final void addLinkHolder( OsmLinkHolder holder, OsmNode source ) + { + OsmLinkHolder firstHolder = getFirstLinkHolder( source ); + if ( firstHolder != null ) { holder.setNextForLink( firstHolder ); } + setFirstLinkHolder( holder, source ); + } + } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java index 96bfb02..f68b27e 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java @@ -11,8 +11,33 @@ import btools.expressions.BExpressionContextWay; import btools.util.ByteArrayUnifier; import btools.util.IByteArrayUnifier; -public class OsmNode implements OsmPos +public class OsmNode extends OsmLink implements OsmPos { + /** + * The latitude + */ + public int ilat; + + /** + * The longitude + */ + public int ilon; + + /** + * The elevation + */ + public short selev; + + /** + * The node-tags, if any + */ + public byte[] nodeDescription; + + /** + * The links to other nodes + */ + public OsmLink firstlink = null; + public OsmNode() { } @@ -29,22 +54,6 @@ public class OsmNode implements OsmPos ilat = (int) ( id & 0xffffffff ); } - /** - * The latitude - */ - public int ilat; - - /** - * The longitude - */ - public int ilon; - - /** - * The elevation - */ - public short selev; - - public byte[] nodeDescription; // interface OsmPos public final int getILat() @@ -67,58 +76,28 @@ public class OsmNode implements OsmPos return selev / 4.; } - /** - * The links to other nodes - */ - public OsmLink firstlink = null; - - // preliminry in forward order to avoid regressions - public final void addLink( OsmLink link ) + private void addLink( OsmLink link, boolean isReverse, OsmNode tn ) { - if ( firstlink == null ) + if ( isReverse ) { + link.n1 = tn; + link.n2 = this; + link.next = tn.firstlink; + link.previous = firstlink; + tn.firstlink = link; firstlink = link; } else { - OsmLink l = firstlink; - while (l.next != null) - l = l.next; - l.next = link; + link.n1 = this; + link.n2 = tn; + link.next = firstlink; + link.previous = tn.firstlink; + tn.firstlink = link; + firstlink = link; } } - private OsmLink getCompatibleLink( int ilon, int ilat, boolean counterLinkWritten, int state ) - { - for ( OsmLink l = firstlink; l != null; l = l.next ) - { - if ( counterLinkWritten == l.counterLinkWritten && l.state == state ) - { - OsmNode t = l.targetNode; - if ( t.ilon == ilon && t.ilat == ilat ) - { - l.state = 0; - return l; - } - } - } - // second try ignoring counterLinkWritten - // (border links are written in both directions) - for ( OsmLink l = firstlink; l != null; l = l.next ) - { - if ( l.state == state ) - { - OsmNode t = l.targetNode; - if ( t.ilon == ilon && t.ilat == ilat ) - { - l.state = 0; - return l; - } - } - } - return null; - } - public final int calcDistance( OsmPos p ) { double l = ( ilat - 90000000 ) * 0.00000001234134; @@ -154,6 +133,8 @@ public class OsmNode implements OsmPos selev = mc.readShort(); int nodeDescSize = mc.readVarLengthUnsigned(); nodeDescription = nodeDescSize == 0 ? null : mc.readUnified( nodeDescSize, abUnifier ); + + OsmLink link0 = firstlink; while (mc.hasMoreData()) { @@ -176,47 +157,42 @@ public class OsmNode implements OsmPos continue; // skip self-ref } - // first check the known links for that target - OsmLink link = getCompatibleLink( linklon, linklat, isReverse, 2 ); - if ( link == null ) // .. not found, then check the hollow nodes + OsmNode tn = null; // find the target node + OsmLink link = null; + + // ...in our known links + for ( OsmLink l = link0; l != null; l = l.getNext( this ) ) { - OsmNode tn = hollowNodes.get( linklon, linklat ); // target node + OsmNode t = l.getTarget( this ); + if ( t.ilon == linklon && t.ilat == linklat ) + { + tn = t; + if ( isReverse || ( l.descriptionBitmap == null && !l.isReverse( this ) ) ) + { + link = l; // the correct one that needs our data + break; + } + } + } + if ( tn == null ) // .. not found, then check the hollow nodes + { + tn = hollowNodes.get( linklon, linklat ); // target node if ( tn == null ) // node not yet known, create a new hollow proxy { tn = new OsmNode( linklon, linklat ); tn.setHollow(); hollowNodes.put( tn ); + addLink( link = tn, isReverse, tn ); // technical inheritance: link instance in node } - link = new OsmLink(); - link.targetNode = tn; - link.counterLinkWritten = isReverse; - link.state = 1; - addLink( link ); } - - // now we have a link with a target node -> get the reverse link - OsmLink rlink = link.targetNode.getCompatibleLink( ilon, ilat, !isReverse, 1 ); - if ( rlink == null ) // .. not found, create it + if ( link == null ) { - rlink = new OsmLink(); - rlink.targetNode = this; - rlink.counterLinkWritten = !isReverse; - rlink.state = 2; - link.targetNode.addLink( rlink ); + addLink( link = new OsmLink(), isReverse, tn ); } - if ( !isReverse ) { - // we have the data for that link, so fill both the link .. link.descriptionBitmap = description; - link.setGeometry( geometry ); - - // .. and the reverse - if ( rlink.counterLinkWritten ) - { - rlink.descriptionBitmap = description; - rlink.setGeometry( geometry ); - } + link.geometry = geometry; } } hollowNodes.remove( this ); @@ -240,21 +216,41 @@ public class OsmNode implements OsmPos public final void unlinkLink( OsmLink link ) { + OsmLink n = link.clear( this ); + if ( link == firstlink ) { - firstlink = link.next; + firstlink = n; return; } - for ( OsmLink l = firstlink; l != null; l = l.next ) + OsmLink l = firstlink; + while( l != null ) { - if ( l.next == link ) + // if ( l.isReverse( this ) ) + if ( l.n1 != this && l.n1 != null ) // isReverse inline { - l.next = link.next; - return; + OsmLink nl = l.previous; + if ( nl == link ) + { + l.previous = n; + return; + } + l = nl; + } + else + { + OsmLink nl = l.next; + if ( nl == link ) + { + l.next = n; + return; + } + l = nl; } } } + @Override public final boolean equals( Object o ) { diff --git a/brouter-mem-router/pom.xml b/brouter-mem-router/pom.xml index d55a5ed..11810b2 100644 --- a/brouter-mem-router/pom.xml +++ b/brouter-mem-router/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-mem-router diff --git a/brouter-routing-app/AndroidManifest.xml b/brouter-routing-app/AndroidManifest.xml index c8250b3..c8ead41 100644 --- a/brouter-routing-app/AndroidManifest.xml +++ b/brouter-routing-app/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="17" + android:versionName="1.4.6" package="btools.routingapp"> org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-routing-app diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java index 73b58b1..d041fc4 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java @@ -766,7 +766,8 @@ public class BRouterView extends View } else { - String result = "version = BRouter-1.4.4\n" + "memory-class = " + memoryClass + "\ndistance = " + cr.getDistance() / 1000. + " km\n" + "filtered ascend = " + cr.getAscend() + String memstat = memoryClass + "mb pathPeak " + ((cr.getPathPeak()+500)/1000) + "k"; + String result = "version = BRouter-1.4.6\n" + "mem = " + memstat + "\ndistance = " + cr.getDistance() / 1000. + " km\n" + "filtered ascend = " + cr.getAscend() + " m\n" + "plain ascend = " + cr.getPlainAscend(); rawTrack = cr.getFoundRawTrack(); diff --git a/brouter-server/pom.xml b/brouter-server/pom.xml index 10aba25..096c06c 100644 --- a/brouter-server/pom.xml +++ b/brouter-server/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-server diff --git a/brouter-server/src/main/java/btools/server/BRouter.java b/brouter-server/src/main/java/btools/server/BRouter.java index 85c9f49..a1acf05 100644 --- a/brouter-server/src/main/java/btools/server/BRouter.java +++ b/brouter-server/src/main/java/btools/server/BRouter.java @@ -88,7 +88,7 @@ public class BRouter } System.exit(0); } - System.out.println("BRouter 1.4.4 / 29082016 / abrensch"); + System.out.println("BRouter 1.4.6 / 30092016 / abrensch"); if ( args.length < 6 ) { System.out.println("Find routes in an OSM map"); @@ -155,6 +155,7 @@ public class BRouter c.setAlternativeIdx( Integer.parseInt( args[6] ) ); } } + c.memoryclass = (int) ( Runtime.getRuntime().maxMemory() / 1024 / 1024 ); return c; } } diff --git a/brouter-server/src/main/java/btools/server/RouteServer.java b/brouter-server/src/main/java/btools/server/RouteServer.java index d46dca4..772c929 100644 --- a/brouter-server/src/main/java/btools/server/RouteServer.java +++ b/brouter-server/src/main/java/btools/server/RouteServer.java @@ -155,7 +155,7 @@ public class RouteServer extends Thread public static void main(String[] args) throws Exception { - System.out.println("BRouter 1.4.4 / 29082016"); + System.out.println("BRouter 1.4.6 / 30092016"); if ( args.length != 5 ) { System.out.println("serve BRouter protocol"); diff --git a/brouter-util/pom.xml b/brouter-util/pom.xml index 545a67d..8f6ce81 100644 --- a/brouter-util/pom.xml +++ b/brouter-util/pom.xml @@ -5,7 +5,7 @@ org.btools brouter - 1.4.4 + 1.4.6 ../pom.xml brouter-util diff --git a/brouter-util/src/main/java/btools/util/SortedHeap.java b/brouter-util/src/main/java/btools/util/SortedHeap.java index e89ec85..0953a6a 100644 --- a/brouter-util/src/main/java/btools/util/SortedHeap.java +++ b/brouter-util/src/main/java/btools/util/SortedHeap.java @@ -1,30 +1,19 @@ package btools.util; -import java.util.ArrayList; -import java.util.List; import java.util.Random; /** - * Memory efficient Heap to get the lowest-key value of a set of key-object pairs + * Memory efficient and lightning fast heap to get the lowest-key value of a set of key-object pairs * * @author ab */ public final class SortedHeap { - private int[][] al; - private int[] lv; // low values - - private int[] nextNonEmpty; - private int firstNonEmpty; - - private int[] lp; // the low pointers - - private Object[][] vla; // value list array - - protected static final int MAXLISTS = 28; // enough for size = ca Integer.MAX_VALUE - private int size; - private boolean isClear = false; + private int peaksize; + private SortedBin first; + private SortedBin second; + private SortedBin firstNonEmpty; public SortedHeap() { @@ -36,66 +25,111 @@ public final class SortedHeap */ public V popLowestKeyValue() { - int idx = firstNonEmpty; - if ( idx < 0 ) + SortedBin bin = firstNonEmpty; + if ( bin == null ) { return null; } size--; - int minId = lv[idx]; - int minIdx = idx; - for (;;) + int minId = bin.lv; + SortedBin minBin = bin; + while( ( bin = bin.nextNonEmpty ) != null ) { - idx = nextNonEmpty[idx]; - if ( idx < 0 ) + if ( bin.lv < minId ) { - return dropLowest( minIdx ); - } - if ( lv[idx] < minId ) - { - minId = lv[idx]; - minIdx = idx; + minId = bin.lv; + minBin = bin; } } + return (V) minBin.dropLowest(); } - private V dropLowest( int idx ) + private static final class SortedBin { - int lp_old = lp[idx]++; - int lp_new = lp_old+1; - if ( lp_new == 4 << idx ) + SortedHeap parent; + SortedBin next; + SortedBin nextNonEmpty; + int binsize; + int[] al; // key array + Object[] vla; // value array + int lv; // low value + int lp; // low pointer + + SortedBin( int binsize, SortedHeap parent ) { - unlinkIdx( idx ); + this.binsize = binsize; + this.parent = parent; + al = new int[binsize]; + vla = new Object[binsize]; + lp = binsize; } - else + + SortedBin next() { - lv[idx] = al[idx][lp_new]; - } - Object[] vlai = vla[idx]; - V res = (V) vlai[lp_old]; - vlai[lp_old] = null; - return res; - } - - private void unlinkIdx( int idx ) - { - if ( idx == firstNonEmpty ) - { - firstNonEmpty = nextNonEmpty[idx]; - return; - } - int i = firstNonEmpty; - for(;;) - { - if ( nextNonEmpty[i] == idx ) + if ( next == null ) { - nextNonEmpty[i] = nextNonEmpty[idx]; + next = new SortedBin( binsize << 1, parent ); + } + return next; + } + + Object dropLowest() + { + int lpOld = lp; + if ( ++lp == binsize ) + { + unlink(); + } + else + { + lv = al[lp]; + } + Object res = vla[lpOld]; + vla[lpOld] = null; + return res; + } + + void unlink() + { + SortedBin neBin = parent.firstNonEmpty; + if ( neBin == this ) + { + parent.firstNonEmpty = nextNonEmpty; return; } - i = nextNonEmpty[i]; + for(;;) + { + SortedBin next = neBin.nextNonEmpty; + if ( next == this ) + { + neBin.nextNonEmpty = nextNonEmpty; + return; + } + neBin = next; + } } - } + void add( int key, Object value ) + { + int p = lp; + for(;;) + { + if ( p == binsize || key < al[p] ) + { + al[p-1] = key; + vla[p-1] = value; + lv = al[--lp]; + return; + } + al[p-1] = al[p]; + vla[p-1] = vla[p]; + p++; + } + } + + + } + /** * add a key value pair to the heap * @@ -106,182 +140,139 @@ public final class SortedHeap */ public void add( int key, V value ) { - isClear = false; size++; - if ( lp[0] == 0 ) + if ( first.lp == 0 && second.lp == 0) // both full ? { sortUp(); } - int lp0 = lp[0]; - - int[] al0 = al[0]; - Object[] vla0 = vla[0]; - - for(;;) + if ( first.lp > 0 ) { - if ( lp0 == 4 || key < al0[lp0] ) + first.add( key, value ); + if ( firstNonEmpty != first ) { - al0[lp0-1] = key; - vla0[lp0-1] = value; - lv[0] = al0[--lp[0]]; - if ( firstNonEmpty != 0 ) - { - nextNonEmpty[0] = firstNonEmpty; - firstNonEmpty = 0; - } - return; + first.nextNonEmpty = firstNonEmpty; + firstNonEmpty = first; } - al0[lp0-1] = al0[lp0]; - vla0[lp0-1] = vla0[lp0]; - lp0++; } + else // second bin not full + { + second.add( key, value ); + if ( first.nextNonEmpty != second ) + { + second.nextNonEmpty = first.nextNonEmpty; + first.nextNonEmpty = second; + } + } + } private void sortUp() { - // determine the first array big enough to take them all - int cnt = 4; // value count up to idx - int idx = 1; - int n = 8; - int nonEmptyCount = 1; - - for ( ;; ) + if ( size > peaksize ) { - int nentries = n - lp[idx]; + peaksize = size; + } + + // determine the first array big enough to take them all + int cnt = 8; // value count of first 2 bins is always 8 + SortedBin tbin = second; // target bin + SortedBin lastNonEmpty = second; + do + { + tbin = tbin.next(); + int nentries = tbin.binsize - tbin.lp; if ( nentries > 0 ) { - cnt += n - lp[idx]; - nonEmptyCount++; + cnt += nentries; + lastNonEmpty = tbin; } - if ( cnt <= n ) - { - break; - } - idx++; - n <<= 1; } + while( cnt > tbin.binsize ); - // create, if not yet - if ( al[idx] == null ) + int[] al_t = tbin.al; + Object[] vla_t = tbin.vla; + int tp = tbin.binsize-cnt; // target pointer + + // unlink any higher, non-empty arrays + SortedBin otherNonEmpty = lastNonEmpty.nextNonEmpty; + lastNonEmpty.nextNonEmpty = null; + + // now merge the content of these non-empty bins into the target bin + while( firstNonEmpty != null ) { - al[idx] = new int[n]; - vla[idx] = new Object[n]; - } + SortedBin ne = firstNonEmpty; + SortedBin minBin = ne; + int minId = minBin.lv; - int[] al_t = al[idx]; - Object[] vla_t = vla[idx]; - int tp = n-cnt; // target pointer - - // now merge the contents of arrays 0...idx into idx - while( nonEmptyCount > 1 ) - { - int neIdx = firstNonEmpty; - int minIdx = neIdx; - int minId = lv[minIdx]; - - for ( int i = 1; i < nonEmptyCount; i++ ) + while ( ( ne = ne.nextNonEmpty ) != null ) { - neIdx = nextNonEmpty[neIdx]; - if ( lv[neIdx] < minId ) + if ( ne.lv < minId ) { - minIdx = neIdx; - minId = lv[neIdx]; + minBin = ne; + minId = minBin.lv; } } // current minimum found, copy to target array al_t[tp] = minId; - vla_t[tp++] = dropLowest( minIdx ); - - if ( lp[minIdx] == 4 << minIdx ) - { - nonEmptyCount--; - } + vla_t[tp++] = minBin.dropLowest(); } - // only one non-empty index left, so just copy the remaining entries - if ( firstNonEmpty != idx ) // no self-copy needed - { - int[] al_s = al[firstNonEmpty]; - Object[] vla_s = vla[firstNonEmpty]; - int sp = lp[firstNonEmpty]; // source-pointer - while( sp < 4 << firstNonEmpty ) - { - al_t[tp] = al_s[sp]; - vla_t[tp++] = vla_s[sp]; - vla_s[sp++] = null; - } - lp[firstNonEmpty] = sp; - } - unlinkIdx( firstNonEmpty ); - lp[idx] = n-cnt; // new target low pointer - lv[idx] = al[idx][lp[idx]]; - nextNonEmpty[idx] = firstNonEmpty; - firstNonEmpty = idx; + tp = tbin.binsize-cnt; + tbin.lp = tp; // new target low pointer + tbin.lv = tbin.al[tp]; + tbin.nextNonEmpty = otherNonEmpty; + firstNonEmpty = tbin; } public void clear() { - if ( !isClear ) - { - isClear = true; - size = 0; - - lp = new int[MAXLISTS]; - - // allocate key lists - al = new int[MAXLISTS][]; - al[0] = new int[4]; // make the first array - - // same for the values - vla = new Object[MAXLISTS][]; - vla[0] = new Object[4]; - - lv = new int[MAXLISTS]; - nextNonEmpty = new int[MAXLISTS]; - - firstNonEmpty = -1; - - int n = 4; - for ( int idx = 0; idx < MAXLISTS; idx++ ) - { - lp[idx] = n; - n <<= 1; - nextNonEmpty[idx] = -1; // no next - } - } + size = 0; + first = new SortedBin( 4, this ); + second = new SortedBin( 4, this ); + firstNonEmpty = null; + } + + public int getSize() + { + return size; } - public List getExtract() + public int getPeakSize() { - int div = size / 1000 + 1; + return peaksize; + } + + public int getExtract( Object[] targetArray ) + { + int tsize = targetArray.length; + int div = size / tsize + 1; + int tp = 0; - ArrayList res = new ArrayList( size / div + 1 ); int lpi = 0; - for ( int i = 1;; i++ ) + SortedBin bin = firstNonEmpty; + while( bin != null ) { - int[] ali = al[i]; - if ( ali == null ) - break; - lpi += lp[i]; - Object[] vlai = vla[i]; - int n = 4 << i; + lpi += bin.lp; + Object[] vlai = bin.vla; + int n = bin.binsize; while (lpi < n) { - res.add( (V) vla[i][lpi] ); + targetArray[tp++] = vlai[lpi]; lpi += div; } lpi -= n; + bin = bin.nextNonEmpty; } - return res; + return tp; } public static void main(String[] args) { SortedHeap sh = new SortedHeap(); Random rnd = new Random(); - for( int i = 0; i< 100; i++ ) + for( int i = 0; i< 1000; i++ ) { int val = rnd.nextInt( 1000000 ); sh.add( val, "" + val ); diff --git a/pom.xml b/pom.xml index 7f7d333..7a0cd96 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.btools brouter - 1.4.4 + 1.4.6 pom http://brouter.de/brouter/ brouter