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

301 lines
8.1 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, OsmTrafficMap trafficMap) throws IOException {
boolean valid = true;
if (mc instanceof MicroCache2) {
valid = writeNodeData2((MicroCache2) mc, trafficMap);
} 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<>();
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, OsmTrafficMap trafficMap) throws IOException {
boolean hasLinks = false;
// write turn restrictions
RestrictionData r = getFirstRestriction();
while (r != null) {
if (r.isValid() && 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<>();
for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) {
OsmLinkP link = link0;
OsmNodeP origin = this;
OsmNodeP target = null;
ArrayList<OsmNodeP> linkNodes = new ArrayList<>();
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;
}
}
// add traffic simulation, if present
byte[] description = link0.descriptionBitmap;
if (trafficMap != null) {
description = trafficMap.addTrafficClass(linkNodes, description);
}
// write link data
int sizeoffset = mc.writeSizePlaceHolder();
mc.writeVarLengthSigned(target.ilon - ilon);
mc.writeVarLengthSigned(target.ilat - ilat);
mc.writeModeAndDesc(isReverse, description);
if (!isReverse && linkNodes.size() > 2) { // write geometry for forward links only
DPFilter.doDPFilter(linkNodes);
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;
}
}