brouter/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java

346 lines
8.3 KiB
Java

/**
* Container for an osm node (pre-pocessor version)
*
* @author ab
*/
package btools.mapcreator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import btools.codec.MicroCache;
import btools.codec.MicroCache2;
public class OsmNodeP extends OsmLinkP
{
/**
* The latitude
*/
public int ilat;
/**
* The longitude
*/
public int ilon;
/**
* The elevation
*/
public short selev;
public final static int NO_BRIDGE_BIT = 1;
public final static int NO_TUNNEL_BIT = 2;
public final static int BORDER_BIT = 4;
public final static int TRAFFIC_BIT = 8;
public final static int ANY_WAY_BIT = 16;
public final static int MULTI_WAY_BIT = 32;
public final static int DP_SURVIVOR_BIT = 64;
public byte bits = 0;
// interface OsmPos
public int getILat()
{
return ilat;
}
public int getILon()
{
return ilon;
}
public short getSElev()
{
// if all bridge or all tunnel, elevation=no-data
return ( bits & NO_BRIDGE_BIT ) == 0 || ( bits & NO_TUNNEL_BIT ) == 0 ? Short.MIN_VALUE : selev;
}
public double getElev()
{
return selev / 4.;
}
// populate and return the inherited link, if available,
// else create a new one
public OsmLinkP createLink( OsmNodeP source )
{
if ( sourceNode == null && targetNode == null )
{
// inherited instance is available, use this
sourceNode = source;
targetNode = this;
source.addLink( this );
return this;
}
OsmLinkP link = new OsmLinkP( source, this );
addLink( link );
source.addLink( link );
return link;
}
// memory-squeezing-hack: OsmLinkP's "previous" also used as firstlink..
public void addLink( OsmLinkP link )
{
link.setNext( previous, this );
previous = link;
}
public OsmLinkP getFirstLink()
{
return sourceNode == null && targetNode == null ? previous : this;
}
public byte[] getNodeDecsription()
{
return null;
}
public RestrictionData getFirstRestriction()
{
return null;
}
public void writeNodeData( MicroCache mc ) throws IOException
{
boolean valid = true;
if ( mc instanceof MicroCache2 )
{
valid = writeNodeData2( (MicroCache2) mc );
}
else
throw new IllegalArgumentException( "unknown cache version: " + mc.getClass() );
if ( valid )
{
mc.finishNode( getIdFromPos() );
}
else
{
mc.discardNode();
}
}
public void checkDuplicateTargets()
{
HashMap<OsmNodeP,OsmLinkP> targets = new HashMap<OsmNodeP,OsmLinkP>();
for ( OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext( this ) )
{
OsmLinkP link = link0;
OsmNodeP origin = this;
OsmNodeP target = null;
// first pass just to see if that link is consistent
while (link != null)
{
target = link.getTarget( origin );
if ( !target.isTransferNode() )
{
break;
}
// next link is the one (of two), does does'nt point back
for ( link = target.getFirstLink(); link != null; link = link.getNext( target ) )
{
if ( link.getTarget( target ) != origin )
break;
}
origin = target;
}
if ( link == null ) continue;
OsmLinkP oldLink = targets.put( target, link0 );
if ( oldLink != null )
{
unifyLink( oldLink );
unifyLink( link0 );
}
}
}
private void unifyLink( OsmLinkP link )
{
if ( link.isReverse( this ) ) return;
OsmNodeP target = link.getTarget( this );
if ( target.isTransferNode() )
{
target.incWayCount();
}
}
public boolean writeNodeData2( MicroCache2 mc ) throws IOException
{
boolean hasLinks = false;
// write turn restrictions
RestrictionData r = getFirstRestriction();
while( r != null )
{
if ( r.fromLon != 0 && r.toLon != 0 )
{
mc.writeBoolean( true ); // restriction follows
mc.writeShort( r.exceptions );
mc.writeBoolean( r.isPositive );
mc.writeInt( r.fromLon );
mc.writeInt( r.fromLat );
mc.writeInt( r.toLon );
mc.writeInt( r.toLat );
}
r = r.next;
}
mc.writeBoolean( false ); // end restritions
mc.writeShort( getSElev() );
mc.writeVarBytes( getNodeDecsription() );
// buffer internal reverse links
ArrayList<OsmNodeP> internalReverse = new ArrayList<OsmNodeP>();
for ( OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext( this ) )
{
OsmLinkP link = link0;
OsmNodeP origin = this;
OsmNodeP target = null;
ArrayList<OsmNodeP> linkNodes = new ArrayList<OsmNodeP>();
linkNodes.add( this );
// first pass just to see if that link is consistent
while (link != null)
{
target = link.getTarget( origin );
linkNodes.add( target );
if ( !target.isTransferNode() )
{
break;
}
// next link is the one (of two), does does'nt point back
for ( link = target.getFirstLink(); link != null; link = link.getNext( target ) )
{
if ( link.getTarget( target ) != origin )
break;
}
if ( link != null && link.descriptionBitmap != link0.descriptionBitmap )
{
throw new IllegalArgumentException( "assertion failed: description change along transfer nodes" );
}
origin = target;
}
if ( link == null )
continue; // dead end
if ( target == this )
continue; // self-ref
hasLinks = true;
// internal reverse links later
boolean isReverse = link0.isReverse( this );
if ( isReverse )
{
if ( mc.isInternal( target.ilon, target.ilat ) )
{
internalReverse.add( target );
continue;
}
}
// write link data
int sizeoffset = mc.writeSizePlaceHolder();
mc.writeVarLengthSigned( target.ilon - ilon );
mc.writeVarLengthSigned( target.ilat - ilat );
mc.writeModeAndDesc( isReverse, link0.descriptionBitmap );
if ( !isReverse && linkNodes.size() > 2 ) // write geometry for forward links only
{
DPFilter.doDPFilter( linkNodes, 0, linkNodes.size()-1 );
origin = this;
for( int i=1; i<linkNodes.size()-1; i++ )
{
OsmNodeP tranferNode = linkNodes.get(i);
if ( ( tranferNode.bits & OsmNodeP.DP_SURVIVOR_BIT ) != 0 )
{
mc.writeVarLengthSigned( tranferNode.ilon - origin.ilon );
mc.writeVarLengthSigned( tranferNode.ilat - origin.ilat );
mc.writeVarLengthSigned( tranferNode.getSElev() - origin.getSElev() );
origin = tranferNode;
}
}
}
mc.injectSize( sizeoffset );
}
while (internalReverse.size() > 0)
{
int nextIdx = 0;
if ( internalReverse.size() > 1 )
{
int max32 = Integer.MIN_VALUE;
for ( int i = 0; i < internalReverse.size(); i++ )
{
int id32 = mc.shrinkId( internalReverse.get( i ).getIdFromPos() );
if ( id32 > max32 )
{
max32 = id32;
nextIdx = i;
}
}
}
OsmNodeP target = internalReverse.remove( nextIdx );
int sizeoffset = mc.writeSizePlaceHolder();
mc.writeVarLengthSigned( target.ilon - ilon );
mc.writeVarLengthSigned( target.ilat - ilat );
mc.writeModeAndDesc( true, null );
mc.injectSize( sizeoffset );
}
return hasLinks;
}
public String toString2()
{
return ( ilon - 180000000 ) + "_" + ( ilat - 90000000 ) + "_" + ( selev / 4 );
}
public long getIdFromPos()
{
return ( (long) ilon ) << 32 | ilat;
}
public boolean isBorderNode()
{
return ( bits & BORDER_BIT ) != 0;
}
public boolean hasTraffic()
{
return ( bits & TRAFFIC_BIT ) != 0;
}
/**
* Not really count the ways, just detect if more than one
*/
public void incWayCount()
{
if ( ( bits & ANY_WAY_BIT ) != 0 )
{
bits |= MULTI_WAY_BIT;
}
bits |= ANY_WAY_BIT;
}
public boolean isTransferNode()
{
return ( bits & BORDER_BIT ) == 0 && ( bits & MULTI_WAY_BIT ) == 0 && _linkCnt() == 2;
}
private int _linkCnt()
{
int cnt = 0;
for ( OsmLinkP link = getFirstLink(); link != null; link = link.getNext( this ) )
{
cnt++;
}
return cnt;
}
}