Subversion Repositories mkgmap

Rev

Rev 3408 | 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: 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);
        }
}