brouter/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNode.java
2023-04-29 19:04:52 +02:00

262 lines
6.3 KiB
Java

/**
* 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;
}
}