statistical encoding

This commit is contained in:
Arndt 2015-10-11 19:27:33 +02:00
parent f8dee5b7d1
commit ccf6641bad
41 changed files with 4543 additions and 1965 deletions

View file

@ -17,5 +17,15 @@
<artifactId>brouter-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-expressions</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View file

@ -1,271 +0,0 @@
/**
* cache for a single square
*
* @author ab
*/
package btools.mapaccess;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import btools.util.ByteDataReader;
import btools.util.Crc32;
final class MicroCache extends ByteDataReader
{
private int[] faid;
private int[] fapos;
private int size = 0;
private int delcount = 0;
private int delbytes = 0;
private int p2size; // next power of 2 of size
// the object parsing position and length
private int aboffsetEnd;
private int lonIdxBase;
private int latIdxBase;
// cache control: a virgin cache can be
// put to ghost state for later recovery
boolean virgin = true;
boolean ghost = false;
public MicroCache( OsmFile segfile, int lonIdx80, int latIdx80, byte[] iobuffer ) throws Exception
{
super( null );
int lonDegree = lonIdx80/80;
int latDegree = latIdx80/80;
lonIdxBase = (lonIdx80/5)*62500 + 31250;
latIdxBase = (latIdx80/5)*62500 + 31250;
int subIdx = (latIdx80-80*latDegree)*80 + (lonIdx80-80*lonDegree);
{
ab = iobuffer;
int asize = segfile.getDataInputForSubIdx(subIdx, ab);
if ( asize == 0 )
{
ab = null;
return;
}
if ( asize > iobuffer.length )
{
ab = new byte[asize];
asize = segfile.getDataInputForSubIdx(subIdx, ab);
}
aboffset = 0;
size = readInt();
// get net size
int nbytes = 0;
for(int i = 0; i<size; i++)
{
aboffset += 4;
int bodySize = readVarLengthUnsigned();
aboffset += bodySize;
nbytes += bodySize;
}
int crc = Crc32.crc( ab, 0, aboffset );
if ( crc != readInt() )
{
throw new IOException( "checkum error" );
}
// new array with only net data
byte[] nab = new byte[nbytes];
aboffset = 4;
int noffset = 0;
faid = new int[size];
fapos = new int[size];
p2size = 0x40000000;
while( p2size > size ) p2size >>= 1;
for(int i = 0; i<size; i++)
{
faid[i] = readInt() ^ 0x8000; // flip lat-sign for correct ordering
int bodySize = readVarLengthUnsigned();
fapos[i] = noffset;
System.arraycopy( ab, aboffset, nab, noffset, bodySize );
aboffset += bodySize;
noffset += bodySize;
}
ab = nab;
}
}
public int getSize()
{
return size;
}
public int getDataSize()
{
return ab == null ? 0 : ab.length;
}
/**
* Set the internal reader (aboffset, ablength)
* to the body data for the given id
*
* @return true if id was found
*
* Throws an exception if that id was already requested
* as an early detector for identity problems
*/
private boolean getAndClear( long id64 )
{
if ( size == 0 )
{
return false;
}
int id = shrinkId( id64 );
int[] a = faid;
int offset = p2size;
int n = 0;
while ( offset> 0 )
{
int nn = n + offset;
if ( nn < size && a[nn] <= id )
{
n = nn;
}
offset >>= 1;
}
if ( a[n] == id )
{
if ( ( fapos[n] & 0x80000000 ) == 0 )
{
aboffset = fapos[n];
int ablength = ( n+1 < size ? fapos[n+1] & 0x7fffffff : ab.length ) - aboffset;
aboffsetEnd = aboffset + ablength;
fapos[n] |= 0x80000000; // mark deleted
delbytes+= ablength;
delcount++;
return true;
}
else
{
throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
}
}
return false;
}
/**
* Fill a hollow node with it's body data
*/
public void fillNode( OsmNode node, OsmNodesMap nodesMap, DistanceChecker dc, boolean doCollect )
{
long id = node.getIdFromPos();
if ( getAndClear( id ) )
{
node.parseNodeBody( this, nodesMap, dc );
}
if ( doCollect && delcount > size / 2 ) // garbage collection
{
collect();
}
}
void collect()
{
if ( delcount > 0 )
{
virgin = false;
int nsize = size - delcount;
if ( nsize == 0 )
{
faid = null;
fapos = null;
}
else
{
int[] nfaid = new int[nsize];
int[] nfapos = new int[nsize];
int idx = 0;
byte[] nab = new byte[ab.length - delbytes];
int nab_off = 0;
for( int i=0; i<size; i++ )
{
int pos = fapos[i];
if ( ( pos & 0x80000000 ) == 0 )
{
int ablength = ( i+1 < size ? fapos[i+1] & 0x7fffffff : ab.length ) - pos;
System.arraycopy( ab, pos, nab, nab_off, ablength );
nfaid[idx] = faid[i];
nfapos[idx] = nab_off;
nab_off += ablength;
idx++;
}
}
faid = nfaid;
fapos = nfapos;
ab = nab;
}
size = nsize;
delcount = 0;
delbytes = 0;
p2size = 0x40000000;
while( p2size > size ) p2size >>= 1;
}
}
void unGhost()
{
ghost = false;
delcount = 0;
delbytes = 0;
for( int i=0; i<size; i++ )
{
fapos[i] &= 0x7fffffff; // clear deleted flags
}
}
public List<OsmNode> getPositions( OsmNodesMap nodesMap )
{
ArrayList<OsmNode> positions = new ArrayList<OsmNode>();
for( int i=0; i<size; i++ )
{
int id32 = faid[i];
long id64 = expandId( id32 );
OsmNode n = new OsmNode( id64 );
n.setHollow();
nodesMap.put( n );
positions.add( n );
}
return positions;
}
private long expandId( int id32 )
{
int lon32 = lonIdxBase + (short)(id32 >> 16);
int lat32 = latIdxBase + (short)((id32 & 0xffff) ^ 0x8000);
return ((long)lon32)<<32 | lat32;
}
private int shrinkId( long id64 )
{
int lon32 = (int)(id64 >> 32);
int lat32 = (int)(id64 & 0xffffffff);
return (lon32 - lonIdxBase)<<16 | ( ( (lat32 - latIdxBase) & 0xffff) ^ 0x8000);
}
public boolean hasMoreData()
{
return aboffset < aboffsetEnd;
}
}

View file

@ -1,291 +1,332 @@
/**
* Efficient cache or osmnodes
*
* @author ab
*/
package btools.mapaccess;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public final class NodesCache
{
private File segmentDir;
private File secondarySegmentsDir = null;
private OsmNodesMap nodesMap;
private int lookupVersion;
private int lookupMinorVersion;
private boolean carMode;
private boolean forceSecondaryData;
private String currentFileName;
private HashMap<String,PhysicalFile> fileCache;
private byte[] iobuffer;
private OsmFile[][] fileRows;
private ArrayList<MicroCache> segmentList = new ArrayList<MicroCache>();
public DistanceChecker distanceChecker;
public boolean oom_carsubset_hint = false;
public boolean first_file_access_failed = false;
public String first_file_access_name;
private long cacheSum = 0;
private boolean garbageCollectionEnabled = false;
public NodesCache( String segmentDir, OsmNodesMap nodesMap, int lookupVersion, int minorVersion, boolean carMode, boolean forceSecondaryData, NodesCache oldCache )
{
this.segmentDir = new File( segmentDir );
this.nodesMap = nodesMap;
this.lookupVersion = lookupVersion;
this.lookupMinorVersion = minorVersion;
this.carMode = carMode;
this.forceSecondaryData = forceSecondaryData;
first_file_access_failed = false;
first_file_access_name = null;
if ( !this.segmentDir.isDirectory() ) throw new RuntimeException( "segment directory " + segmentDir + " does not exist" );
if ( oldCache != null )
{
fileCache = oldCache.fileCache;
iobuffer = oldCache.iobuffer;
oom_carsubset_hint = oldCache.oom_carsubset_hint;
secondarySegmentsDir = oldCache.secondarySegmentsDir;
// re-use old, virgin caches
fileRows = oldCache.fileRows;
for( OsmFile[] fileRow : fileRows )
{
if ( fileRow == null ) continue;
for( OsmFile osmf : fileRow )
{
cacheSum += osmf.setGhostState();
}
}
}
else
{
fileCache = new HashMap<String,PhysicalFile>(4);
fileRows = new OsmFile[180][];
iobuffer = new byte[65636];
secondarySegmentsDir = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
}
}
private File getFileFromSegmentDir( String filename )
{
if ( forceSecondaryData )
{
return new File( secondarySegmentsDir, filename );
}
File f = new File( segmentDir, filename );
if ( secondarySegmentsDir != null && !f.exists() )
{
File f2 = new File( secondarySegmentsDir, filename );
if ( f2.exists() ) return f2;
}
return f;
}
// if the cache sum exceeded a threshold,
// clean all ghosts and enable garbage collection
private void checkEnableCacheCleaning()
{
if ( cacheSum < 200000 || garbageCollectionEnabled ) return;
for( int i=0; i<fileRows.length; i++ )
{
OsmFile[] fileRow = fileRows[i];
if ( fileRow == null ) continue;
int nghosts = 0;
for( OsmFile osmf : fileRow )
{
if ( osmf.ghost ) nghosts++;
else osmf.cleanAll();
}
if ( nghosts == 0 ) continue;
int j=0;
OsmFile[] frow = new OsmFile[fileRow.length-nghosts];
for( OsmFile osmf : fileRow )
{
if ( osmf.ghost ) continue;
frow[j++] = osmf;
}
fileRows[i] = frow;
}
garbageCollectionEnabled = true;
}
public int loadSegmentFor( int ilon, int ilat )
{
MicroCache mc = getSegmentFor( ilon, ilat );
return mc == null ? 0 : mc.getSize();
}
public MicroCache getSegmentFor( int ilon, int ilat )
{
try
{
int lonIdx80 = ilon/12500;
int latIdx80 = ilat/12500;
int lonDegree = lonIdx80/80;
int latDegree = latIdx80/80;
OsmFile osmf = null;
OsmFile[] fileRow = fileRows[latDegree];
int ndegrees = fileRow == null ? 0 : fileRow.length;
for( int i=0; i<ndegrees; i++ )
{
if ( fileRow[i].lonDegree == lonDegree )
{
osmf = fileRow[i];
break;
}
}
if ( osmf == null )
{
osmf = fileForSegment( lonDegree, latDegree );
OsmFile[] newFileRow = new OsmFile[ndegrees+1];
for( int i=0; i<ndegrees; i++ )
{
newFileRow[i] = fileRow[i];
}
newFileRow[ndegrees] = osmf;
fileRows[latDegree] = newFileRow;
}
osmf.ghost = false;
currentFileName = osmf.filename;
if ( osmf.microCaches == null )
{
return null;
}
int subIdx = (latIdx80-80*latDegree)*80 + (lonIdx80-80*lonDegree);
MicroCache segment = osmf.microCaches[subIdx];
if ( segment == null )
{
// nodesMap.removeCompleteNodes();
checkEnableCacheCleaning();
segment = new MicroCache( osmf, lonIdx80, latIdx80, iobuffer );
cacheSum += segment.getDataSize();
osmf.microCaches[subIdx] = segment;
segmentList.add( segment );
}
else if ( segment.ghost )
{
segment.unGhost();
segmentList.add( segment );
}
return segment;
}
catch( RuntimeException re )
{
throw re;
}
catch( Exception e )
{
throw new RuntimeException( "error reading datafile " + currentFileName + ": " + e );
}
}
public boolean obtainNonHollowNode( OsmNode node )
{
if ( !node.isHollow() ) return true;
MicroCache segment = getSegmentFor( node.ilon, node.ilat );
if ( segment == null )
{
return false;
}
segment.fillNode( node, nodesMap, distanceChecker, garbageCollectionEnabled );
return !node.isHollow();
}
private OsmFile fileForSegment( int lonDegree, int latDegree ) throws Exception
{
int lonMod5 = lonDegree % 5;
int latMod5 = latDegree % 5;
int tileIndex = lonMod5 * 5 + latMod5;
int lon = lonDegree - 180 - lonMod5;
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
int lat = latDegree - 90 - latMod5;
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
String filenameBase = slon + "_" + slat;
currentFileName = filenameBase + ".rd5/cd5";
PhysicalFile ra = null;
if ( !fileCache.containsKey( filenameBase ) )
{
File f = null;
if ( carMode )
{
File carFile = getFileFromSegmentDir( "carsubset/" + filenameBase + ".cd5" );
if ( carFile.exists() ) f = carFile;
}
if ( f == null )
{
File fullFile = getFileFromSegmentDir( filenameBase + ".rd5" );
if ( fullFile.exists() ) f = fullFile;
if ( carMode && f != null ) oom_carsubset_hint = true;
}
if ( f != null )
{
currentFileName = f.getName();
ra = new PhysicalFile( f, iobuffer, lookupVersion, lookupMinorVersion );
}
fileCache.put( filenameBase, ra );
}
ra = fileCache.get( filenameBase );
OsmFile osmf = new OsmFile( ra, tileIndex, iobuffer );
osmf.lonDegree = lonDegree;
osmf.latDegree = latDegree;
if ( first_file_access_name == null )
{
first_file_access_name = currentFileName;
first_file_access_failed = osmf.filename == null;
}
return osmf;
}
public List<OsmNode> getAllNodes()
{
List<OsmNode> all = new ArrayList<OsmNode>();
for( MicroCache segment : segmentList )
{
List<OsmNode> positions = segment.getPositions( nodesMap );
all.addAll( positions );
}
return all;
}
public void close()
{
for( PhysicalFile f: fileCache.values() )
{
try
{
if ( f != null ) f.ra.close();
}
catch( IOException ioe )
{
// ignore
}
}
}
}
/**
* Efficient cache or osmnodes
*
* @author ab
*/
package btools.mapaccess;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import btools.codec.DataBuffers;
import btools.codec.MicroCache;
import btools.codec.WaypointMatcher;
import btools.expressions.BExpressionContextWay;
public final class NodesCache
{
private File segmentDir;
private File secondarySegmentsDir = null;
private OsmNodesMap nodesMap;
private BExpressionContextWay expCtxWay;
private int lookupVersion;
private int lookupMinorVersion;
private boolean carMode;
private boolean forceSecondaryData;
private String currentFileName;
private HashMap<String, PhysicalFile> fileCache;
private DataBuffers dataBuffers;
private OsmFile[][] fileRows;
private ArrayList<MicroCache> segmentList = new ArrayList<MicroCache>();
public DistanceChecker distanceChecker;
public WaypointMatcher waypointMatcher;
public boolean oom_carsubset_hint = false;
public boolean first_file_access_failed = false;
public String first_file_access_name;
private long cacheSum = 0;
private boolean garbageCollectionEnabled = false;
public NodesCache( String segmentDir, OsmNodesMap nodesMap, BExpressionContextWay ctxWay, boolean carMode, boolean forceSecondaryData,
NodesCache oldCache )
{
this.segmentDir = new File( segmentDir );
this.nodesMap = nodesMap;
this.expCtxWay = ctxWay;
this.lookupVersion = ctxWay.meta.lookupVersion;
this.lookupMinorVersion = ctxWay.meta.lookupMinorVersion;
this.carMode = carMode;
this.forceSecondaryData = forceSecondaryData;
first_file_access_failed = false;
first_file_access_name = null;
if ( !this.segmentDir.isDirectory() )
throw new RuntimeException( "segment directory " + segmentDir + " does not exist" );
if ( oldCache != null )
{
fileCache = oldCache.fileCache;
dataBuffers = oldCache.dataBuffers;
oom_carsubset_hint = oldCache.oom_carsubset_hint;
secondarySegmentsDir = oldCache.secondarySegmentsDir;
// re-use old, virgin caches
fileRows = oldCache.fileRows;
for ( OsmFile[] fileRow : fileRows )
{
if ( fileRow == null )
continue;
for ( OsmFile osmf : fileRow )
{
cacheSum += osmf.setGhostState();
}
}
}
else
{
fileCache = new HashMap<String, PhysicalFile>( 4 );
fileRows = new OsmFile[180][];
dataBuffers = new DataBuffers();
secondarySegmentsDir = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
}
}
private File getFileFromSegmentDir( String filename )
{
if ( forceSecondaryData )
{
return new File( secondarySegmentsDir, filename );
}
File f = new File( segmentDir, filename );
if ( secondarySegmentsDir != null && !f.exists() )
{
File f2 = new File( secondarySegmentsDir, filename );
if ( f2.exists() )
return f2;
}
return f;
}
// if the cache sum exceeded a threshold,
// clean all ghosts and enable garbage collection
private void checkEnableCacheCleaning()
{
if ( cacheSum < 500000 || garbageCollectionEnabled )
return;
for ( int i = 0; i < fileRows.length; i++ )
{
OsmFile[] fileRow = fileRows[i];
if ( fileRow == null )
continue;
int nghosts = 0;
for ( OsmFile osmf : fileRow )
{
if ( osmf.ghost )
nghosts++;
else
osmf.cleanAll();
}
if ( nghosts == 0 )
continue;
int j = 0;
OsmFile[] frow = new OsmFile[fileRow.length - nghosts];
for ( OsmFile osmf : fileRow )
{
if ( osmf.ghost )
continue;
frow[j++] = osmf;
}
fileRows[i] = frow;
}
garbageCollectionEnabled = true;
}
public int loadSegmentFor( int ilon, int ilat )
{
MicroCache mc = getSegmentFor( ilon, ilat );
return mc == null ? 0 : mc.getSize();
}
public MicroCache getSegmentFor( int ilon, int ilat )
{
try
{
int lonDegree = ilon / 1000000;
int latDegree = ilat / 1000000;
OsmFile osmf = null;
OsmFile[] fileRow = fileRows[latDegree];
int ndegrees = fileRow == null ? 0 : fileRow.length;
for ( int i = 0; i < ndegrees; i++ )
{
if ( fileRow[i].lonDegree == lonDegree )
{
osmf = fileRow[i];
break;
}
}
if ( osmf == null )
{
osmf = fileForSegment( lonDegree, latDegree );
OsmFile[] newFileRow = new OsmFile[ndegrees + 1];
for ( int i = 0; i < ndegrees; i++ )
{
newFileRow[i] = fileRow[i];
}
newFileRow[ndegrees] = osmf;
fileRows[latDegree] = newFileRow;
}
osmf.ghost = false;
currentFileName = osmf.filename;
if ( !osmf.hasData() )
{
return null;
}
MicroCache segment = osmf.getMicroCache( ilon, ilat );
if ( segment == null )
{
checkEnableCacheCleaning();
segment = osmf.createMicroCache( ilon, ilat, dataBuffers, expCtxWay, waypointMatcher );
cacheSum += segment.getDataSize();
if ( segment.getSize() > 0 )
{
segmentList.add( segment );
}
}
else if ( segment.ghost )
{
segment.unGhost();
if ( segment.getSize() > 0 )
{
segmentList.add( segment );
}
}
return segment;
}
catch (RuntimeException re)
{
throw re;
}
catch (Exception e)
{
throw new RuntimeException( "error reading datafile " + currentFileName + ": ", e );
}
}
public boolean obtainNonHollowNode( OsmNode node )
{
if ( !node.isHollow() )
return true;
MicroCache segment = getSegmentFor( node.ilon, node.ilat );
if ( segment == null )
{
return false;
}
long id = node.getIdFromPos();
if ( segment.getAndClear( id ) )
{
node.parseNodeBody( segment, nodesMap, distanceChecker );
}
if ( garbageCollectionEnabled ) // garbage collection
{
segment.collect( segment.getSize() >> 1 );
}
return !node.isHollow();
}
private OsmFile fileForSegment( int lonDegree, int latDegree ) throws Exception
{
int lonMod5 = lonDegree % 5;
int latMod5 = latDegree % 5;
int lon = lonDegree - 180 - lonMod5;
String slon = lon < 0 ? "W" + ( -lon ) : "E" + lon;
int lat = latDegree - 90 - latMod5;
String slat = lat < 0 ? "S" + ( -lat ) : "N" + lat;
String filenameBase = slon + "_" + slat;
currentFileName = filenameBase + ".rd5/cd5";
PhysicalFile ra = null;
if ( !fileCache.containsKey( filenameBase ) )
{
File f = null;
if ( carMode )
{
File carFile = getFileFromSegmentDir( "carsubset/" + filenameBase + ".cd5" );
if ( carFile.exists() )
f = carFile;
}
if ( f == null )
{
File fullFile = getFileFromSegmentDir( filenameBase + ".rd5" );
if ( fullFile.exists() )
f = fullFile;
if ( carMode && f != null )
oom_carsubset_hint = true;
}
if ( f != null )
{
currentFileName = f.getName();
ra = new PhysicalFile( f, dataBuffers, lookupVersion, lookupMinorVersion );
}
fileCache.put( filenameBase, ra );
}
ra = fileCache.get( filenameBase );
OsmFile osmf = new OsmFile( ra, lonDegree, latDegree, dataBuffers );
if ( first_file_access_name == null )
{
first_file_access_name = currentFileName;
first_file_access_failed = osmf.filename == null;
}
return osmf;
}
public List<OsmNode> getAllNodes()
{
List<OsmNode> all = new ArrayList<OsmNode>();
for ( MicroCache segment : segmentList )
{
ArrayList<OsmNode> positions = new ArrayList<OsmNode>();
int size = segment.getSize();
for ( int i = 0; i < size; i++ )
{
long id = segment.getIdForIndex( i );
OsmNode n = new OsmNode( id );
n.setHollow();
nodesMap.put( n );
positions.add( n );
}
all.addAll( positions );
}
return all;
}
public void close()
{
for ( PhysicalFile f : fileCache.values() )
{
try
{
if ( f != null )
f.ra.close();
}
catch (IOException ioe)
{
// ignore
}
}
}
}

View file

@ -1,124 +1,208 @@
/**
* cache for a single square
*
* @author ab
*/
package btools.mapaccess;
import java.io.IOException;
import java.io.RandomAccessFile;
import btools.util.ByteDataReader;
import btools.util.Crc32;
final class OsmFile
{
private RandomAccessFile is = null;
private long fileOffset;
private int[] posIdx;
public MicroCache[] microCaches;
public int lonDegree;
public int latDegree;
public String filename;
public boolean ghost = false;
public OsmFile( PhysicalFile rafile, int tileIndex, byte[] iobuffer ) throws Exception
{
if ( rafile != null )
{
filename = rafile.fileName;
long[] index = rafile.fileIndex;
fileOffset = tileIndex > 0 ? index[ tileIndex-1 ] : 200L;
if ( fileOffset == index[ tileIndex] ) return; // empty
is = rafile.ra;
posIdx = new int[6400];
microCaches = new MicroCache[6400];
is.seek( fileOffset );
is.readFully( iobuffer, 0, 25600 );
if ( rafile.fileHeaderCrcs != null )
{
int headerCrc = Crc32.crc( iobuffer, 0, 25600 );
if ( rafile.fileHeaderCrcs[tileIndex] != headerCrc )
{
throw new IOException( "sub index checksum error" );
}
}
ByteDataReader dis = new ByteDataReader( iobuffer );
for( int i=0; i<6400; i++ )
{
posIdx[i] = dis.readInt();
}
}
}
private int getPosIdx( int idx )
{
return idx == -1 ? 25600 : posIdx[idx];
}
public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws Exception
{
int startPos = getPosIdx(subIdx-1);
int endPos = getPosIdx(subIdx);
int size = endPos-startPos;
if ( size > 0 )
{
is.seek( fileOffset + startPos );
if ( size <= iobuffer.length )
{
is.readFully( iobuffer, 0, size );
}
}
return size;
}
// set this OsmFile to ghost-state:
long setGhostState()
{
long sum = 0;
ghost = true;
int nc = microCaches == null ? 0 : microCaches.length;
for( int i=0; i< nc; i++ )
{
MicroCache mc = microCaches[i];
if ( mc == null ) continue;
if ( mc.virgin )
{
mc.ghost = true;
sum += mc.getDataSize();
}
else
{
microCaches[i] = null;
}
}
return sum;
}
void cleanAll()
{
int nc = microCaches == null ? 0 : microCaches.length;
for( int i=0; i< nc; i++ )
{
MicroCache mc = microCaches[i];
if ( mc == null ) continue;
if ( mc.ghost )
{
microCaches[i] = null;
}
else
{
mc.collect();
}
}
}
}
/**
* cache for a single square
*
* @author ab
*/
package btools.mapaccess;
import java.io.IOException;
import java.io.RandomAccessFile;
import btools.codec.DataBuffers;
import btools.codec.MicroCache;
import btools.codec.MicroCache1;
import btools.codec.MicroCache2;
import btools.codec.TagValueValidator;
import btools.codec.WaypointMatcher;
import btools.util.ByteDataReader;
import btools.util.Crc32;
final class OsmFile
{
private RandomAccessFile is = null;
private long fileOffset;
private int[] posIdx;
private MicroCache[] microCaches;
public int lonDegree;
public int latDegree;
public String filename;
public boolean ghost = false;
private int divisor;
private int cellsize;
private int ncaches;
private int indexsize;
public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws Exception
{
this.lonDegree = lonDegree;
this.latDegree = latDegree;
int lonMod5 = lonDegree % 5;
int latMod5 = latDegree % 5;
int tileIndex = lonMod5 * 5 + latMod5;
if ( rafile != null )
{
divisor = rafile.divisor;
cellsize = 1000000 / divisor;
ncaches = divisor * divisor;
indexsize = ncaches * 4;
byte[] iobuffer = dataBuffers.iobuffer;
filename = rafile.fileName;
long[] index = rafile.fileIndex;
fileOffset = tileIndex > 0 ? index[tileIndex - 1] : 200L;
if ( fileOffset == index[tileIndex] )
return; // empty
is = rafile.ra;
posIdx = new int[ncaches];
microCaches = new MicroCache[ncaches];
is.seek( fileOffset );
is.readFully( iobuffer, 0, indexsize );
if ( rafile.fileHeaderCrcs != null )
{
int headerCrc = Crc32.crc( iobuffer, 0, indexsize );
if ( rafile.fileHeaderCrcs[tileIndex] != headerCrc )
{
throw new IOException( "sub index checksum error" );
}
}
ByteDataReader dis = new ByteDataReader( iobuffer );
for ( int i = 0; i < ncaches; i++ )
{
posIdx[i] = dis.readInt();
}
}
}
public boolean hasData()
{
return microCaches != null;
}
public MicroCache getMicroCache( int ilon, int ilat )
{
int lonIdx = ilon / cellsize;
int latIdx = ilat / cellsize;
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
return microCaches[subIdx];
}
public MicroCache createMicroCache( int ilon, int ilat, DataBuffers dataBuffers, TagValueValidator wayValidator, WaypointMatcher waypointMatcher )
throws Exception
{
int lonIdx = ilon / cellsize;
int latIdx = ilat / cellsize;
MicroCache segment = createMicroCache( lonIdx, latIdx, dataBuffers, wayValidator, waypointMatcher, true );
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
microCaches[subIdx] = segment;
return segment;
}
private int getPosIdx( int idx )
{
return idx == -1 ? indexsize : posIdx[idx];
}
public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws Exception
{
int startPos = getPosIdx( subIdx - 1 );
int endPos = getPosIdx( subIdx );
int size = endPos - startPos;
if ( size > 0 )
{
is.seek( fileOffset + startPos );
if ( size <= iobuffer.length )
{
is.readFully( iobuffer, 0, size );
}
}
return size;
}
public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator,
WaypointMatcher waypointMatcher, boolean reallyDecode ) throws Exception
{
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
byte[] ab = dataBuffers.iobuffer;
int asize = getDataInputForSubIdx( subIdx, ab );
if ( asize == 0 )
{
return MicroCache.emptyCache();
}
if ( asize > ab.length )
{
ab = new byte[asize];
asize = getDataInputForSubIdx( subIdx, ab );
}
// hack: the checksum contains the information
// which type of microcache we have
int crcData = Crc32.crc( ab, 0, asize - 4 );
int crcFooter = new ByteDataReader( ab, asize - 4 ).readInt();
if ( crcData == crcFooter )
{
return reallyDecode ? new MicroCache1( ab, lonIdx, latIdx ) : null;
}
if ( ( crcData ^ 2 ) == crcFooter )
{
return reallyDecode ? new MicroCache2( dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher ) : null;
}
throw new IOException( "checkum error" );
}
// set this OsmFile to ghost-state:
long setGhostState()
{
long sum = 0;
ghost = true;
int nc = microCaches == null ? 0 : microCaches.length;
for ( int i = 0; i < nc; i++ )
{
MicroCache mc = microCaches[i];
if ( mc == null )
continue;
if ( mc.virgin )
{
mc.ghost = true;
sum += mc.getDataSize();
}
else
{
microCaches[i] = null;
}
}
return sum;
}
void cleanAll()
{
int nc = microCaches == null ? 0 : microCaches.length;
for ( int i = 0; i < nc; i++ )
{
MicroCache mc = microCaches[i];
if ( mc == null )
continue;
if ( mc.ghost )
{
microCaches[i] = null;
}
else
{
mc.collect( 0 );
}
}
}
}

View file

@ -5,6 +5,8 @@
*/
package btools.mapaccess;
import btools.util.ByteDataReader;
public class OsmLink
{
@ -14,32 +16,77 @@ public class OsmLink
*/
public byte[] descriptionBitmap;
/**
* The target is either the next link or the target node
*/
public OsmNode targetNode;
public OsmLink next;
public byte[] firsttransferBytes;
public OsmLinkHolder firstlinkholder = null;
final public OsmTransferNode decodeFirsttransfer()
public byte[] geometry;
public boolean counterLinkWritten;
public boolean hasNewGeometry; // preliminary
public byte state;
public void setGeometry( byte[] geometry )
{
return firsttransferBytes == null ? null : OsmTransferNode.decode( firsttransferBytes );
this.geometry = geometry;
hasNewGeometry = true;
}
final public OsmTransferNode decodeFirsttransfer( OsmNode sourceNode )
{
if ( geometry == null ) return null;
if ( hasNewGeometry )
{
OsmTransferNode firstTransferNode = null;
OsmTransferNode lastTransferNode = null;
OsmNode startnode = counterLinkWritten ? targetNode : sourceNode;
ByteDataReader r = new ByteDataReader( geometry );
int olon = startnode.ilon;
int olat = startnode.ilat;
int oselev = startnode.selev;
while ( r.hasMoreData() )
{
OsmTransferNode trans = new OsmTransferNode();
trans.ilon = olon + r.readVarLengthSigned();
trans.ilat = olat + r.readVarLengthSigned();
trans.descriptionBitmap = descriptionBitmap;
trans.selev = (short)(oselev + r.readVarLengthSigned());
olon = trans.ilon;
olat = trans.ilat;
oselev = trans.selev;
if ( counterLinkWritten ) // reverse chaining
{
trans.next = firstTransferNode;
firstTransferNode = trans;
}
else
{
if ( lastTransferNode == null )
{
firstTransferNode = trans;
}
else
{
lastTransferNode.next = trans;
}
lastTransferNode = trans;
}
}
return firstTransferNode;
}
return OsmTransferNode.decode( geometry );
}
final public void encodeFirsttransfer( OsmTransferNode firsttransfer )
{
if ( firsttransfer == null ) firsttransferBytes = null;
else firsttransferBytes = OsmTransferNode.encode( firsttransfer );
if ( firsttransfer == null ) geometry = null;
else geometry = OsmTransferNode.encode( firsttransfer );
}
public boolean counterLinkWritten;
public byte state;
public OsmLinkHolder firstlinkholder = null;
final public void addLinkHolder( OsmLinkHolder holder )
{
if ( firstlinkholder != null ) { holder.setNextForLink( firstlinkholder ); }

View file

@ -1,363 +1,471 @@
/**
* Container for an osm node
*
* @author ab
*/
package btools.mapaccess;
import btools.util.ByteArrayUnifier;
public class OsmNode implements OsmPos
{
public static final int EXTERNAL_BITMASK = 0x80; // old semantic
public static final int SIGNLON_BITMASK = 0x80;
public static final int SIGNLAT_BITMASK = 0x40;
public static final int TRANSFERNODE_BITMASK = 0x20;
public static final int WRITEDESC_BITMASK = 0x10;
public static final int SKIPDETAILS_BITMASK = 0x08;
public static final int NODEDESC_BITMASK = 0x04;
public static final int RESERVED1_BITMASK = 0x02;
public static final int RESERVED2_BITMASK = 0x01;
public OsmNode()
{
}
public OsmNode( int ilon, int ilat )
{
this.ilon = ilon;
this.ilat = ilat;
}
public OsmNode( long id )
{
ilon = (int)(id >> 32);
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 int getILat()
{
return ilat;
}
public int getILon()
{
return ilon;
}
public short getSElev()
{
return selev;
}
public double getElev()
{
return selev / 4.;
}
/**
* The links to other nodes
*/
public OsmLink firstlink = null;
// preliminry in forward order to avoid regressions
public void addLink( OsmLink link )
{
if ( firstlink == null )
{
firstlink = link;
}
else
{
OsmLink l = firstlink;
while( l.next != null ) l = l.next;
l.next = 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 int calcDistance( OsmPos p )
{
double l = (ilat-90000000) * 0.00000001234134;
double l2 = l*l;
double l4 = l2*l2;
double coslat = 1.- l2 + l4 / 6.;
double dlat = (ilat - p.getILat() )/1000000.;
double dlon = (ilon - p.getILon() )/1000000. * coslat;
double d = Math.sqrt( dlat*dlat + dlon*dlon ) * (6378000. / 57.3);
return (int)(d + 1.0 );
}
public String toString()
{
return "" + getIdFromPos();
}
public void parseNodeBody( MicroCache is, OsmNodesMap hollowNodes, DistanceChecker dc )
{
ByteArrayUnifier abUnifier = hollowNodes.getByteArrayUnifier();
selev = is.readShort();
while( is.hasMoreData() )
{
int ilonref = ilon;
int ilatref = ilat;
boolean counterLinkWritten = false;
OsmTransferNode firstTransferNode = null;
OsmTransferNode lastTransferNode = null;
int linklon;
int linklat;
byte[] description = null;
for(;;)
{
int bitField = is.readByte();
int dlon = is.readVarLengthUnsigned();
int dlat = is.readVarLengthUnsigned();
if ( (bitField & SIGNLON_BITMASK) != 0 ) { dlon = -dlon;}
if ( (bitField & SIGNLAT_BITMASK) != 0 ) { dlat = -dlat;}
linklon = ilonref + dlon;
linklat = ilatref + dlat;
ilonref = linklon;
ilatref = linklat;
// read variable length or old 8 byte fixed, and ensure that 8 bytes is only fixed
if ( (bitField & WRITEDESC_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
description = abUnifier.unify( ab );
}
if ( (bitField & NODEDESC_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
nodeDescription = abUnifier.unify( ab );
}
if ( (bitField & RESERVED1_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
}
if ( (bitField & RESERVED2_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
}
if ( (bitField & SKIPDETAILS_BITMASK ) != 0 )
{
counterLinkWritten = true;
}
if ( description == null && !counterLinkWritten ) throw new IllegalArgumentException( "internal error: missing way description!" );
boolean isTransfer = (bitField & TRANSFERNODE_BITMASK ) != 0;
if ( isTransfer )
{
OsmTransferNode trans = new OsmTransferNode();
trans.ilon = linklon;
trans.ilat = linklat;
trans.descriptionBitmap = description;
trans.selev = (short)(selev + is.readVarLengthSigned());
if ( lastTransferNode == null )
{
firstTransferNode = trans;
}
else
{
lastTransferNode.next = trans;
}
lastTransferNode = trans;
}
else
{
break;
}
}
// performance shortcut: ignore link if out of reach
if ( dc != null && !counterLinkWritten )
{
if ( !dc.isWithinRadius( ilon, ilat, firstTransferNode, linklon, linklat ) )
{
continue;
}
}
if ( linklon == ilon && linklat == ilat )
{
continue; // skip self-ref
}
// first check the known links for that target
OsmLink link = getCompatibleLink( linklon, linklat, counterLinkWritten, 2 );
if ( link == null ) // .. not found, then check the hollow nodes
{
long targetNodeId = ((long)linklon)<<32 | linklat;
OsmNode tn = hollowNodes.get( targetNodeId ); // target node
if ( tn == null ) // node not yet known, create a new hollow proxy
{
tn = new OsmNode(linklon, linklat);
tn.setHollow();
hollowNodes.put( tn );
}
link = new OsmLink();
link.targetNode = tn;
link.counterLinkWritten = counterLinkWritten;
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, !counterLinkWritten, 1 );
if ( rlink == null ) // .. not found, create it
{
rlink = new OsmLink();
rlink.targetNode = this;
rlink.counterLinkWritten = !counterLinkWritten;
rlink.state = 2;
link.targetNode.addLink( rlink );
}
if ( !counterLinkWritten )
{
// we have the data for that link, so fill both the link ..
link.descriptionBitmap = description;
link.encodeFirsttransfer(firstTransferNode);
// .. and the reverse
if ( rlink.counterLinkWritten )
{
rlink.descriptionBitmap = description; // default for no transfer-nodes
OsmTransferNode previous = null;
OsmTransferNode rtrans = null;
for( OsmTransferNode trans = firstTransferNode; trans != null; trans = trans.next )
{
if ( previous == null )
{
rlink.descriptionBitmap = trans.descriptionBitmap;
}
else
{
previous.descriptionBitmap = trans.descriptionBitmap;
}
rtrans = new OsmTransferNode();
rtrans.ilon = trans.ilon;
rtrans.ilat = trans.ilat;
rtrans.selev = trans.selev;
rtrans.next = previous;
rtrans.descriptionBitmap = description;
previous = rtrans;
}
rlink.encodeFirsttransfer(rtrans);
}
}
}
if ( dc == null )
{
hollowNodes.remove( this );
}
}
public boolean isHollow()
{
return selev == -12345;
}
public void setHollow()
{
selev = -12345;
}
public long getIdFromPos()
{
return ((long)ilon)<<32 | ilat;
}
public void unlinkLink( OsmLink link )
{
if ( link == firstlink )
{
firstlink = link.next;
return;
}
for( OsmLink l = firstlink; l != null; l = l.next )
{
if ( l.next == link )
{
l.next = link.next;
return;
}
}
}
@Override
public boolean equals( Object o )
{
if ( o instanceof OsmNode )
{
OsmNode n = (OsmNode)o;
return n.ilon == ilon && n.ilat == ilat;
}
return false;
}
@Override
public int hashCode( )
{
return ilon + ilat;
}
}
/**
* Container for an osm node
*
* @author ab
*/
package btools.mapaccess;
import btools.codec.MicroCache;
import btools.codec.MicroCache1;
import btools.codec.MicroCache2;
import btools.util.ByteArrayUnifier;
public class OsmNode implements OsmPos
{
public static final int EXTERNAL_BITMASK = 0x80; // old semantic
public static final int SIGNLON_BITMASK = 0x80;
public static final int SIGNLAT_BITMASK = 0x40;
public static final int TRANSFERNODE_BITMASK = 0x20;
public static final int WRITEDESC_BITMASK = 0x10;
public static final int SKIPDETAILS_BITMASK = 0x08;
public static final int NODEDESC_BITMASK = 0x04;
public static final int RESERVED1_BITMASK = 0x02;
public static final int RESERVED2_BITMASK = 0x01;
public OsmNode()
{
}
public OsmNode( int ilon, int ilat )
{
this.ilon = ilon;
this.ilat = ilat;
}
public OsmNode( long id )
{
ilon = (int) ( id >> 32 );
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 int getILat()
{
return ilat;
}
public int getILon()
{
return ilon;
}
public short getSElev()
{
return selev;
}
public double getElev()
{
return selev / 4.;
}
/**
* The links to other nodes
*/
public OsmLink firstlink = null;
// preliminry in forward order to avoid regressions
public void addLink( OsmLink link )
{
if ( firstlink == null )
{
firstlink = link;
}
else
{
OsmLink l = firstlink;
while (l.next != null)
l = l.next;
l.next = 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 int calcDistance( OsmPos p )
{
double l = ( ilat - 90000000 ) * 0.00000001234134;
double l2 = l * l;
double l4 = l2 * l2;
double coslat = 1. - l2 + l4 / 6.;
double dlat = ( ilat - p.getILat() ) / 1000000.;
double dlon = ( ilon - p.getILon() ) / 1000000. * coslat;
double d = Math.sqrt( dlat * dlat + dlon * dlon ) * ( 6378000. / 57.3 );
return (int) ( d + 1.0 );
}
public String toString()
{
return "" + getIdFromPos();
}
public void parseNodeBody( MicroCache mc, OsmNodesMap hollowNodes, DistanceChecker dc )
{
if ( mc instanceof MicroCache1 )
{
parseNodeBody1( (MicroCache1) mc, hollowNodes, dc );
}
else if ( mc instanceof MicroCache2 )
{
parseNodeBody2( (MicroCache2) mc, hollowNodes, dc );
}
else
throw new IllegalArgumentException( "unknown cache version: " + mc.getClass() );
}
public void parseNodeBody2( MicroCache2 mc, OsmNodesMap hollowNodes, DistanceChecker dc )
{
ByteArrayUnifier abUnifier = hollowNodes.getByteArrayUnifier();
selev = mc.readShort();
int nodeDescSize = mc.readVarLengthUnsigned();
nodeDescription = nodeDescSize == 0 ? null : mc.readUnified( nodeDescSize, abUnifier );
while (mc.hasMoreData())
{
// read link data
int endPointer = mc.getEndPointer();
int linklon = ilon + mc.readVarLengthSigned();
int linklat = ilat + mc.readVarLengthSigned();
int sizecode = mc.readVarLengthUnsigned();
boolean isReverse = ( sizecode & 1 ) != 0;
byte[] description = null;
int descSize = sizecode >> 1;
if ( descSize > 0 )
{
description = mc.readUnified( descSize, abUnifier );
}
byte[] geometry = mc.readDataUntil( endPointer );
// preliminary hack: way-point-matching not here (done at decoding time)
if ( dc != null )
continue;
if ( linklon == ilon && linklat == ilat )
{
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
{
long targetNodeId = ( (long) linklon ) << 32 | linklat;
OsmNode tn = hollowNodes.get( targetNodeId ); // target node
if ( tn == null ) // node not yet known, create a new hollow proxy
{
tn = new OsmNode( linklon, linklat );
tn.setHollow();
hollowNodes.put( tn );
}
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
{
rlink = new OsmLink();
rlink.targetNode = this;
rlink.counterLinkWritten = !isReverse;
rlink.state = 2;
link.targetNode.addLink( rlink );
}
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 );
}
}
}
if ( dc == null )
{
hollowNodes.remove( this );
}
}
public void parseNodeBody1( MicroCache1 is, OsmNodesMap hollowNodes, DistanceChecker dc )
{
ByteArrayUnifier abUnifier = hollowNodes.getByteArrayUnifier();
selev = is.readShort();
while (is.hasMoreData())
{
int ilonref = ilon;
int ilatref = ilat;
boolean counterLinkWritten = false;
OsmTransferNode firstTransferNode = null;
OsmTransferNode lastTransferNode = null;
int linklon;
int linklat;
byte[] description = null;
for ( ;; )
{
int bitField = is.readByte();
int dlon = is.readVarLengthUnsigned();
int dlat = is.readVarLengthUnsigned();
if ( ( bitField & SIGNLON_BITMASK ) != 0 )
{
dlon = -dlon;
}
if ( ( bitField & SIGNLAT_BITMASK ) != 0 )
{
dlat = -dlat;
}
linklon = ilonref + dlon;
linklat = ilatref + dlat;
ilonref = linklon;
ilatref = linklat;
// read variable length or old 8 byte fixed, and ensure that 8 bytes is
// only fixed
if ( ( bitField & WRITEDESC_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
description = abUnifier.unify( ab );
}
if ( ( bitField & NODEDESC_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
nodeDescription = abUnifier.unify( ab );
}
if ( ( bitField & RESERVED1_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
}
if ( ( bitField & RESERVED2_BITMASK ) != 0 )
{
byte[] ab = new byte[is.readByte()];
is.readFully( ab );
}
if ( ( bitField & SKIPDETAILS_BITMASK ) != 0 )
{
counterLinkWritten = true;
}
if ( description == null && !counterLinkWritten )
throw new IllegalArgumentException( "internal error: missing way description!" );
boolean isTransfer = ( bitField & TRANSFERNODE_BITMASK ) != 0;
if ( isTransfer )
{
OsmTransferNode trans = new OsmTransferNode();
trans.ilon = linklon;
trans.ilat = linklat;
trans.descriptionBitmap = description;
trans.selev = (short) ( selev + is.readVarLengthSigned() );
if ( lastTransferNode == null )
{
firstTransferNode = trans;
}
else
{
lastTransferNode.next = trans;
}
lastTransferNode = trans;
}
else
{
break;
}
}
// performance shortcut: ignore link if out of reach
if ( dc != null && !counterLinkWritten )
{
if ( !dc.isWithinRadius( ilon, ilat, firstTransferNode, linklon, linklat ) )
{
continue;
}
}
if ( linklon == ilon && linklat == ilat )
{
continue; // skip self-ref
}
// first check the known links for that target
OsmLink link = getCompatibleLink( linklon, linklat, counterLinkWritten, 2 );
if ( link == null ) // .. not found, then check the hollow nodes
{
long targetNodeId = ( (long) linklon ) << 32 | linklat;
OsmNode tn = hollowNodes.get( targetNodeId ); // target node
if ( tn == null ) // node not yet known, create a new hollow proxy
{
tn = new OsmNode( linklon, linklat );
tn.setHollow();
hollowNodes.put( tn );
}
link = new OsmLink();
link.targetNode = tn;
link.counterLinkWritten = counterLinkWritten;
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, !counterLinkWritten, 1 );
if ( rlink == null ) // .. not found, create it
{
rlink = new OsmLink();
rlink.targetNode = this;
rlink.counterLinkWritten = !counterLinkWritten;
rlink.state = 2;
link.targetNode.addLink( rlink );
}
if ( !counterLinkWritten )
{
// we have the data for that link, so fill both the link ..
link.descriptionBitmap = description;
link.encodeFirsttransfer( firstTransferNode );
// .. and the reverse
if ( rlink.counterLinkWritten )
{
rlink.descriptionBitmap = description; // default for no
// transfer-nodes
OsmTransferNode previous = null;
OsmTransferNode rtrans = null;
for ( OsmTransferNode trans = firstTransferNode; trans != null; trans = trans.next )
{
if ( previous == null )
{
rlink.descriptionBitmap = trans.descriptionBitmap;
}
else
{
previous.descriptionBitmap = trans.descriptionBitmap;
}
rtrans = new OsmTransferNode();
rtrans.ilon = trans.ilon;
rtrans.ilat = trans.ilat;
rtrans.selev = trans.selev;
rtrans.next = previous;
rtrans.descriptionBitmap = description;
previous = rtrans;
}
rlink.encodeFirsttransfer( rtrans );
}
}
}
if ( dc == null )
{
hollowNodes.remove( this );
}
}
public boolean isHollow()
{
return selev == -12345;
}
public void setHollow()
{
selev = -12345;
}
public long getIdFromPos()
{
return ( (long) ilon ) << 32 | ilat;
}
public void unlinkLink( OsmLink link )
{
if ( link == firstlink )
{
firstlink = link.next;
return;
}
for ( OsmLink l = firstlink; l != null; l = l.next )
{
if ( l.next == link )
{
l.next = link.next;
return;
}
}
}
@Override
public boolean equals( Object o )
{
if ( o instanceof OsmNode )
{
OsmNode n = (OsmNode) o;
return n.ilon == ilon && n.ilat == ilat;
}
return false;
}
@Override
public int hashCode()
{
return ilon + ilat;
}
}

View file

@ -5,8 +5,11 @@
*/
package btools.mapaccess;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import btools.codec.DataBuffers;
import btools.util.ByteDataReader;
import btools.util.Crc32;
@ -21,6 +24,8 @@ final public class PhysicalFile
String fileName;
public int divisor = 80;
/**
* Checks the integrity of the file using the build-in checksums
*
@ -28,39 +33,50 @@ final public class PhysicalFile
*/
public static String checkFileIntegrity( File f )
{
PhysicalFile pf = null;
try
{
byte[] iobuffer = new byte[65636];
pf = new PhysicalFile( f, new byte[65636], -1, -1 );
for( int tileIndex=0; tileIndex<25; tileIndex++ )
{
OsmFile osmf = new OsmFile( pf, tileIndex, iobuffer );
if ( osmf.microCaches != null )
for( int lonIdx80=0; lonIdx80<80; lonIdx80++ )
for( int latIdx80=0; latIdx80<80; latIdx80++ )
new MicroCache( osmf, lonIdx80, latIdx80, iobuffer );
}
}
catch( IllegalArgumentException iae )
{
return iae.getMessage();
}
catch( Exception e )
{
return e.toString();
}
finally
{
if ( pf != null ) try{ pf.ra.close(); } catch( Exception ee ) {}
}
return null;
PhysicalFile pf = null;
try
{
DataBuffers dataBuffers = new DataBuffers();
pf = new PhysicalFile( f, dataBuffers, -1, -1 );
int div = pf.divisor;
for ( int lonDegree = 0; lonDegree < 5; lonDegree++ ) // does'nt really matter..
{
for ( int latDegree = 0; latDegree < 5; latDegree++ ) // ..where on earth we are
{
OsmFile osmf = new OsmFile( pf, lonDegree, latDegree, dataBuffers );
if ( osmf.hasData() )
for ( int lonIdx = 0; lonIdx < div; lonIdx++ )
for ( int latIdx = 0; latIdx < div; latIdx++ )
osmf.createMicroCache( lonDegree * div + lonIdx, latDegree * div + latIdx, dataBuffers, null, null, false );
}
}
}
catch (IllegalArgumentException iae)
{
return iae.getMessage();
}
catch (Exception e)
{
return e.toString();
}
finally
{
if ( pf != null )
try
{
pf.ra.close();
}
catch (Exception ee)
{
}
}
return null;
}
public PhysicalFile( File f, byte[] iobuffer, int lookupVersion, int lookupMinorVersion ) throws Exception
public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws Exception
{
fileName = f.getName();
byte[] iobuffer = dataBuffers.iobuffer;
ra = new RandomAccessFile( f, "r" );
ra.readFully( iobuffer, 0, 200 );
fileIndexCrc = Crc32.crc( iobuffer, 0, 200 );
@ -99,7 +115,17 @@ final public class PhysicalFile
ra.readFully( iobuffer, 0, extraLen );
dis = new ByteDataReader( iobuffer );
creationTime = dis.readLong();
if ( dis.readInt() != fileIndexCrc )
int crcData = dis.readInt();
if ( crcData == fileIndexCrc )
{
divisor = 80; // old format
}
else if ( (crcData ^ 2) == fileIndexCrc )
{
divisor = 32; // new format
}
else
{
throw new IOException( "top index checksum error" );
}