/*
* 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: 06-Jul-2008
*/
package uk.me.parabola.imgfmt.app.net;
import java.util.Arrays;
import uk.me.parabola.imgfmt.ReadFailedException;
import uk.me.parabola.imgfmt.app.CommonHeader;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.Section;
/**
* Header information for the NOD file.
*
* This is a routing network for the map.
*
* @author Steve Ratcliffe
*/
public class NODHeader extends CommonHeader {
public static final int HEADER_LEN = 127;
static final char DEF_ALIGN = 6;
private static final char BOUNDARY_ITEM_SIZE = 9;
private final Section nodes = new Section();
private final Section roads = new Section(nodes);
private final Section boundary = new Section(roads, BOUNDARY_ITEM_SIZE);
private final Section highClassBoundary = new Section(boundary);
private final int[] classBoundaries = new int[5];
private int flags;
private int align;
private int mult1;
private int tableARecordLen;
private boolean driveOnLeft;
public NODHeader() {
super(HEADER_LEN, "GARMIN NOD");
Arrays.fill(classBoundaries, Integer.MAX_VALUE);
}
/**
* Read the rest of the header. Specific to the given file. It is guaranteed
* that the file position will be set to the correct place before this is
* called.
*
* @param reader The header is read from here.
*/
protected void readFileHeader(ImgFileReader reader) throws ReadFailedException {
nodes.readSectionInfo(reader, false);
flags = reader.getChar();
reader.getChar();
align = reader.get();
mult1 = reader.get();
tableARecordLen = reader.getChar();
roads.readSectionInfo(reader, false);
reader.getInt();
boundary.readSectionInfo(reader, true);
reader.getInt();
if (getHeaderLength() > 0x3f) {
highClassBoundary.readSectionInfo(reader, false);
classBoundaries[0] = reader.getInt();
classBoundaries[1] = classBoundaries[0] + reader.getInt();
classBoundaries[2] = classBoundaries[1] + reader.getInt();
classBoundaries[3] = classBoundaries[2] + reader.getInt();
classBoundaries[4] = classBoundaries[3] + reader.getInt();
}
}
/**
* Write the rest of the header. It is guaranteed that the writer will be set
* to the correct position before calling.
*
* @param writer The header is written here.
*/
// multiplier shift for road + arc length values, the smaller the shift the higher the precision and NOD size
// as it has an influence on the number of bits needed to encode a length
final static int DISTANCE_MULT_SHIFT = 1; // 0..7 1 seems to be a good compromise
final static int DISTANCE_MULT = 1 << DISTANCE_MULT_SHIFT;
protected void writeFileHeader(ImgFileWriter writer) {
nodes.setPosition(HEADER_LEN);
nodes.writeSectionInfo(writer);
// 0x0001 always set, meaning ?
// 0x0002 (enable turn restrictions)
// 0x001c meaning ?
// 0x00E0 distance multiplier, effects predicted travel time
int flags = 0x0207;
assert Integer.bitCount(DISTANCE_MULT) == 1;
assert DISTANCE_MULT_SHIFT < 8;
flags |= DISTANCE_MULT_SHIFT << 5;
if(driveOnLeft)
flags |= 0x0100;
writer.putInt(flags);
byte align = DEF_ALIGN;
writer.put(align);
writer.put((byte) 0); // pointer multiplier
writer.putChar((char) 5);
roads.writeSectionInfo(writer);
writer.putInt(0);
boundary.writeSectionInfo(writer);
// new fields for header length > 0x3f
writer.putInt(2); // no other value spotted, meaning ?
highClassBoundary.writeSectionInfo(writer);
writer.putInt(classBoundaries[0]);
for (int i = 1; i < classBoundaries.length; i++){
writer.putInt(classBoundaries[i] - classBoundaries[i-1]);
}
}
private static final double UNIT_TO_METER = 2.4;
public static int metersToRaw(double m) {
double d = m / (DISTANCE_MULT * UNIT_TO_METER);
return (int) Math.round(d);
}
public int getNodeStart() {
return nodes.getPosition();
}
public void setNodeStart(int start) {
nodes.setPosition(start);
}
public int getNodeSize() {
return nodes.getSize();
}
public void setNodeSize(int size) {
nodes.setSize(size);
}
public Section getNodeSection() {
return nodes;
}
public void setRoadSize(int size) {
roads.setSize(size);
}
public Section getRoadSection() {
return roads;
}
public void setBoundarySize(int size) {
boundary.setSize(size);
}
public Section getBoundarySection() {
return boundary;
}
public void setHighClassBoundarySize(int size) {
highClassBoundary.setSize(size);
}
public Section getHighClassBoundary() {
return highClassBoundary;
}
public int[] getClassBoundaries() {
return classBoundaries;
}
public void setDriveOnLeft(boolean dol) {
driveOnLeft = dol;
}
public int getFlags() {
return flags;
}
public int getAlign() {
return align;
}
public int getMult1() {
return mult1;
}
public int getTableARecordLen() {
return tableARecordLen;
}
}