/** * 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 = Short.MIN_VALUE; /** * 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) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()))); } 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; } }