Blame |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 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 3 or
* 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.
*/
package test.files;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import java.util.*;
import static test.util.AccessBits.*;
import static test.util.RestrictionBits.CLASS;
import static test.util.RestrictionBits.SPEED;
/**
* The tables in the NOD1 section.
*
* @author Steve Ratcliffe
*/
public class RouteCenter
{
private Coord coord
;
private final long offset
;
private long next
; // The next node starts at this offset.
private final List<TableA
> tableA =
new ArrayList<>();
private final List<TableB
> tableB =
new ArrayList<>();
private final TableC tableC =
new TableC
();
private final Map<Integer, RouteNode
> nodes =
new LinkedHashMap<>();
private int index
;
public RouteCenter
(long start
) {
offset = start
;
}
public static RouteCenter readTable
(ImgFileReader r,
int nodesStart,
long start
) {
RouteCenter center =
new RouteCenter
(start
);
r.
position(nodesStart + start
);
center.
readTables(r
);
return center
;
}
public long getOffset
() {
return offset
;
}
public long getNext
() {
return next
;
}
public int getCPointerSize
() {
return tableC.
getPointerSize();
}
public Coord getCoord
() {
return coord
;
}
public int getLat
() {
return coord.
getLatitude();
}
public int getLon
() {
return coord.
getLongitude();
}
/**
* Read in all the node table information for a route center.
*
* Each route center has a location, a table of pointers to NET, a table of pointers to
* NOD1 (for links to other route centers) and a table of route restrictions.
*
* @param r The img file reader, which should be positioned at the start of the tables.
* At the end it will be just after, ready to read the next node.
*/
private void readTables
(ImgFileReader r
) {
int restrformat = r.
get() & 0xff
;
int lon = latLongField
(r
);
int lat = latLongField
(r
);
coord =
new Coord
(lat, lon
);
int nTabA = r.
get() & 0xff
;
int nTabB = r.
get() & 0xff
;
// Now do 'Table A' (segments)
for (int i =
0; i
< nTabA
; i++
) {
TableA a = readTableARecord
(r
);
tableA.
add(a
);
}
// 'Table B' (inter-section pointers)
for (int i =
0; i
< nTabB
; i++
) {
TableB b =
new TableB
();
b.
setNodOffset(r.
get3());
tableB.
add(b
);
}
// 'Table C' (restrictions)
int size =
0;
if (restrformat ==
0) {
// if next byte is 0, discard it
long pos = r.
position();
int val = r.
get();
if (val
!=
0) {
// rewind so that byte gets used
r.
position(pos
);
}
} else if ((restrformat
& 1) !=
0) {
size = r.
get() & 0xff
;
} else if ((restrformat
& 2) !=
0) {
size = r.
getChar() & 0xffff
;
}
tableC.
setRaw(r.
get(size
));
if ((restrformat
& 4) !=
0) tableC.
unk1 = r.
get();
if ((restrformat
& 8) !=
0) tableC.
unpaved = r.
get();
if ((restrformat
& 0x10
) !=
0) tableC.
ferry = r.
get();
if (restrformat
!=
0 && (restrformat
& 0x03
) ==
0)
tableC.
unk2 = r.
get();
next = r.
position();
}
private TableA readTableARecord
(ImgFileReader r
) {
TableA a =
new TableA
();
// Get the NET pointer.
int off = r.
get3();
// top 2 bits of net pointer are access bits
int access = off
& 0xc0_00_00
;
off
&= 0x3fffff
;
a.
setNetOffset(off
);
int paramA = r.
get() & 0xff
;
int paramB = r.
get() & 0xff
;
a.
setClass((paramA
& CLASS
) >> 4);
a.
setSpeed((paramA
& SPEED
));
a.
setAccess(access | paramB
);
return a
;
}
private int latLongField
(ImgFileReader r
) {
int l = r.
get3();
if ((l
& 0x800000
) !=
0)
l |= 0xff000000
;
return l
;
}
public void addNode
(RouteNode node
) {
nodes.
put(node.
getOffset(), node
);
}
public void setIndex
(int index
) {
this.
index = index
;
}
public int getIndex
() {
return index
;
}
public Map<Integer, RouteNode
> nodes
() {
return nodes
;
}
public int linkId
(int idx
) {
if (idx
< 0 || idx
>= tableB.
size())
return -
1;
TableB b = tableB.
get(idx
);
return b.
getNodOffset();
}
public TableA getNet
(int idx
) {
if (idx
< 0 || idx
>= tableA.
size())
return null;
TableA a = tableA.
get(idx
);
return a
;
}
public void resort
() {
List<RouteNode
> nlist =
new ArrayList<>(nodes.
values());
Collections.
sort(nlist,
new Comparator<RouteNode
>() {
public int compare
(RouteNode o1, RouteNode o2
) {
Integer i1 = o1.
getOffset();
Integer i2 = o2.
getOffset();
return i1.
compareTo(i2
);
}
});
nodes.
clear();
for (RouteNode node : nlist
)
nodes.
put(node.
getOffset(), node
);
}
/**
* A table A record contains a link to NET1 and access restriction information.
*/
class TableA
{
private int netOffset
;
private int roadClass
;
private int speed
;
private int access
;
public void setNetOffset
(int netOffset
) {
this.
netOffset = netOffset
;
}
public int getNetOffset
() {
return netOffset
;
}
public void setClass
(int aClass
) {
this.
roadClass = aClass
;
}
public int getRoadClass
() {
return roadClass
;
}
public void setRoadClass
(int roadClass
) {
this.
roadClass = roadClass
;
}
public void setSpeed
(int speed
) {
this.
speed = speed
;
}
public int getSpeed
() {
return speed
;
}
public void setAccess
(int access
) {
this.
access = access
;
}
public int getAccess
() {
return access
;
}
public String noAccessString
() {
StringBuilder sb =
new StringBuilder();
if ((access
& NOEMERGENCY
) !=
0) sb.
append("emergency,");
if ((access
& NODELIVERY
) !=
0) sb.
append("delivery,");
if ((access
& NOCAR
) !=
0) sb.
append("car,");
if ((access
& NOBUS
) !=
0) sb.
append("bus,");
if ((access
& NOTAXI
) !=
0) sb.
append("taxi,");
if ((access
& NOCARPOOL
) !=
0) sb.
append("carpool,");
if ((access
& NOFOOT
) !=
0) sb.
append("foot,");
if ((access
& NOBIKE
) !=
0) sb.
append("bike,");
if ((access
& NOTRUCK
) !=
0) sb.
append("truck,");
// Trim final comma, if there is anything.
if (sb.
length() > 1)
sb.
setLength(sb.
length() -
1);
return sb.
toString();
}
}
/**
* Table B consists of pointers into NOD1. Used for jumps between different route centres.
*/
class TableB
{
private int nodOffset
;
public void setNodOffset
(int nodOffset
) {
this.
nodOffset = nodOffset
;
}
public int getNodOffset
() {
return nodOffset
;
}
}
class TableC
{
// The raw value of table C (restrictions)
private byte[] raw
;
private int size
;
public byte unk1
;
public byte unpaved
;
public byte ferry
;
public byte unk2
;
public void setRaw
(byte[] raw
) {
this.
raw = raw
;
}
public int getPointerSize
() {
if (size
> 127)
return 2;
else
return 1;
}
}
}