/*
* Copyright (C) 2007,2014 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.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import uk.me.parabola.imgfmt.Utils;
import static test.util.AccessBits.*;
import static test.util.ArcFlags.*;
import static test.util.NodeFlags.*;
import static test.util.RestrictionBits.*;
/**
* 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 NodDisplay
extends CommonDisplay
{
private int nodesStart
;
private int nodesLen
;
private int roadDataStart
;
private int roadDataLen
;
private int boundriesStart
;
private int boundriesLen
;
private int boundriesRecsize
;
private final Map<Integer, Offset
> nod1recs =
new HashMap<>();
private NetDisplay net
;
private byte align
;
private int aMask
;
private int tableARecordLen
;
// Initialise this here if the automatic detection does not work.
private Boolean hasExtraZeros
;
private byte mult
;
protected void print
() {
readCommonHeader
();
printHeader
();
printRoadData
();
printBoundryNodes
();
printNodes
();
}
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
) {
while (mult
!=
0 && ((reader.
position() - nodesStart
) & ((1 << mult
) -
1)) !=
0)
d.
byteValue("pad");
long start = reader.
position();
int low =
(d.
byteValue("low byte %x, pointer to")) & 0xff
;
long end = calcTablePosition
(start, low
);
d.
item().
addText("tables at %x", end
);
// determine size of Table C offsets
reader.
position(end
);
// read Table C format
int fmt = reader.
get();
int tabCOffsetSize =
0;
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");
printNode
(d, end, tabCOffsetSize
);
reader.
position(end
);
d.
print(outStream
);
printTables
(d,
0);
}
d.
print(outStream
);
}
private void printNode
(Displayer d,
long end,
int tabCOffsetSize
) {
long groupStart = reader.
position();
TableHeader tableHeader = readTableHeader
((int) end
);
while (reader.
position() < end
) {
d.
gap();
d.
item().
addText("New node");
long nodeOff = reader.
position();
DisplayItem item = d.
byteItem();
int low = item.
getValue();
item.
addText("low %x", low
& 0xff
);
if (calcTablePosition
(nodeOff, low
) != end
&& low
> 0) {
d.
item().
addText("error lost sync calc-end=%x, should be=%x",
calcTablePosition
(nodeOff, low
), end
);
low = d.
byteValue("retry low %x") & 0xff
;
if (calcTablePosition
(nodeOff +
1, low
) != end
) {
d.
rawValue((int) (end - reader.
position()),
"remaining bytes");
break;
}
nodeOff +=
1;
}
// byte2 looks like flags, only a small number of values
int flags = d.
byteValue("Flags %x");
if (low ==
0 && flags ==
0) {
item.
addText("End of nodes");
d.
rawValue((int) (end - reader.
position()),
"perhaps more nodes");
continue;
}
boolean arcs =
(flags
& NODE_ARCS
) == 0x40
; // has arcs
boolean bigoff =
(flags
& NODE_LARGE_OFFSET
) == 0x20
; // 2 bytes each for lat/lon offsets
boolean restr =
(flags
& NODE_RESTRICTIONS
) == 0x10
; // restrictions present at this node
boolean boundary =
(flags
& NODE_BOUNDARY
) == 0x08
; // this is a boundary node
int destclass = flags
& NODE_DESTCLASS_MASK
; // Highest road class this node leads to
boolean unkFlags =
(flags
& NODE_UNKNOWN_MASK
) !=
0; // has unknown flags
StringBuilder sb =
new StringBuilder(": ");
if (restr
) sb.
append("restrictions ");
if (bigoff
) sb.
append("large-offsets ");
if (boundary
) sb.
append("boundary-node ");
if (arcs
) sb.
append("arcs ");
sb.
append("destclass=");
sb.
append(destclass
);
sb.
append(" ");
if (unkFlags
) sb.
append("UNKNOWN ");
d.
item().
addText(sb.
toString());
// 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
(d, tableHeader, bigoff
);
if(arcs
)
pointerPart
(d, tableHeader, 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;
}
}
}
while (mult
!=
0 && ((reader.
position() - nodesStart
) & ((1 << mult
) -
1)) !=
0)
d.
byteValue("pad");
}
}
private TableHeader readTableHeader
(int offset
) {
return new TableHeader
(reader, offset
);
}
private void positionOffsets
(Displayer d, TableHeader currentTableHeader,
boolean bigoff
) {
DisplayItem item
;
if (bigoff
) {
item = d.
item();
short longoff =
(short) reader.
getChar();
item.
setBytes((char) longoff
);
item.
addText("longitude %.5f %+d", Utils.
toDegrees(longoff + currentTableHeader.
getLon()), longoff
);
item = d.
item();
short latoff =
(short) reader.
getChar();
item.
setBytes((char) latoff
);
item.
addText("latitude %.5f %+d", Utils.
toDegrees(latoff + currentTableHeader.
getLat()), latoff
);
} else {
item = d.
item();
int latlon = reader.
get3();
short latoff =
(short) (latlon
>> 12);
if ((latoff
& 0x800
) !=
0)
latoff |= 0xf000
;
short longoff =
(short) (latlon
& 0xfff
);
if ((longoff
& 0x800
) !=
0)
longoff |= 0xf000
;
item.
setBytes3(latlon
);
item.
addText("longitude %.5f %+d", Utils.
toDegrees(longoff + currentTableHeader.
getLon()), longoff
);
item.
addText("latitude %.5f %+d", Utils.
toDegrees(latoff + currentTableHeader.
getLat()), latoff
);
}
}
private void pointerPart
(Displayer d, TableHeader tableHeader,
long offset,
long min,
long max
) {
// looks like there are 2b before the low1 pointer and 4 after.
boolean end =
false;
boolean first =
true;
boolean lastSign =
false;
do {
// Start with alt6 byte
// bit 0x20 seems to determine whether there's an extra byte at the end
int alt6 = d.
byteValue("alt6 byte %02x");
// this is not the class of the segment, but the max of classes of the dest node
int classmask = 0x07
;
boolean newRoad =
(alt6
& ARC_HASNET
) == 0x80
;
int destclass = alt6
& classmask
;
boolean sign =
(alt6
& ARC_SIGN
) !=
0;
boolean curve =
(alt6
& ARC_CURVE
) !=
0;
d.
item().
addText("net=%d sign=%d curve=%x len=%02x destclass=%d",
newRoad
?1:
0, sign
?1:
0, curve
?1:
0, alt6
& ARC_LEN, destclass
);
// Continue with two byte values. The first one has the top
// bit set if this is the last pointer in the node record.
DisplayItem item = d.
item();
int intro1 = item.
setBytes(reader.
get());
// Note that this is the last if it is.
if ((intro1
& 0x80
) == 0x80
) {
end =
true;
item.
addText("last one");
}
// The second highest bit, means inter-section pointer
boolean longlink =
(intro1
& 0x40
) == 0x40
;
if (longlink
) {
int idx = intro1
& 0x3f
;
if (idx == 0x3f
) {
idx = item.
setBytes(reader.
get()) & 0xff
;
}
int linkoff = tableHeader.
getLink(idx
);
if (linkoff
>=
0)
item.
addText("inter-section link, index %d -> %x", idx, linkoff
);
else
item.
addText("error: inter-section link, index %d -> BAD INDEX", idx
);
} else {
// in-section relative node pointer
int intro2 = item.
setBytes(reader.
get()) & 0xff
;
short nodeoff =
(short) ((intro1
<< 8 | intro2
) & 0x3fff
);
// Construct a pointer to another node, signed 16 bit quantity
if ((nodeoff
& 0x2000
) !=
0)
nodeoff |= 0xc000
;
nodeoff
<<= mult
;
item.
addText("pointer to another node %x", nodeoff
);
long otherNode = offset + nodeoff - nodesStart
;
item.
addText(" -> node %x", otherNode
);
checkValidNode
(item, max, otherNode
);
if (min - nodesStart
> otherNode || otherNode
>= max - nodesStart
) {
item.
addText("error INVALID node: pointer out of range!");
}
}
if (first || newRoad
) {
d.
byteValue("pointer to local net index %d");
} else {
d.
item().
addText("no segment pointer");
}
int len
;
if ((alt6
& ARC_EXTRA
) == ARC_EXTRA
) {
item = d.
byteItem();
int len1 = item.
getValue() & 0xff
;
if ((len1
& 0x80
) == 0x80
) {
if ((len1
& 0x40
) == 0x40
) {
item.
addText("22 bit length + curve");
int len2 = item.
setBytes(reader.
getChar());
len =
(len1
& 0x3f
) |
(len2
<< 6); // 6+16 bits
curve =
true;
} else {
int len2 = item.
setBytes(reader.
get()) & 0xff
;
item.
addText("14 bit length %02x %02x", len1
& 0x3f, len2
);
len =
(len1
& 0x3f
) |
(len2
<< 6); // 6+8 bits
curve =
false;
}
} else {
item.
addText("15 bit length + curve");
int len2 = item.
setBytes(reader.
get()) & 0xff
;
len =
(len1
& 0x7f
) |
(len2
<< 7); // 7+8 bits
curve =
true;
}
}
else {
curve =
(alt6
& ARC_CURVE
) == ARC_CURVE
;
if(curve
)
d.
item().
addText("9 bit length + curve");
else
d.
item().
addText("9 bit length");
len =
(alt6
& ARC_LEN
) << 5;
item = d.
item();
len |= item.
setBytes(reader.
get()) & 0xff
; // 2+8 bits
}
// length should be in units of 16 feet
item.
addText("length %d (%dm)", len,
(int)(len
* 16 /
3.25));
if (first
) {
haveDir =
false;
largeDir =
!newRoad
;
}
if (first || newRoad ||
(sign
!= lastSign
)) {
fetchDirection
(d
);
}
if (curve
) {
item = d.
item();
int curvea = item.
setBytes(reader.
get()) & 0xff
;
if ((curvea
& 0xe0
) ==
0) {
int curveb = item.
setBytes(reader.
get()) & 0xff
;
item.
addText("curve[0] %02x curve[1] %02x (two bytes)", curvea, curveb
);
} else {
int angle =
360 * (byte)(((curvea
& 0x1f
) << 3) |
((curvea
& 0xe0
) >> 5)) /
256;
item.
addText("curve[0] %02x (%d deg?)", curvea, angle
);
}
}
d.
line();
first =
false;
lastSign = sign
;
} while (!end
);
}
private byte savedDir
;
private boolean haveDir
;
private boolean largeDir
;
/**
* Fetch the direction. See the {@link test.files.DirectionReader} class for more details.
* It could be used here but we want a byte by byte breakdown printed out.
*/
private int fetchDirection
(Displayer d
) {
if (largeDir
) {
DisplayItem item = d.
byteItem();
int dir = item.
getValue();
item.
addText("large direction %d (%.0fdeg)", dir,
(dir
* 360.0) /
256);
return dir
;
}
if (haveDir
) {
haveDir =
false;
int dir =
(savedDir
& 0xf0
);
d.
item().
addText("saved direction %x (%.0fdeg)", dir,
(dir
* 360.0) /
256);
return dir
;
} else {
DisplayItem item = d.
byteItem();
savedDir =
(byte) item.
getValue();
haveDir =
true;
int dir = savedDir
& 0xf
;
dir
<<=
4;
item.
addText("read direction %x (%.0fdeg)", dir,
(dir
* 360.0) /
256);
return dir
;
}
}
private void checkValidNode
(DisplayItem item,
long tablePosition,
long nodeStart
) {
// Work out if this is actually a node to help in debugging. False positives
// are possible, but not false negatives.
if (nodeStart
< 0) {
item.
addText("error INVALID node, start=%x", nodeStart
);
return;
}
long savpos = reader.
position();
reader.
position(nodeStart + nodesStart
);
int low = reader.
get() & 0xff
;
long tabpos = calcTablePosition
(nodeStart + nodesStart, low
);
if (tabpos
!= tablePosition
) {
item.
addText("error INVALID node, low=%x, tabpos=%x, max=%x", low, tabpos, tablePosition
);
}
// restore position
reader.
position(savpos
);
}
private void printTables
(Displayer d,
int len
) {
d.
setTitle("Tables");
int remain = len
;
// Get the header
TableHeader tableHeader =
new TableHeader
();
tableHeader.
setPosition(reader.
position());
int restrformat = d.
byteValue("Table C format");
remain -=
1;
int l = latLongField
(d,
"longitude");
tableHeader.
setLong(l
);
remain -=
3;
l = latLongField
(d,
"latitude");
tableHeader.
setLat(l
);
remain -=
3;
int n = d.
byteValue("%d records in Table A") & 0xff
;
remain -=
1;
int m = d.
byteValue("%d records in Table B") & 0xff
;
remain -=
1;
d.
print(outStream
);
// Now do 'Table A' (segments)
d.
setTitle("Table A (segments)");
for (int i =
0; i
< n
; i++
) {
d.
item().
addText("net pointer index %d %02x", i, i
);
DisplayItem item = d.
item();
int off = item.
setBytes3(reader.
get3());
// top 2 bits of net pointer are access bits
int access =
(off
& 0xc00000
) >> 8;
off
&= 0x3fffff
;
item.
addText("pointer to net %06x (%s)", off, getNameFromNetOff
(off
));
item = d.
item();
int paramA = item.
setBytes(reader.
get());
int paramB =
0;
if (tableARecordLen
>=
5)
paramB = item.
setBytes(reader.
get());
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++
) {
d.
item().
addText("node pointer index %d %02x", i, i
);
d.
int3Value("offset into Nod1 %x");
remain -=
3;
}
d.
print(outStream
);
// 'Table C' (restrictions)
d.
setTitle("Table C (restrictions)");
int size=
0;
if ((restrformat
& 1) !=
0) {
size = d.
byteValue("table c size") & 0xff
;
remain--
;
} else if ((restrformat
& 2) !=
0) {
size = d.
charValue("table c size") & 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");
for (int i =
0; i
< 3; i++
) {
// top bit always set
DisplayItem item = d.
item();
int off = item.
setBytes(reader.
getChar());
item.
addText("node pointer %06x",
tableHeader.
getPosition() -
(off
& 0x7fff
));
}
d.
byteValue("pointer to local net index %d");
d.
byteValue("pointer to local net index %d");
}
} else {
d.
rawValue(size,
"restrictions table (in unknown format");
remain -= size
;
}
if ((restrformat
& 4) !=
0) {
d.
byteValue("??? restrformat4");
remain--
;
}
if ((restrformat
& 8) !=
0) {
d.
byteValue("%d unpaved roads");
remain--
;
}
if((restrformat
& 0x10
) !=
0) {
d.
byteValue("%d ferry routes");
remain--
;
}
if((restrformat
& 0x03
) ==
0) {
// Sometimes there is an extra zero here. Eg mkgmap currently adds one.
// If there is no zero then we know that that isn't the case for this file.
// If it is however, the it could be the start of a node with a very short section.
// So we can't really tell, but if we found a node at this position from NOD 2,
// then we know that there definitely is not.
long pos = reader.
position();
byte b = reader.
get();
reader.
position(pos
);
// Is there a node at pos? If so then there are not extra bytes.
Offset offset = nod1recs.
get((int) pos - nodesStart
);
// Since the file either has this or not, then remember this the first time. It will
// then always be right or always wrong. Initialise hasExtraZeros at the top if
// looking at a file where this heuristic does not work.
if (hasExtraZeros ==
null) {
if (b ==
0 && offset ==
null) {
hasExtraZeros =
true;
} else {
hasExtraZeros =
false;
}
}
if (hasExtraZeros
) {
d.
byteValue("Skip zero value %d");
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
(Displayer d,
String label
) {
DisplayItem item1 = d.
item();
int l = item1.
setBytes3(reader.
get3());
if ((l
& 0x800000
) !=
0)
l |= 0xff000000
;
item1.
addText("%s %.5f %x", label, Utils.
toDegrees(l
), l
);
return l
;
}
private String getNameFromNetOff
(int off
) {
if (net
!=
null)
return net.
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, class=%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 =
String.
format("nod 2 %06x", recstart
);
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 %d");
// 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") & 0xff
;
d.
rawValue(len
>> 1,
"extra data 1");
}
if((extraFormat
& 0x02
) !=
0) {
int len = d.
byteValue("len") & 0xff
;
d.
rawValue(len
>> 1,
"extra data 2");
}
if((extraFormat
& 0x04
) !=
0) {
d.
byteValue("extra data 4");
}
if((extraFormat
& 0x08
) !=
0) {
d.
charValue("extra data 8");
}
}
else if(extraFormat == 0x0c
) {
int len = d.
byteValue("len") & 0xff
;
d.
rawValue(len
>> 1,
"extra data c");
}
else if(extraFormat == 0x0d
) {
int len = d.
byteValue("len") & 0xff
;
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") & 0xff
;
d.
rawValue(len
>> 1,
"extra data ea");
len = d.
byteValue("len") & 0xff
;
d.
rawValue(len
>> 1,
"extra data eb");
}
else if(extraFormat == 0x0f
) {
int len = d.
byteValue("len") & 0xff
;
d.
rawValue(len
>> 1,
"extra data fa");
len = d.
byteValue("len") & 0xff
;
d.
rawValue(len
>> 1,
"extra data fb");
len = d.
byteValue("len") & 0xff
;
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
) {
BitSet bset =
BitSet.
valueOf(bs
);
StringBuilder sb =
new StringBuilder();
for (int i =
0; i
< nbits
; i++
)
sb.
append(bset.
get(i
) ? "1" :
"0");
return sb.
toString();
}
/**
* This is a set of fixed length records, so is a good one to start with.
*/
private void printBoundryNodes
() {
Displayer d =
new Displayer
(reader
);
d.
setSectStart(nodesStart
);
d.
setTitle("NOD 3 (boundary nodes)");
reader.
position(boundriesStart
);
for (int pos = boundriesStart
; pos
< boundriesStart + boundriesLen
; pos += boundriesRecsize
) {
latLongField
(d,
"longitude");
latLongField
(d,
"latitude");
// limited range
d.
int3Value("offset into NOD 1 %06x");
d.
gap();
}
d.
print(outStream
);
}
private void printHeader
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("NOD header");
reader.
position();
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 %x");
mult = d.
byteValue("ptr multiplier %d");
aMask =
(1<<align
) -
1;
tableARecordLen = d.
charValue("Table A record len %d");
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 (boundary nodes) at offset %#08x");
boundriesLen = d.
intValue("NOD 3 length %d");
boundriesRecsize = d.
charValue("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.
rawValue((int) (getHeaderLen
() - reader.
position()));
d.
print(outStream
);
}
public static void main
(String[] args
) {
if (args.
length < 1) {
System.
err.
println("Usage: noddisplay <filename>");
System.
exit(1);
}
String name = args
[0];
NodDisplay nd =
new NodDisplay
();
nd.
display(name,
"NOD");
}
public void setNet
(NetDisplay net
) {
this.
net = net
;
}
}