Rev 533 |
Blame |
Compare with Previous |
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 java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import test.display.check.Log;
import static test.util.AccessBits.*;
import static test.util.RestrictionBits.*;
/**
* The tables in the NOD1 section.
*
* @author Steve Ratcliffe
*/
public class RouteCenter
{
private Coord coord
;
private final long offset
;
private final int ptrShift
;
private final int nodesStart
;
private long next
; // The next node starts at this offset.
private final List<TableA
> tableA =
new ArrayList<>();
private final int tableARecordLen
;
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 static void setHasExtraZeros
(Boolean hasExtraZeros
) {
RouteCenter.
hasExtraZeros = hasExtraZeros
;
}
private static Boolean hasExtraZeros
;
public RouteCenter
(long start,
int tableARecordLen,
int ptrShift,
int nodesStart
) {
this.
tableARecordLen = tableARecordLen
;
offset = start
;
this.
ptrShift = ptrShift
;
this.
nodesStart = nodesStart
;
}
public void readTable
(ImgFileReader r,
long start
) {
r.
position(nodesStart + start
);
readTables
(r
);
}
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.
get1u();
int lon = r.
get3s();
int lat = r.
get3s();
coord =
new Coord
(lat, lon
);
int nTabA = r.
get1u();
int nTabB = r.
get1u();
// 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.
get3u() << ptrShift
);
tableB.
add(b
);
}
// 'Table C' (restrictions)
int size =
0;
if ((restrformat
& 1) !=
0) {
size = r.
get1u();
} else if ((restrformat
& 2) !=
0) {
size = r.
get2u();
}
tableC.
setPtrSize(size
> 127? 2:
1);
//byte[] raw = r.get(size);
//tableC.setRaw(raw);
readTableC
(r, size, restrformat
);
if ((restrformat
& 4) !=
0) tableC.
setRoundabout(r.
get());
if ((restrformat
& 8) !=
0) tableC.
setUnpaved(r.
get());
if ((restrformat
& 0x10
) !=
0) tableC.
setFerry(r.
get());
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 = r.
position();
byte b = r.
get();
r.
position(pos
);
// Is there a node at pos? If so then there are not extra bytes.
// 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) {
System.
out.
println("setting extra zero option");
hasExtraZeros =
true;
} else {
hasExtraZeros =
false;
}
}
if (hasExtraZeros
) {
r.
get();
}
}
next = r.
position();
}
/**
* Read the restriction in table C.
* @param r The file reader.
* @param size The size of the Table C section.
* @param format The restriction format flags.
*/
private void readTableC
(ImgFileReader r,
int size,
int format
) {
int start =
(int) r.
position();
int end =
(int) (r.
position() + size -
(format
& 0x3
));
while (r.
position() < end
) {
Restriction restriction =
new Restriction
((int) r.
position() - start
);
// read the type
int restrType = r.
get1u();
if ((restrType
& 0xf
) !=
5) {
Log.
error("Unknown restriction type %x", restrType
);
continue;
}
int flags = r.
get1u();
restriction.
setFlags(flags
);
int numItems =
(flags
>>> 5) & 0xff
;
boolean extra =
(flags
& RESTR_EXTRA
) !=
0;
r.
get(); // unknown, usually 0
if ((flags
& MORE_EXCEPTIONS
) !=
0) {
//0x01: car; 0x02: bus; 0x04: taxi; 0x10: delivery; 0x20: bicycle; 0x40: truck
int vehicleExceptions = r.
get1u();
restriction.
setVehicleExceptions(vehicleExceptions
);
}
// The list of nodes that are involved
for (int i =
0; i
< numItems +
1; i++
) {
int off = r.
get2u();
int nodeOffset
;
if ((off
& 0x8000
) ==
0) {
int tabB = off
& 0x00ff
;
nodeOffset = tableB.
get(tabB
).
getNodOffset();
} else {
nodeOffset =
(int) offset -
(off
& 0x7fff
);
}
restriction.
addNodeOffset(nodeOffset
);
}
// The list of roads that are involved.
for (int i =
0; i
< numItems
; i++
) {
int ri = r.
get1u();
int roadOffset = tableA.
get(ri
).
getNetOffset();
restriction.
addRoadOffset(roadOffset
);
}
if (extra
) {
r.
get();
int b = r.
get1u();
if (b == 0x88
) {
//d.byteValue("??");
r.
get();
} else if (b == 0x8c
) {
r.
get2u();
} else if (b == 0x8d
) {
//d.intValue(3, "??");
//d.byteValue("??");
r.
get();
byte f1 = r.
get();
if ((f1
& 0x80
) ==
0) {
r.
get();
} else {
r.
get2u();
}
} else if (b == 0x08 || b == 0x0c
) {
r.
get(5);
//} else if ( b == 0x0c) {
// int val = d.intValue("??");
// //d.charValue("??");
// while ((val & 0x100000) == 0) {
// d.charValue("??");
// val = d.intValue("??");
// }
}
}
tableC.
add(restriction
);
}
}
private TableA readTableARecord
(ImgFileReader r
) {
TableA a =
new TableA
();
// Get the NET pointer.
int off = r.
get3u();
// top 2 bits of net pointer are access bits
int access = off
& 0xc0_00_00
;
off
&= 0x3fffff
;
a.
setNetOffset(off
);
int paramA = r.
get1u();
int paramB =
0;
if (tableARecordLen
>=
5) {
paramB = r.
get1u();
}
a.
setParam(paramA
);
a.
setClass((paramA
& CLASS
) >> 4);
a.
setSpeed((paramA
& SPEED
));
a.
setAccess(access | paramB
);
return a
;
}
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 List<TableA
> getTableA
() {
return tableA
;
}
public List<TableB
> getTableB
() {
return tableB
;
}
public List<Restriction
> getRestrictions
() {
return tableC.
getRestrictions();
}
public Restriction getRestriction
(int val
) {
return tableC.
getRestriction(val
);
}
/**
* A table A record contains a link to NET1 and access restriction information.
*/
public class TableA
{
private int netOffset
;
private int roadClass
;
private int speed
;
private int access
;
private int param
;
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 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();
}
public void setParam
(int param
) {
this.
param = param
;
}
public boolean isOneway
() {
return (this.
param & 0x8
) !=
0;
}
}
/**
* Table B consists of pointers into NOD1. Used for jumps between different route centres.
*/
public class TableB
{
private int nodOffset
;
public void setNodOffset
(int nodOffset
) {
this.
nodOffset = nodOffset
;
}
public int getNodOffset
() {
return nodOffset
;
}
}
public class Restriction
{
private final int offset
;
private int restrType
;
private int flags
;
private int vehicleExceptions
;
private List<Integer> nodeOffsets =
new ArrayList<>();
private List<Integer> roadOffsets =
new ArrayList<>();
public Restriction
(int offset
) {
this.
offset = offset
;
}
public int getOffset
() {
return offset
;
}
public void setRestrType
(int restrType
) {
this.
restrType = restrType
;
}
void addNodeOffset
(int off
) {
nodeOffsets.
add(off
);
}
void addRoadOffset
(int off
) {
roadOffsets.
add(off
);
}
public void setFlags
(int flags
) {
this.
flags = flags
;
}
public int getFlags
() {
return flags
;
}
public void setVehicleExceptions
(int vehicleExceptions
) {
this.
vehicleExceptions = vehicleExceptions
;
}
public int getVehicleExceptions
() {
return vehicleExceptions
;
}
public List<Integer> getNodeOffsets
() {
return nodeOffsets
;
}
public List<Integer> getRoadOffsets
() {
return roadOffsets
;
}
}
public class TableC
{
// The raw value of table C (restrictions)
private byte roundabout
;
private byte unpaved
;
private byte ferry
;
private int ptrSize
;
Map<Integer, Restriction
> restrictions =
new LinkedHashMap<>();
public void add
(Restriction rstr
) {
restrictions.
put(rstr.
getOffset(), rstr
);
}
public List<Restriction
> getRestrictions
() {
return new ArrayList<>(restrictions.
values());
}
public int getPointerSize
() {
return ptrSize
;
}
public void setPtrSize
(int ptrSize
) {
this.
ptrSize = ptrSize
;
}
public byte getRoundabout
() {
return roundabout
;
}
public void setRoundabout
(byte roundabout
) {
this.
roundabout = roundabout
;
}
public byte getUnpaved
() {
return unpaved
;
}
public void setUnpaved
(byte unpaved
) {
this.
unpaved = unpaved
;
}
public byte getFerry
() {
return ferry
;
}
public void setFerry
(byte ferry
) {
this.
ferry = ferry
;
}
public Restriction getRestriction
(int n
) {
return restrictions.
get(n
);
}
}
}