Rev 189 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* 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.util.ArrayList;
import uk.me.parabola.imgfmt.Utils;
/**
* Standalone program to convert a routing network from NOD to OSM format
* for visualising in, say, JOSM.
*/
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
private static final int NOBIKE = 0x20
;
private static final int NOFOOT = 0x10
;
// missing restrictions: emergency, delivery, bus, car, taxi, truck
// private static final int NOOTHER = 0xcf;
private int nodesStart
;
private int nodesLen
;
private int 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='0x%02x' />\n";
private final String strtag =
"<tag k='%s' v='%s' />\n";
protected void print
() {
readCommonHeader
();
printHeader
();
System.
out.
printf("<?xml version='1.0' encoding='UTF-8'?>\n");
System.
out.
printf("<osm version='0.5' generator='NodConvert'>\n");
printNod1
();
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 printNod1
() {
reader.
position(nodesStart
);
while (reader.
position() < nodesStart + nodesLen
) {
long start = reader.
position();
int low = reader.
get() & 0xff
;
long end = calcTablePosition
(start, low
);
reader.
position(reader.
position() -
1); // step back to low byte
curTable = readTable
(end
);
printNode
(end
);
reader.
position(curTable.
next);
}
}
private void printNode
(long end
) {
long groupStart = reader.
position();
while (reader.
position() < end
) {
long nodeOff = reader.
position();
int low = reader.
get();
if (low ==
0) return; // XXX
calcTablePosition
(nodeOff, low
);
int flags = reader.
get();
System.
out.
printf("<node id='%d' ", nodeOff+
1-nodesStart
);
boolean restr =
(flags
& 0x10
) == 0x10
; // restrictions present at this node
boolean bigoff =
(flags
& 0x20
) == 0x20
; // 2 bytes each for lat/lon offsets
boolean boundary =
(flags
& 0x08
) == 0x08
; // this is a boundry node
positionOffsets
(curTable, bigoff
);
System.
out.
printf("<tag k='restrictions' v='%b' />\n", restr
);
System.
out.
printf("<tag k='boundary' v='%b' />\n", boundary
);
System.
out.
printf("<tag k='offset' v='0x%08x' />\n", nodeOff-nodesStart
);
System.
out.
printf(inttag,
"center", curTable.
id);
System.
out.
printf("</node>\n");
pointerPart
(nodeOff, groupStart, end
);
}
}
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
) {
System.
err.
println("reading table at " + 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("<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++
) {
reader.
get3();
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
);
table.
tableB.
add(reader.
get3());
}
// 'Table C' (restrictions)
int size=
0;
// I've seen restrbytes up to 2
if (restrbytes ==
0) {
reader.
get();
} else if (restrbytes ==
1) {
size = reader.
get() & 0xff
;
} else if (restrbytes ==
2) {
size = reader.
getChar() & 0xffff
;
} else if (restrbytes ==
3) {
size = reader.
getChar() & 0xffffff
;
} else {
}
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 {
}
table.
next = reader.
position();
// Restore position
reader.
position(orig
);
return table
;
}
private void positionOffsets
(Table currentTable,
boolean bigoff
) {
short longoff
;
short latoff
;
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("lon='%f' lat='%f'>\n",
Utils.
toDegrees(longoff + currentTable.
lon),
Utils.
toDegrees(latoff + currentTable.
lat));
}
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 {
System.
out.
printf("<way id='%d'>\n", reader.
position());
System.
out.
printf("<nd ref='%d' />\n", offset+
1-nodesStart
);
// 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
boolean newdir =
(alt6
& NEWDIR
) == 0x80
;
System.
out.
printf(booltag,
"newdir", newdir
);
int classmask = 0x07
;
int destclass = alt6
& classmask
;
System.
out.
printf(inttag,
"destclass", destclass
);
System.
out.
printf(booltag,
"sign",
(alt6
& SIGN
) !=
0);
if (first
) {
assert !newdir
;
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 intro1 = reader.
get() & 0xff
;
// Note that this is the last if it is.
if ((intro1
& 0x80
) == 0x80
) {
end =
true;
System.
out.
printf(booltag,
"last",
true);
}
// The second highest bit, means inter-section pointer
boolean bit2 =
(intro1
& 0x40
) == 0x40
;
System.
out.
printf(booltag,
"external", bit2
);
if (bit2
) {
int idx = intro1
& 0x3f
;
if (idx == 0x3f
) {
idx = reader.
get() & 0xff
;
}
System.
out.
printf(inttag,
"indexB", idx
);
System.
out.
printf("<nd ref='%d' />\n",
curTable.
tableB.
get(idx
) +
1);
} else {
// in-section relative node pointer
int intro2 = 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
;
long otherNode = offset + nodeoff
;
System.
out.
printf("<nd ref='%d' />\n", otherNode+
1-nodesStart
);
}
if (newdir
) {
int indexA = reader.
get() & 0xff
;
System.
out.
printf(inttag,
"indexA", indexA
);
System.
out.
printf(inttag,
"roadclass",
curTable.
tableA.
get(indexA
));
}
System.
out.
printf(strtag,
"type", newdir
? "arc" :
"link");
int len
;
boolean curve
;
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
curve =
true;
} else {
int len2 = reader.
get() & 0xff
;
len =
(len1
& 0x3f
) |
(len2
<< 6); // 6+8 bits
curve =
false;
}
} else {
int len2 = reader.
get() & 0xff
;
len =
(len1
& 0x7f
) |
(len2
<< 7); // 7+8 bits
curve =
true;
}
} else {
curve =
(alt6
& CURVE
) == CURVE
;
len =
(alt6
& 0x18
) << 5;
len |= reader.
get() & 0xff
; // 2+8 bits
}
System.
out.
printf(inttag,
"length", len
);
if (newdir
) {
int dir = reader.
get() & 0xff
;
System.
out.
printf(inttag,
"direction",
(dir
* 360) >> 8);
}
if (curve
) {
int curvea = reader.
get() & 0xff
;
int curveb
;
if (curvea
< 0x20
) {
curveb = reader.
get() & 0xff
;
System.
out.
printf(inttag,
"curvebytes",
2);
} else {
System.
out.
printf(inttag,
"curvebytes",
1);
curveb =
(curvea
& 0x1f
) << 3;
curvea
&= 0x70
;
}
System.
out.
printf(inttag,
"curvea", curvea
);
System.
out.
printf(inttag,
"curveb", curveb
);
}
System.
out.
printf("</way>\n");
} while (!end
);
}
private int latLongField
() {
int l = reader.
get3();
if ((l
& 0x800000
) !=
0)
l |= 0xff000000
;
return l
;
}
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
);
d.
charValue("???");
d.
charValue("???");
//d.charValue("???");
align = d.
byteValue("node align");
aMask =
(1<<align
) -
1;
d.
byteValue("???");
d.
charValue("???");
int roadDataStart = d.
intValue("NOD 2 (road data) at offset %#08x");
int roadDataLen = d.
intValue("NOD 2 length %d");
d.
item().
addText("End of section %08x, len %#08x", roadDataStart +
roadDataLen, roadDataLen
);
d.
intValue("???");
int boundriesStart = d.
intValue("NOD 3 (boundry nodes) at offset %#08x");
int boundriesLen = d.
intValue("NOD 3 length %d");
byte 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
);
}
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");
}
}