/** * Container for an osm node * * @author ab */ package btools.mapaccess; import btools.codec.MicroCache; import btools.codec.MicroCache2; import btools.util.ByteArrayUnifier; import btools.util.CheapRuler; import btools.util.IByteArrayUnifier; 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; public TurnRestriction firstRestriction; public int visitID; public void addTurnRestriction( TurnRestriction tr ) { tr.next = firstRestriction; firstRestriction = tr; } /** * The links to other nodes */ public OsmLink firstlink; 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 ); } // interface OsmPos public final int getILat() { return ilat; } public final int getILon() { return ilon; } public final short getSElev() { return selev; } public final double getElev() { return selev / 4.; } public final void addLink( OsmLink link, boolean isReverse, OsmNode tn ) { if ( link == firstlink ) { throw new IllegalArgumentException( "UUUUPS" ); } if ( isReverse ) { link.n1 = tn; link.n2 = this; link.next = tn.firstlink; link.previous = firstlink; tn.firstlink = link; firstlink = link; } else { link.n1 = this; link.n2 = tn; link.next = firstlink; link.previous = tn.firstlink; tn.firstlink = link; firstlink = link; } } public final int calcDistance( OsmPos p ) { return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 ); } public String toString() { return "n_" + (ilon-180000000) + "_" + (ilat-90000000); } public final void parseNodeBody( MicroCache mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay ) { if ( mc instanceof MicroCache2 ) { parseNodeBody2( (MicroCache2) mc, hollowNodes, expCtxWay ); } else throw new IllegalArgumentException( "unknown cache version: " + mc.getClass() ); } public final void parseNodeBody2( MicroCache2 mc, OsmNodesMap hollowNodes, IByteArrayUnifier expCtxWay ) { ByteArrayUnifier abUnifier = hollowNodes.getByteArrayUnifier(); // read turn restrictions while( mc.readBoolean() ) { TurnRestriction tr = new TurnRestriction(); tr.exceptions = mc.readShort(); tr.isPositive = mc.readBoolean(); tr.fromLon = mc.readInt(); tr.fromLat = mc.readInt(); tr.toLon = mc.readInt(); tr.toLat = mc.readInt(); addTurnRestriction( tr ); } 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, expCtxWay ); } byte[] geometry = mc.readDataUntil( endPointer ); addLink( linklon, linklat, description, geometry, hollowNodes, isReverse ); } hollowNodes.remove( this ); } public void addLink( int linklon, int linklat, byte[] description, byte[] geometry, OsmNodesMap hollowNodes, boolean isReverse ) { if ( linklon == ilon && linklat == ilat ) { return; // skip self-ref } OsmNode tn = null; // find the target node OsmLink link = null; // ...in our known links for ( OsmLink l = firstlink; l != null; l = l.getNext( this ) ) { 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 } } if ( link == null ) { addLink( link = new OsmLink(), isReverse, tn ); } if ( !isReverse ) { link.descriptionBitmap = description; link.geometry = geometry; } } public final boolean isHollow() { return selev == -12345; } public final void setHollow() { selev = -12345; } public final long getIdFromPos() { return ( (long) ilon ) << 32 | ilat; } public void vanish() { if ( !isHollow() ) { OsmLink l = firstlink; while( l != null ) { OsmNode target = l.getTarget( this ); OsmLink nextLink = l.getNext( this ); if ( !target.isHollow() ) { unlinkLink( l ); if ( !l.isLinkUnused() ) { target.unlinkLink( l ); } } l = nextLink; } } } public final void unlinkLink( OsmLink link ) { OsmLink n = link.clear( this ); if ( link == firstlink ) { firstlink = n; return; } OsmLink l = firstlink; while( l != null ) { // if ( l.isReverse( this ) ) if ( l.n1 != this && l.n1 != null ) // isReverse inline { OsmLink nl = l.previous; if ( nl == link ) { l.previous = n; return; } l = nl; } else if ( l.n2 != this && l.n2 != null ) { OsmLink nl = l.next; if ( nl == link ) { l.next = n; return; } l = nl; } else { throw new IllegalArgumentException( "unlinkLink: unknown source" ); } } } @Override public final boolean equals( Object o ) { return ((OsmNode)o).ilon == ilon && ((OsmNode)o).ilat == ilat; } @Override public final int hashCode() { return ilon + ilat; } }