Subversion Repositories mkgmap

Rev

Rev 3366 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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;
        }
}