/*
* Copyright (C) 2008 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: 18-Jul-2008
*/
package uk.me.parabola.imgfmt.app.net;
import java.util.HashMap;
import java.util.LinkedHashMap;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.log.Logger;
/**
* Table A that contains road information for segments in one RouteCenter.
*
* Each arc starting from a node in the RouteCenter has an associated
* entry in Table A, shared by the inverse arc for internal arcs. This
* entry consists of some routing parameters and a link to the road in
* NET.
*/
public class TableA {
private static final Logger log = Logger.getLogger(TableA.class);
private static final int ITEM_SIZE = 5;
// This table's start position relative to the start of NOD 1
private int offset;
// arcs for roundabouts
private final HashMap<RoadDef,Integer> roundaboutArcs = new LinkedHashMap<RoadDef,Integer>();
// arcs for unpaved ways
private final HashMap<RoadDef,Integer> unpavedArcs = new LinkedHashMap<RoadDef,Integer>();
// arcs for ferry ways
private final HashMap<RoadDef,Integer> ferryArcs = new LinkedHashMap<RoadDef,Integer>();
// arcs for paved ways
private final HashMap<RoadDef,Integer> pavedArcs = new LinkedHashMap<RoadDef,Integer>();
private static int count;
private boolean frozen ; // true when no more arcs should be added
public TableA() {
log.debug("creating TableA", count);
count++;
}
/**
* Add an arc to the table if not present and set its index.
*
* The value may overflow while it isn't certain that
* the table fulfils the size constraint.
*/
public void addArc(RouteArc arc) {
assert !frozen : "trying to add arc to Table A after it has been frozen";
int i;
RoadDef rd = arc.getRoadDef();
if(rd.isRoundabout()) {
if (!roundaboutArcs.containsKey(rd)) {
i = roundaboutArcs.size();
roundaboutArcs.put(rd, i);
log.debug("added roundabout arc", count, rd, i);
}
}
else if(rd.ferry()) {
if (!ferryArcs.containsKey(rd)) {
i = ferryArcs.size();
ferryArcs.put(rd, i);
log.debug("added ferry arc", count, rd, i);
}
}
else if(rd.paved()) {
if (!pavedArcs.containsKey(rd)) {
i = pavedArcs.size();
pavedArcs.put(rd, i);
log.debug("added paved arc", count, rd, i);
}
}
else {
if (!unpavedArcs.containsKey(rd)) {
i = unpavedArcs.size();
unpavedArcs.put(rd, i);
log.debug("added unpaved arc", count, rd, i);
}
}
}
/**
* Retrieve an arc's index.
* Order in table A: roundabouts, unpaved, ferry, paved
*/
public byte getIndex(RouteArc arc) {
frozen = true; // don't allow any more arcs to be added
int i;
RoadDef rd = arc.getRoadDef();
if(rd.isRoundabout()) {
assert roundaboutArcs.containsKey(rd):
"Trying to read Table A index for non-registered arc: " + count + " " + rd;
i = roundaboutArcs.get(rd);
}
else if(rd.ferry()) {
assert ferryArcs.containsKey(rd):
"Trying to read Table A index for non-registered arc: " + count + " " + rd;
i = roundaboutArcs.size() + unpavedArcs.size() + ferryArcs.get(rd);
}
else if(rd.paved()) {
assert pavedArcs.containsKey(rd):
"Trying to read Table A index for non-registered arc: " + count + " " + rd;
i = roundaboutArcs.size() + unpavedArcs.size() + ferryArcs.size() + pavedArcs.get(rd);
}
else {
assert unpavedArcs.containsKey(rd):
"Trying to read Table A index for non-registered arc: " + count + " " + rd;
i = roundaboutArcs.size() + unpavedArcs.get(rd);
}
assert i < 0x100 : "Table A index too large: " + rd;
return (byte) i;
}
/**
* Retrieve the size of the Table as an int.
*
* While Table A is limited to byte size (0x100 entries),
* we temporarily build larger tables while subdividing
* the network.
*/
public int size() {
return roundaboutArcs.size() + unpavedArcs.size() + ferryArcs.size() + pavedArcs.size();
}
public int numRoundaboutArcs() {
return roundaboutArcs.size();
}
public int numUnpavedArcs() {
return unpavedArcs.size();
}
public int numFerryArcs() {
return ferryArcs.size();
}
/**
* Retrieve the size of the table as byte.
*
* This value is what should be written to the table
* header. When this is read, the table is assumed to
* be fit for writing, so at this point we check
* it isn't too large.
*/
public byte getNumberOfItems() {
assert size() < 0x100 : "Table A too large";
return (byte)size();
}
/**
* This is called first to reserve enough space. It will be rewritten
* later.
*/
public void write(ImgFileWriter writer) {
offset = writer.position();
int size = size() * ITEM_SIZE;
log.debug("tab a offset", offset, "tab a size", size);
for (int i = 0; i < size; i++)
writer.put((byte) 0);
}
/**
* Fill in the table once the NET offsets of the roads are known.
*/
public void writePost(ImgFileWriter writer) {
writer.position(offset);
// unpaved arcs first
for (RoadDef rd: roundaboutArcs.keySet()) {
writePost(writer, rd);
}
for (RoadDef rd: unpavedArcs.keySet()) {
writePost(writer, rd);
}
// followed by the ferry arcs
for (RoadDef rd : ferryArcs.keySet()) {
writePost(writer, rd);
}
// followed by the paved arcs
for (RoadDef rd : pavedArcs.keySet()) {
writePost(writer, rd);
}
}
public void writePost(ImgFileWriter writer, RoadDef rd) {
// write the table A entries. Consists of a pointer to net
// followed by 2 bytes of class and speed flags and road restrictions.
int pos = rd.getOffsetNet1();
int access = rd.getTabAAccess();
// top bits of access go into net1 offset
final int ACCESS_TOP_BITS = 0xc000;
pos |= (access & ACCESS_TOP_BITS) << 8;
access &= ~ACCESS_TOP_BITS;
writer.put3(pos);
writer.put((byte) rd.getTabAInfo());
writer.put((byte) access);
}
}