Hi Steve, attached is my latest version of NodConvert. Maybe it helps you. It doesn't contain the changes from r317.
Gerd Date: Tue, 7 Jan 2014 08:07:21 +0000 From: [email protected] To: [email protected] Subject: Re: [mkgmap-dev] Loops in roads Hi Gerd > I must confest that I don't fully understand why a loop is causing > trouble with routing. Of course, a very simple loop > without any exit point is a problem, but why also normal > roundabouts? In NOD, we save only parts of the roundabout, > so NOD doesn't "see" a loop. > Is the reason that we save the way as one polyline in > NET and that it has only one ref to NOD? I don't know. I have started on my program to display the routing graph information on top of the roads. I hope it will help to understand what is going on. Attached is the first result. Not very useful yet, it displays the routing nodes in black with the direction held within each part as a short arrow. Best viewed in an svg viewer such as inkscape so you can hide layers. I've attached a screen shot as well. I based this on NodConvert but it doesn't cope with many files. Do you have any updates to NodConvert? ..Steve _______________________________________________ mkgmap-dev mailing list [email protected] http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
/* * Copyright (C) 2007 Steve Ratcliffe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * Author: Steve Ratcliffe * Create date: Dec 16, 2007 */ package test.display; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.app.ImgReader; import uk.me.parabola.imgfmt.app.map.MapReader; import uk.me.parabola.imgfmt.app.net.RoadDef; import uk.me.parabola.mkgmap.general.LevelInfo; /** * Standalone program to display the NOD file as it is worked out. Will start * out with what is in imgdecode by John Mechalas. * * Credits: * Alexander Atanasov and his libgarmin (http://libgarmin.sourceforge.net/) project. * Robert Vollmert */ public class NodConvert extends CommonDisplay { // whether this is a new direction compared to the previous link // not set for first link // thus, we have a short link if NEWDIR is not set and it's not the first link private static final int NEWDIR = 0x80; // this appears to signify the orientation of the link within the road private static final int SIGN = 0x40; private static final int CURVE = 0x20; private static final int LEN = 0x18; private static final int EXTRA = CURVE|LEN; // restrictions, first byte private static final int TOLL = 0x80; private static final int CLASS = 0x70; private static final int ONEWAY = 0x08; private static final int SPEED = 0x07; // second byte + top 2 bits of net pointer private static final int NOEMERGENCY = 0x8000; private static final int NODELIVERY = 0x4000; private static final int NOCAR = 0x0001; private static final int NOBUS = 0x0002; private static final int NOTAXI = 0x0004; private static final int NOCARPOOL = 0x0008; private static final int NOFOOT = 0x0010; private static final int NOBIKE = 0x0020; private static final int NOTRUCK = 0x0040; private static final int NO_THROUGH_ROUTING = 0x0080; private int citySize = 1; private int zipSize = 1; private int nodesStart; private int nodesLen; private int roadDataStart; private int roadDataLen; private int boundriesStart; private int boundriesLen; private byte boundriesRecsize; private final TreeMap<Integer, Offset> nod1recs = new TreeMap<Integer, Offset>(); private NetDisplay netDisplay; private byte align; private int aMask; private Table curTable; private final String booltag = "<tag k='%s' v='%b' />\n"; private final String inttag = "<tag k='%s' v='%d' />\n"; private final String bytetag = "<tag k='%s' v='%02x' />\n"; private final String strtag = "<tag k='%s' v='%s' />\n"; boolean convert = true; protected void print() { readCommonHeader(); printHeader(); /* openLbl(); if (lbl.getCities().size() > 255) citySize = 2; if (lbl.getZips().size() > 255) zipSize = 2; openTre(); openNet(); openRgn(); */ // rgn.setNetFile(net); System.out.printf("<?xml version='1.0' encoding='UTF-8'?>\n"); System.out.printf("<osm version='0.5' upload='false' generator='NodConvert'>\n"); printRoadData(); readBoundryNodes(); for (Integer i:nod1recs.keySet()) System.err.println("nod1off = 0x" + Integer.toHexString(i)); printNodes(); System.out.printf("</osm>\n"); } private long calcTablePosition(long start, int low) { long pos = start - nodesStart; pos += aMask + 1; pos += low * ((long) 1 << align); pos &= ~aMask; return nodesStart + pos; } private void printNodes() { Displayer d = new Displayer(reader); d.setTitle("NOD 1 (nodes)"); d.setSectStart(nodesStart); reader.position(nodesStart); while (reader.position() < nodesStart + nodesLen) { long start = reader.position(); int low = reader.get() & 0xff; long end = calcTablePosition(start, low); int testSectoff = (int)(start - nodesStart); Offset off = nod1recs.get(testSectoff); if (off == null){ testSectoff = getNextKnownOffset(testSectoff, start+1000); if (testSectoff >= 0 && testSectoff < end - nodesStart){ long pos2 = reader.position(); reader.position(testSectoff + nodesStart); int low2 = reader.get() & 0xff; long end2 = calcTablePosition(start, low); if (end != end2){ System.err.println("sync?"); } reader.position(pos2); } long dd = 4; } d.item().addText("tables at %x", end); // determine size of Table C offsets int tabCOffsetSize = 0; reader.position(end); // read Table C format int fmt = reader.get(); if((fmt & 2) != 0) { // Table C size is 16 bits, so offset must be too tabCOffsetSize = 2; } else if((fmt & 1) != 0) { // this is tricky because the size could be more than // 128 in which case the offset needs to be 16 bits so // we need to actually read the size to find out reader.position(end + 7); int tabASize = reader.get() & 0xff; int tabBSize = reader.get() & 0xff; reader.position(end + 9 + tabASize * 5 + tabBSize * 3); if((reader.get() & 0xff) >= 0x80) tabCOffsetSize = 2; else tabCOffsetSize = 1; } reader.position(start); // step back to low byte if(tabCOffsetSize == 1) d.item().addText("Table C offsets are 8 bits"); else if(tabCOffsetSize == 2) d.item().addText("Table C offsets are 16 bits"); curTable = readTable(end, tabCOffsetSize); printNode(end, tabCOffsetSize); // reader.position(end); // d.print(outStream); //printTables(0); reader.position(curTable.next); } d.print(outStream); } private void printNode(long end, int tabCOffsetSize) { long groupStart = reader.position(); TableHeader tableHeader = readTableHeader((int) end); int lastLow = Integer.MAX_VALUE; while (reader.position() < end) { // d.gap(); // d.item().addText("New node"); long nodeOff = reader.position(); if (nodeOff == 5043){ long dd = 4; } int sectoff = (int)nodeOff - nodesStart; Offset off = nod1recs.get(sectoff); long rest = end - nodeOff; if (rest < 10){ long dd = 4; } int low = reader.get() & 0xff; /* if (low > lastLow){ System.err.println("lost sync: low byte too high:" + low + " offset:" + sectoff ); for (int i = sectoff - 1; i > 0; i--){ Offset testOff = nod1recs.get(i); if (testOff != null){ System.err.println("prev known offset is at " + i + " (" + (nodesStart + i + 1) + ")"); break; } } boolean foundMore = false; for (int i = sectoff + 1; i < end-nodesStart; i++){ Offset testOff = nod1recs.get(i); if (testOff != null){ System.err.println("next known offset is at " + i + " (" + (nodesStart + i + 1) + ")"); reader.position(testOff.getOffset() + nodesStart); foundMore = true; break; } } if (foundMore) continue; break; } lastLow = low; */ if (calcTablePosition(nodeOff, low) != end) { /* if (low == 0) { d.item().addText("spurious 0?"); continue; } d.item().addText("lost sync calc-end=%x, should be=%x", calcTablePosition(nodeOff, low), end); d.rawValue((int) (end - reader.position()), "remaining bytes"); */ System.err.println("lost sync calc-end " + low); break; } // byte2 looks like flags, only a small number of values int flags = reader.get() & 0xff; System.err.printf("flags 0x%x\n",flags); boolean restr = (flags & 0x10) == 0x10; // restrictions present at this node boolean bigoff = (flags & 0x20) == 0x20; // 2 bytes each for lat/lon offsets boolean boundry = (flags & 0x08) == 0x08; // this is a boundry node boolean arcs = (flags & 0x40) == 0x40; // has arcs int minRequired = ((bigoff) ? 4:3 ) + ((arcs) ? 4:0 ) ; if (rest < minRequired) break; StringBuilder sb = new StringBuilder(": "); if (restr) sb.append("restrictions "); if (bigoff) sb.append("large-offsets "); if (boundry) sb.append("boundry-node "); if (arcs) sb.append("arcs "); if (restr && tabCOffsetSize == 0 ){ System.err.println("lost sync: restr is true, but table is empty"); } // System.out.printf(booltag, "verified offset", (off != null)); if (off == null){ long dd = 4; if (boundry) return; } // d.item().addText(sb.toString()); System.out.printf("<node id='%d' ", nodeOff+1); if (nodeOff + 1 == 12868){ long dd = 4; } // 1. even spread // 2. biased toward 00 f0 0f ff etc // 3. even // 4. uneven, peaks at 0x20, 40, 60 // 5. uneven again // 6. fairly even but big peak at 3e,3f // 7. fairly even // 8. number decreases with value positionOffsets(tableHeader, bigoff); System.out.printf("<tag k='verified' v='%b' />\n", off!=null); System.out.printf("<tag k='restrictions' v='%b' />\n", restr); System.out.printf("<tag k='boundary' v='%b' />\n", boundry); System.out.printf("</node>\n"); if(arcs){ if (off == null){ long dd = 4; } rest = end - reader.position(); if (rest < 10){ System.err.println("reading arc near table"); } pointerPart(nodeOff, groupStart, end); } /* if(restr) { boolean done = false; while(!done) { if(tabCOffsetSize == 1) { int off = d.byteValue("Restriction offset 0x%x"); done = (off & 0x80) == 0x80; } else if(tabCOffsetSize == 2) { int off = d.charValue("Restriction offset 0x%x"); done = (off & 0x8000) == 0x8000; } else { done = true; } } } */ } } private int tableId = 1; class Table { final int id; final int nodeid; int lat, lon; long next; // start of next RouteCenter // just the class for now final ArrayList<Integer> tableA = new ArrayList<Integer>(); // destination offsets for now final ArrayList<Integer> tableB = new ArrayList<Integer>(); Table() { id = tableId++; nodeid = -id; } } private Table readTable(long offset, int tabCOffsetSize) { if (offset == 0x62bf){ long dd = 4; } System.err.println("reading table at 0x" + Long.toHexString(offset)); long orig = reader.position(); Table table = new Table(); reader.position(offset); // Get the header int restrbytes = reader.get() & 0xff; int l = latLongField(); table.lon = l; l = latLongField(); table.lat = l; System.out.printf(Locale.ROOT, "<node id='%d' lon='%f' lat='%f'>\n", table.nodeid, Utils.toDegrees(table.lon), Utils.toDegrees(table.lat)); System.out.printf(strtag, "routecenter", table.id); System.out.printf("</node>\n"); int n = reader.get() & 0xff; int m = reader.get() & 0xff; System.err.println("table sizes: " + n + " " + m); // Now do 'Table A' (segments) for (int i = 0; i < n; i++) { int off = reader.get3(); off &= 0x3fffff; System.err.printf("net off: %06x\n", off); int paramA = reader.get(); table.tableA.add((paramA & CLASS) >> 4); reader.get(); } // 'Table B' (inter-section pointers) for (int i = 0; i < m; i++) { System.err.println("adding table b entry " + i); int nod1off = reader.getu3(); if (nod1off > nodesLen){ System.err.println("lost sync: invalid entry in tableB" ); } table.tableB.add(nod1off); } if (tabCOffsetSize != 0){ // 'Table C' (restrictions) int size=0; // I've seen restrbytes up to 2 if (restrbytes == 0) { // if next byte is 0, discard it long pos = reader.position(); int val = reader.get(); if(val == 0) { //d.item().addText("table c size is zero"); System.err.println("zero byte discarded"); } else { // rewind so that byte gets used reader.position(pos); } } else if ((restrbytes & 1) != 0) { size = reader.get() & 0xff; } else if ((restrbytes & 2) != 0) { size = reader.getChar() & 0xffff; } if (size % 11 == 0) { // assume these are fixed length records of size 11 for (; size > 0; size -= 11) { // turn restriction at second node from first node (via first segment) // to third node (via second segment) reader.get3(); for (int i = 0; i < 3; i++) { reader.getChar(); } reader.get(); reader.get(); } } else { reader.get(size); } } if ((restrbytes & 4) != 0) { //d.byteValue("??? restrformat4"); reader.get(); } if ((restrbytes & 8) != 0) { //d.byteValue("%d unpaved roads"); reader.get(); } if((restrbytes & 0x10) != 0) { //d.byteValue("%d ferry routes"); reader.get(); } if(/*restrbytes != 0 && (restrbytes & 0x03) == 0*/ tabCOffsetSize == 0) { // d.byteValue("???"); long pos = reader.position(); int unk = reader.get() & 0xff; if (unk != 0) reader.position(pos); else System.err.printf("unk byte eaten: %x\n" , unk); } table.next = reader.position(); // Restore position reader.position(orig); return table; } private TableHeader readTableHeader(int offset) { long orig = reader.position(); TableHeader tableHeader = new TableHeader(); reader.position(offset); reader.get(); tableHeader.setLong(reader.get3()); tableHeader.setLat(reader.get3()); // Restore position reader.position(orig); return tableHeader; } private void positionOffsets(TableHeader currentTableHeader, boolean bigoff) { short latoff, longoff; if (bigoff ) { longoff = (short) reader.getChar(); latoff = (short) reader.getChar(); } else { int latlon = reader.get3(); latoff = (short) (latlon >> 12); if ((latoff & 0x800) != 0) latoff |= 0xf000; longoff = (short) (latlon & 0xfff); if ((longoff & 0x800) != 0) longoff |= 0xf000; } System.out.printf(Locale.ROOT, "lon='%f' lat='%f'>\n", Utils.toDegrees(longoff + currentTableHeader.getLon()), Utils.toDegrees(latoff + currentTableHeader.getLat())); } private void pointerPart(long offset, long min, long max) { // looks like there are 2b before the low1 pointer and 4 after. boolean end = false; boolean first = true; do { if (reader.position() == 5184){ long dd = 4; } if (reader.position() + 10 > max ){ long dd = 4; // close to end } System.out.printf("<way id='%d'>\n", reader.position()); System.out.printf("<nd ref='%d' />\n", offset+1); // Start with alt6 byte // bit 0x20 seems to determine whether there's an extra byte at the end int alt6 = reader.get() & 0xff; System.out.printf(bytetag, "alt6", alt6); // this is not the class of the segment, but the max of classes of the dest node int classmask = 0x07; boolean newdir = (alt6 & NEWDIR) != 0; System.out.printf(booltag, "newdir", newdir); int destclass = alt6 & classmask; System.out.printf(inttag, "destclass", destclass); System.out.printf(booltag, "sign", (alt6 & SIGN) != 0); if (first) { newdir = true; first = false; } // Continue with two byte values. The first one has the top // bit set if this is the last pointer in the node record. int flagB = reader.get() & 0xff; // Note that this is the last if it is. if ((flagB & 0x80) == 0x80) { end = true; System.out.printf(booltag, "last", true); } // The second highest bit, means inter-section pointer boolean external = (flagB & 0x40) == 0x40; System.out.printf(booltag, "external", external); long pos = reader.position(); if (external) { int idx = flagB & 0x3f; if (idx == 0x3f) { idx = reader.get() & 0xff; } System.out.printf(inttag, "indexB", idx); int off = curTable.tableB.get(idx); if (nod1recs.containsKey(off) == false){ long dd = 4; } else { long dd = 4; } System.out.printf("<nd ref='%d' />\n", off +1); } else { // in-section relative node pointer int intro2 = reader.get() & 0xff; short nodeoff = (short) ((flagB << 8 | intro2) & 0x3fff); // Construct a pointer to another node, signed 16 bit quantity if ((nodeoff & 0x2000) != 0) nodeoff |= 0xc000; long otherNode = offset + nodeoff; if (min > otherNode || otherNode >= max) { System.err.println("lost sync in pointerPart"); } System.out.printf("<nd ref='%d' />\n", otherNode+1); } if (newdir && curTable.tableA.isEmpty()){ System.err.println("newdir with empty tableA"); } if (newdir){ int indexA = reader.get() & 0xff; System.out.printf(inttag, "indexA", indexA); System.out.printf(inttag, "roadclass", curTable.tableA.get(indexA)); } else { //d.item().addText("short link, no segment pointer"); } System.out.printf(strtag, "type", newdir ? "arc" : "link"); int len; boolean curve; int curveInfo = (alt6 & EXTRA) >> 3; System.err.println("curveinfo: " + curveInfo); if (curveInfo != 7){ long dd = 4; } if ((alt6 & EXTRA) == EXTRA) { int len1 = reader.get() & 0xff; if ((len1 & 0x80) == 0x80) { if ((len1 & 0x40) == 0x40) { int len2 = reader.getChar(); len = (len1 & 0x3f) | (len2 << 6); // 6+16 bits System.out.printf(inttag, "lengthbits", 22); if (len <(1 << 14)) { System.err.println("invalid length 22 bits?"); } curve = true; } else { int len2 = reader.get() & 0xff; len = (len1 & 0x3f) | (len2 << 6); // 6+8 bits System.out.printf(inttag, "lengthbits", 14); curve = false; } } else { int len2 = reader.get() & 0xff; len = (len1 & 0x7f) | (len2 << 7); // 7+8 bits System.out.printf(inttag, "lengthbits", 15); curve = true; } } else { curve = (alt6 & CURVE) == CURVE; len = (alt6 & 0x08) << 5; len |= reader.get() & 0xff; // 1+8 bits if (len > 0x200){ System.err.println("invalid length 9 bits?"); } System.out.printf(inttag, "lengthbits", 9); } // length should be in units of 16 feet System.out.printf(inttag, "length-16f", len); System.out.printf(inttag, "length-m", (int)(len * 16 / 3.2808)); if (newdir) { int dir = reader.get(); System.out.printf(inttag, "direction", (dir * 360) >> 8); } if (curve) { int curvea = reader.get() & 0xff; pos = reader.position(); int curveb; if ((curvea & 0xe0) == 0) { curveb = reader.get() & 0xff; //item.addText("curve[0] %02x curve[1] %02x (two bytes)", curvea, curveb); System.out.printf(inttag, "curvebytes", 2); if (curveb == 0){ long dd = 4; // reader.position(pos); } } else { System.out.printf(inttag, "curvebytes", 1); System.out.printf(inttag, "curvebyte-raw", curvea); int angle = 360 * (byte)(((curvea & 0x1f) << 3) | ((curvea & 0xe0) >> 5)) / 256; //item.addText("curve[0] %02x (%d deg?)", curvea, angle); curveb = (curvea & 0x1f) << 3; curvea = curvea & 0x70; } System.out.printf(inttag, "curvea", curvea); System.out.printf(inttag, "curveb", curveb); } System.out.printf("</way>\n"); } while (!end); } private void printTables(int len) { int remain = len; // Get the header TableHeader tableHeader = new TableHeader(); tableHeader.setPosition(reader.position()); int restrformat = reader.get() & 0xff; remain -= 1; int l = latLongField(); tableHeader.setLong(l); remain -= 3; l = latLongField(); tableHeader.setLat(l); remain -= 3; int n = reader.get() & 0xff; remain -= 1; int m = reader.get() & 0xff; remain -= 1; // Now do 'Table A' (segments) for (int i = 0; i < n; i++) { int off = reader.get3(); int paramA = reader.get(); int paramB = reader.get(); /* // top 2 bits of net pointer are access bits int access = (off & 0xc00000) >> 8; off &= 0x3fffff; String par = "class %d, speed %d"; if ((paramA & TOLL) == TOLL) par += ", toll"; if ((paramA & ONEWAY) == ONEWAY) par += ", oneway"; if ((paramB & NO_THROUGH_ROUTING) == NO_THROUGH_ROUTING) par += ", no through routing"; item.addText(par, (paramA & CLASS) >> 4, paramA & SPEED); access |= paramB & 0xff; par = ""; par += (access & NOEMERGENCY) == 0 ? "emergency, " : "no emergency, "; par += (access & NODELIVERY) == 0 ? "delivery, " : "no delivery, "; par += (access & NOCAR) == 0 ? "car, " : "no car, "; par += (access & NOBUS) == 0 ? "bus, " : "no bus, "; par += (access & NOTAXI) == 0 ? "taxi, " : "no taxi, "; par += (access & NOCARPOOL) == 0 ? "carpool, " : "no carpool, "; par += (access & NOFOOT) == 0 ? "foot, " : "no foot, "; par += (access & NOBIKE) == 0 ? "bike, " : "no bike, "; par += (access & NOTRUCK) == 0 ? "truck, " : "no truck, "; item.addText("access: %s", par); if((paramB & 0x08) != 0) item.addText("Unknown access bits: %02x", paramB & 0x08); */ remain -= 5; } // d.print(outStream); // 'Table B' (inter-section pointers) // d.setTitle("Table B (inter-section pointers)"); for (int i = 0; i < m; i++) { int nodeOff = reader.get3(); remain -= 3; } // d.print(outStream); // 'Table C' (restrictions) // d.setTitle("Table C (restrictions)"); int size=0; if (restrformat == 0) { // if next byte is 0, discard it long pos = reader.position(); int val = reader.get(); if(val == 0) { // d.item().addText("table c size is zero"); } else { // rewind so that byte gets used reader.position(pos); } } else if ((restrformat & 1) != 0) { size = reader.get() & 0xff; remain--; } else if ((restrformat & 2) != 0) { size = reader.getChar() & 0xffff; remain -= 2; } if (size % 11 == 0) { // assume these are fixed length records of size 11 remain -= size; for (; size > 0; size -= 11) { // turn restriction at second node from first node (via first segment) // to third node (via second segment) // d.rawValue(3, "restriction header"); reader.get3(); for (int i = 0; i < 3; i++) { int off = reader.getChar() & 0xffff; } reader.get(); reader.get(); } } else { reader.get(size); remain -= size; } if ((restrformat & 4) != 0) { // d.byteValue("??? restrformat4"); reader.get(); remain--; } if ((restrformat & 8) != 0) { // d.byteValue("%d unpaved roads"); reader.get(); remain--; } if((restrformat & 0x10) != 0) { // d.byteValue("%d ferry routes"); reader.get(); remain--; } if(restrformat != 0 && (restrformat & 0x03) == 0) { // d.byteValue("???"); reader.get(); remain--; } // d.print(outStream); // if there's something left, we probably missed the first // node of the next section /* if (remain > 0) { d.setTitle("left over"); d.rawValue(remain, "extra data remaining"); } d.print(outStream); d.setTitle(""); */ } private int latLongField() { int l = reader.get3(); if ((l & 0x800000) != 0) l |= 0xff000000; return l; } private String getNameFromNetOff(int off) { if (netDisplay != null) return netDisplay.getNameFromNetOff(off); else return ""; } private void printRoadData() { Displayer d = new Displayer(reader); d.setTitle("NOD 2 (road data)"); d.setSectStart(roadDataStart); d.print(outStream); reader.position(roadDataStart); int end = roadDataStart + roadDataLen; int nrecords = 0; while (reader.position() < end) { nrecords++; d = new Displayer(reader); d.setSectStart(roadDataStart); int recstart = (int) reader.position(); // XXX looks like a set of flags but ? // agree with speed & class in RouteParam (plus one bit) DisplayItem item = d.item(); int flags = item.setBytes(reader.get()); item.addText("Road classification speed=%d, type=%d", (flags & 0xf) >> 1, (flags & 0x70) >> 4); if((flags & 1) == 0) item.addText("bit 0 is zero"); int sectoff = d.int3Value("offset into NOD 1 %06x"); String origin = (new Formatter()).format("nod 2 %06x", recstart).toString(); Offset offset = nod1recs.get(sectoff); if (offset != null) { offset.appendOrigin(origin); } else { Offset offval = new Offset(sectoff, origin); nod1recs.put(sectoff, offval); } // Ok this is the number of bits in the following. int nbits = d.charValue("Bit stream len"); // The number of set bits appears to be the number of nodes in the road. // Usually th lowest nbits appear to be set, and I've seen the lowest // missing when an end of the road is not a node. --Rob // A number of bits follows. Work out how many bytes are needed to // hold that number of bits. int nstream = (nbits+7)/8; byte[] bs = d.rawValue(nstream, "Bit stream"); String bsStr = bitStreamAsString(bs, nbits); d.item().addText("BIT STREAM %s", bsStr); if((flags & 0x80) != 0) { int extraFormat = d.byteValue("extra data format"); if(extraFormat >= 0x01 && extraFormat <= 0x0b) { if((extraFormat & 0x01) != 0) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data 1"); } if((extraFormat & 0x02) != 0) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data 2"); } if((extraFormat & 0x04) != 0) { int extra = d.byteValue("extra data 4"); } if((extraFormat & 0x08) != 0) { int extra = d.charValue("extra data 8"); } } else if(extraFormat == 0x0c) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data c"); } else if(extraFormat == 0x0d) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data da"); len = d.byteValue("len"); d.rawValue(len >> 1, "extra data db"); } else if(extraFormat == 0x0e) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data ea"); len = d.byteValue("len"); d.rawValue(len >> 1, "extra data eb"); } else if(extraFormat == 0x0f) { int len = d.byteValue("len"); d.rawValue(len >> 1, "extra data fa"); len = d.byteValue("len"); d.rawValue(len >> 1, "extra data fb"); len = d.byteValue("len"); d.rawValue(len >> 1, "extra data fc"); } else d.item().addText("Unknown format"); } d.gap(); // d.print(outStream); } d.item().addText("Number of records %d", nrecords); d.print(outStream); } private String bitStreamAsString(byte[] bs, int nbits) { long l = 0; for (int i = 0; i < (nbits+7)/8; i++) { byte b = bs[i]; l |= (b & 0xff) << (i * 8); } String s = Long.toBinaryString(l); if (s.length() < nbits) { StringBuilder sb = new StringBuilder(s); for (int i = s.length(); i < nbits; i++) sb.insert(0, '0'); return sb.toString(); } return s; } int getNextKnownOffset(int offset, long end){ for (int i = offset + 1; i < end-nodesStart; i++){ Offset testOff = nod1recs.get(i); if (testOff != null){ System.err.println("next known offset is at " + i + " (" + (nodesStart + i + 1) + ")"); reader.position(testOff.getOffset() + nodesStart); return testOff.getOffset(); } } return -1; } /** * This is a set of fixed length records, so is a good one to start with. */ private void readBoundryNodes() { reader.position(boundriesStart); for (int pos = boundriesStart; pos < boundriesStart + boundriesLen; pos += boundriesRecsize) { //latLongField(d, "longitude"); //latLongField(d, "latitude"); reader.get(6); // limited range int sectoff = reader.getu3(); Offset offset = nod1recs.get(sectoff); if (offset != null) { offset.appendOrigin("NOD 3"); } else { Offset offval = new Offset(sectoff, "NOD 3"); nod1recs.put(sectoff, offval); } //d.int3Value("offset into NOD 1 %06x"); // We believe that this is an offset into unk1 too // nodeOffsets.add(new Offset(sectoff, "NOD 3 chunk3")); } } private void printHeader() { Displayer d = new Displayer(reader); d.setTitle("NOD header"); nodesStart = d.intValue("NOD 1 (nodes) at offset %#08x"); nodesLen = d.intValue("NOD 1 length %d"); d.item().addText("End of section %08x, len %#08x", nodesStart + nodesLen, nodesLen); int flags = d.charValue("flags"); // usually 0x0025 or 0x0027 if ((flags & 0x02) != 0) d.item().addText("have restrictions"); d.charValue("???"); //d.charValue("???"); align = d.byteValue("node align"); aMask = (1<<align) - 1; d.byteValue("???"); d.charValue("???"); roadDataStart = d.intValue("NOD 2 (road data) at offset %#08x"); roadDataLen = d.intValue("NOD 2 length %d"); d.item().addText("End of section %08x, len %#08x", roadDataStart + roadDataLen, roadDataLen); d.intValue("???"); boundriesStart = d.intValue("NOD 3 (boundry nodes) at offset %#08x"); boundriesLen = d.intValue("NOD 3 length %d"); boundriesRecsize = d.byteValue("NOD 3 record size %d"); d.item().addText("End of section %08x, len %#08x", boundriesStart + boundriesLen, boundriesLen) .addText("Number of records %d", boundriesLen / boundriesRecsize); d.print(outStream); } private void readNetHeader() { Displayer d = new Displayer(reader); readSection(d, "road defs", 1, false, false); reader.get(); readSection(d, "segmented roads", 2, false, false); reader.get(); readSection(d, "road index", 3, true, false); } public static void main(String[] args) { if (args.length < 1) { System.err.println("Usage: noddisplay <filename>"); System.exit(1); } String name = args[0]; NodConvert nd = new NodConvert(); nd.setOutStream(System.err); nd.display(name, "NOD"); } }
_______________________________________________ mkgmap-dev mailing list [email protected] http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
