Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2008
 *
 *  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.
 *
 * Create date: 07-Jul-2008
 */

package uk.me.parabola.imgfmt.app.net;

import java.util.List;

import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.log.Logger;

/**
 * Routing nodes are divided into areas which I am calling RouteCenter's.
 * The center has a location and it contains nodes that are nearby.
 * There is routing between nodes in the center and there are links
 * to nodes in other centers.
 */

public class RouteCenter {
        private static final Logger log = Logger.getLogger(RouteCenter.class);

        private final Area area;
        private final Coord centralPoint;

        private final List<RouteNode> nodes;

        private final TableA tabA;
        private final TableB tabB;
        private final TableC tabC;

        public RouteCenter(Area area, List<RouteNode> nodes,
                                           TableA tabA, TableB tabB) {

                this.area = area;
                this.centralPoint = area.getCenter();
                this.nodes = nodes;
                this.tabA = tabA;
                this.tabB = tabB;
                this.tabC = new TableC(tabA);

                log.info("new RouteCenter at " + centralPoint.toDegreeString() +
                                 ", nodes: " + nodes.size()     + " tabA: " + tabA.size() +
                                 " tabB: " + tabB.size());
        }

        /**
         * update arcs with table indices; populate tabC
         */

        private void updateOffsets(){
                for (RouteNode node : nodes) {
                        node.setOffsets(centralPoint);
                        for (RouteArc arc : node.arcsIteration()) {
                                arc.setIndexA(tabA.getIndex(arc));
                                arc.setInternal(nodes.contains(arc.getDest()));
                                if (!arc.isInternal())
                                        arc.setIndexB(tabB.getIndex(arc.getDest()));
                        }

                        for (RouteRestriction restr : node.getRestrictions()){
                                if (restr.getArcs().size() >= 3){
                                        // only restrictions with more than 2 arcs can contain further arcs
                                        for (RouteArc arc : restr.getArcs()){
                                                if (arc.getSource() == node)
                                                        continue;
                                                arc.setIndexA(tabA.getIndex(arc));
                                                arc.setInternal(nodes.contains(arc.getDest()));
                                                if (!arc.isInternal())
                                                        arc.setIndexB(tabB.getIndex(arc.getDest()));
                                        }
                                }
                                restr.setOffsetC(tabC.addRestriction(restr));
                        }
                }
                // update size of tabC offsets, now that tabC has been populated
                tabC.propagateSizeBytes();
        }
       
        /**
         * Write a route center.
         *
         * writer.position() is relative to the start of NOD 1.
         * Space for Table A is reserved but not written. See writeTableA.
         */

        public void write(ImgFileWriter writer, int[] classBoundaries) {
                assert !nodes.isEmpty(): "RouteCenter without nodes";
                updateOffsets();
                int centerPos = writer.position();
                for (RouteNode node : nodes){
                        node.write(writer);
                        int group = node.getGroup();
                        if (group == 0)
                                continue;
                        if (centerPos < classBoundaries[group-1]){
                                // update positions (loop is used because style might not use all classes  
                                for (int i = group-1; i >= 0; i--){
                                        if (centerPos < classBoundaries[i] )
                                                classBoundaries[i] = centerPos;
                                }
                        }
                }
                int alignment = 1 << NODHeader.DEF_ALIGN;
                int alignMask = alignment - 1;

                // Calculate the position of the tables.
                int tablesOffset = (writer.position() + alignment) & ~alignMask;
                log.debug("write table a at offset", Integer.toHexString(tablesOffset));

                // Go back and fill in all the table offsets
                for (RouteNode node : nodes) {
                        int pos = node.getOffsetNod1();
                        log.debug("node pos", pos);
                        byte bo = (byte) calcLowByte(pos, tablesOffset);

                        writer.position(pos);
                        log.debug("rewrite taba offset", writer.position(), bo);
                        writer.put(bo);

                        // fill in arc pointers
                        node.writeSecond(writer);
                }

                writer.position(tablesOffset);

                // Write the tables header
                writer.put(tabC.getFormat());
                writer.put3(centralPoint.getLongitude());
                writer.put3(centralPoint.getLatitude());
                writer.put(tabA.getNumberOfItems());
                writer.put(tabB.getNumberOfItems());

                tabA.write(writer);
                tabB.write(writer);
                tabC.write(writer, tablesOffset);
                log.info("end of center:", writer.position());
        }

        public void writePost(ImgFileWriter writer) {
                // NET addresses are now known
                tabA.writePost(writer);
                // all RouteNodes now have their NOD1 offsets
                tabB.writePost(writer);
        }

        /**
         * Inverse of calcTableOffset.
         */

        private static int calcLowByte(int nodeOffset, int tablesOffset) {
                assert nodeOffset < tablesOffset;
                int align = NODHeader.DEF_ALIGN;
                int mask = (1 << align) - 1;
                if ((tablesOffset & mask) != 0) {
                        log.warn("tablesOffset not a multiple of (1<<align): %x", tablesOffset);
                        // round up to next multiple
                        tablesOffset = ((tablesOffset >> align) + 1) << align;
                }
                int low = (tablesOffset >> align) - (nodeOffset >> align) - 1;
                assert 0 <= low && low < 0x100;
                return low;
        }

        public Area getArea() {
                return area;
        }

        public String reportSizes() {
                int nodesSize = 0;
                for(RouteNode n : nodes)
                        nodesSize += n.boundSize();
                return "n=(" + nodes.size() + "," + nodesSize + "), a=" + tabA.size() + ", b=" + tabB.size();
        }
}