package btools.mapaccess; import btools.codec.DataBuffers; import btools.codec.NoisyDiffCoder; import btools.codec.StatCoderContext; import btools.codec.TagValueCoder; import btools.codec.TagValueValidator; import btools.codec.TagValueWrapper; import btools.codec.WaypointMatcher; import btools.util.ByteDataWriter; /** * DirectWeaver does the same decoding as MicroCache2, but decodes directly * into the instance-graph, not into the intermediate nodes-cache */ public final class DirectWeaver extends ByteDataWriter { private long id64Base; public DirectWeaver(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes) { super(null); int cellsize = 1000000 / divisor; id64Base = ((long) (lonIdx * cellsize)) << 32 | (latIdx * cellsize); TagValueCoder wayTagCoder = new TagValueCoder(bc, dataBuffers, wayValidator); TagValueCoder nodeTagCoder = new TagValueCoder(bc, dataBuffers, null); NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder(bc); NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder(bc); NoisyDiffCoder extLonDiff = new NoisyDiffCoder(bc); NoisyDiffCoder extLatDiff = new NoisyDiffCoder(bc); NoisyDiffCoder transEleDiff = new NoisyDiffCoder(bc); int size = bc.decodeNoisyNumber(5); int[] faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2; bc.decodeSortedArray(faid, 0, size, 29, 0); OsmNode[] nodes = new OsmNode[size]; for (int n = 0; n < size; n++) { long id = expandId(faid[n]); int ilon = (int) (id >> 32); int ilat = (int) (id & 0xffffffff); OsmNode node = hollowNodes.get(ilon, ilat); if (node == null) { node = new OsmNode(ilon, ilat); } else { node.visitID = 1; hollowNodes.remove(node); } nodes[n] = node; } int netdatasize = bc.decodeNoisyNumber(10); // (not needed for direct weaving) ab = dataBuffers.bbuf1; aboffset = 0; int selev = 0; for (int n = 0; n < size; n++) { // loop over nodes OsmNode node = nodes[n]; int ilon = node.ilon; int ilat = node.ilat; // future escapes (turn restrictions?) short trExceptions = 0; for (; ; ) { int featureId = bc.decodeVarBits(); if (featureId == 0) break; int bitsize = bc.decodeNoisyNumber(5); if (featureId == 2) { // exceptions to turn-restriction trExceptions = (short) bc.decodeBounded(1023); } else if (featureId == 1) { // turn-restriction TurnRestriction tr = new TurnRestriction(); tr.exceptions = trExceptions; trExceptions = 0; tr.isPositive = bc.decodeBit(); tr.fromLon = ilon + bc.decodeNoisyDiff(10); tr.fromLat = ilat + bc.decodeNoisyDiff(10); tr.toLon = ilon + bc.decodeNoisyDiff(10); tr.toLat = ilat + bc.decodeNoisyDiff(10); node.addTurnRestriction(tr); } else { for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip } } selev += nodeEleDiff.decodeSignedValue(); node.selev = (short) selev; TagValueWrapper nodeTags = nodeTagCoder.decodeTagValueSet(); node.nodeDescription = nodeTags == null ? null : nodeTags.data; // TODO: unified? int links = bc.decodeNoisyNumber(1); for (int li = 0; li < links; li++) { int nodeIdx = n + nodeIdxDiff.decodeSignedValue(); int dlon_remaining; int dlat_remaining; boolean isReverse = false; if (nodeIdx != n) { // internal (forward-) link dlon_remaining = nodes[nodeIdx].ilon - ilon; dlat_remaining = nodes[nodeIdx].ilat - ilat; } else { isReverse = bc.decodeBit(); dlon_remaining = extLonDiff.decodeSignedValue(); dlat_remaining = extLatDiff.decodeSignedValue(); } TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet(); int linklon = ilon + dlon_remaining; int linklat = ilat + dlat_remaining; aboffset = 0; if (!isReverse) { // write geometry for forward links only WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher; int ilontarget = ilon + dlon_remaining; int ilattarget = ilat + dlat_remaining; if (matcher != null) { if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) { matcher = null; } } int transcount = bc.decodeVarBits(); int count = transcount + 1; for (int i = 0; i < transcount; i++) { int dlon = bc.decodePredictedValue(dlon_remaining / count); int dlat = bc.decodePredictedValue(dlat_remaining / count); dlon_remaining -= dlon; dlat_remaining -= dlat; count--; int elediff = transEleDiff.decodeSignedValue(); if (wayTags != null) { writeVarLengthSigned(dlon); writeVarLengthSigned(dlat); writeVarLengthSigned(elediff); } if (matcher != null) matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining); } if (matcher != null) matcher.end(); } if (wayTags != null) { byte[] geometry = null; if (aboffset > 0) { geometry = new byte[aboffset]; System.arraycopy(ab, 0, geometry, 0, aboffset); } if (nodeIdx != n) { // valid internal (forward-) link OsmNode node2 = nodes[nodeIdx]; OsmLink link = node.isLinkUnused() ? node : (node2.isLinkUnused() ? node2 : null); if (link == null) { link = new OsmLink(); } link.descriptionBitmap = wayTags.data; link.geometry = geometry; node.addLink(link, isReverse, node2); } else { // weave external link node.addLink(linklon, linklat, wayTags.data, geometry, hollowNodes, isReverse); node.visitID = 1; } } } // ... loop over links } // ... loop over nodes hollowNodes.cleanupAndCount(nodes); } private static final long[] id32_00 = new long[1024]; private static final long[] id32_10 = new long[1024]; private static final long[] id32_20 = new long[1024]; static { for (int i = 0; i < 1024; i++) { id32_00[i] = _expandId(i); id32_10[i] = _expandId(i << 10); id32_20[i] = _expandId(i << 20); } } private static long _expandId(int id32) { int dlon = 0; int dlat = 0; for (int bm = 1; bm < 0x8000; bm <<= 1) { if ((id32 & 1) != 0) dlon |= bm; if ((id32 & 2) != 0) dlat |= bm; id32 >>= 2; } return ((long) dlon) << 32 | dlat; } public long expandId(int id32) { return id64Base + id32_00[id32 & 1023] + id32_10[(id32 >> 10) & 1023] + id32_20[(id32 >> 20) & 1023]; } }