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.ArrayList;

import java.util.List;

import uk.me.parabola.imgfmt.app.ImgFileWriter;
import static uk.me.parabola.imgfmt.app.net.AccessTagsAndBits.*;


/**
 * A restriction in the routing graph.
 *
 * A routing restriction has two or more arcs.
 * The first arc is the "from" arc, the last is the "to" arc,
 *  and other arc is a "via" arc.
 *
 * A from-to-via restriction says you can't go along arc "to"
 * if you came to node to.getSource() == from.getSource()
 * via the inverse arc of "from". We're using the inverse of
 * "from" since that has the information we need for writing
 * the Table C entry.
 *
 * @author Robert Vollmert
 */

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

        // first three bytes of the header -- might specify the type of restriction
        // and when it is active
        private static final byte RESTRICTION_TYPE = 0x05; // 0x07 spotted, meaning?

        // To specify that a node is given by a relative offset instead
        // of an entry to Table B.
        private static final int F_INTERNAL = 0x8000;

        // the arcs
        private final List<RouteArc> arcs;

        private final RouteNode viaNode;
        // offset in Table C
        private byte offsetSize;
        private int offsetC;

        // last restriction in a node
        private boolean last;

        // mask that specifies which vehicle types the restriction doesn't apply to
        private final byte exceptMask;
        private final byte flags; // meaning of bits 0x01 and 0x10 are not clear

        private final static byte F_EXCEPT_FOOT      = 0x02;
        private final static byte F_EXCEPT_EMERGENCY = 0x04;
        private final static byte F_MORE_EXCEPTIONS  = 0x08;
       
        private final static byte EXCEPT_CAR      = 0x01;
        private final static byte EXCEPT_BUS      = 0x02;
        private final static byte EXCEPT_TAXI     = 0x04;
        private final static byte EXCEPT_DELIVERY = 0x10;
        private final static byte EXCEPT_BICYCLE  = 0x20;
        private final static byte EXCEPT_TRUCK    = 0x40;
       
        /**
         *
         * @param viaNode the node to which this restriction is related
         * @param traffArcs the arcs that describe the "forbidden" path
         * @param mkgmapExceptMask the exception mask in the mkgmap format
         */

        public RouteRestriction(RouteNode viaNode, List<RouteArc> traffArcs, byte mkgmapExceptMask) {
                this.viaNode = viaNode;
                this.arcs = new ArrayList<>(traffArcs);
                for (int i = 0; i < arcs.size(); i++){
                        RouteArc arc = arcs.get(i);
                        assert arc.getDest() != viaNode;
                }
                byte flags = 0;
               
                if ((mkgmapExceptMask & FOOT) != 0)
                        flags |= F_EXCEPT_FOOT;
                if ((mkgmapExceptMask & EMERGENCY) != 0)
                        flags |= F_EXCEPT_EMERGENCY;
               
                exceptMask = translateExceptMask(mkgmapExceptMask);
                if(exceptMask != 0)
                        flags |= F_MORE_EXCEPTIONS;

                int numArcs = arcs.size();
                assert numArcs < 8;
                flags |= ((numArcs) << 5);
                this.flags = flags;
        }

       
        /**
         * Translate the mkgmap internal representation of vehicles to the one used in the img format
         * @param mkgmapExceptMask
         * @return
         */

        private byte translateExceptMask(byte mkgmapExceptMask) {
                byte mask = 0;
                if ((mkgmapExceptMask & CAR) != 0)
                        mask |= EXCEPT_CAR;
                if ((mkgmapExceptMask & BUS) != 0)
                        mask |= EXCEPT_BUS;
                if ((mkgmapExceptMask & TAXI) != 0)
                        mask |= EXCEPT_TAXI;
                if ((mkgmapExceptMask & DELIVERY) != 0)
                        mask |= EXCEPT_DELIVERY;
                if ((mkgmapExceptMask & BIKE) != 0)
                        mask |= EXCEPT_BICYCLE;
                if ((mkgmapExceptMask & TRUCK) != 0)
                        mask |= EXCEPT_TRUCK;
                return mask;
        }


        private int calcOffset(RouteNode node, int tableOffset) {
                int offset = tableOffset - node.getOffsetNod1();
                assert offset >= 0 : "node behind start of tables";
                assert offset < 0x8000 : "node offset too large";
                return offset | F_INTERNAL;
        }

        public List<RouteArc> getArcs(){
                return arcs;
        }
       
        /**
         * Writes a Table C entry with 3 or more nodes.
         *
         * @param writer The writer.
         * @param tableOffset The offset in NOD 1 of the tables area.
         *
         */

        public void write(ImgFileWriter writer, int tableOffset) {
                writer.put(RESTRICTION_TYPE);

                writer.put(flags);
                writer.put((byte)0); // meaning ?

                if(exceptMask != 0)
                        writer.put(exceptMask);

                int numArcs = arcs.size();
                int[] offsets = new int[numArcs+1];
                int pos = 0;
                boolean viaWritten = false;
                for (int i = 0; i < numArcs; i++){
                        RouteArc arc = arcs.get(i);
                        // the arcs must have a specific order and direction
                        // first arc: dest is from node , last arc: dest is to node
                        // if there only two arcs, both will have the via node as source node.
                        // For more n via nodes, the order is like this:
                        // from <- via(1) <- via(2) <- ... <- this via node -> via( n-1) -> via(n) -> to
                        if (arc.isInternal())
                                offsets[pos++] = calcOffset(arc.getDest(), tableOffset);
                        else
                                offsets[pos++] = arc.getIndexB();
                        if (arc.getSource() == viaNode){
                                // there will be two nodes with source node = viaNode, but we write the source only once
                                if (!viaWritten){
                                        offsets[pos++] = calcOffset(viaNode, tableOffset);
                                        viaWritten = true;
                                }
                        }
                }

                for (int offset : offsets)
                        writer.putChar((char) offset);

                for (RouteArc arc: arcs)
                        writer.put(arc.getIndexA());
        }

        /**
         * Write this restriction's offset within Table C into a node record.
         */

        public void writeOffset(ImgFileWriter writer) {
                assert 0 < offsetSize && offsetSize <= 2 : "illegal offset size";
                int offset = offsetC;
                if (offsetSize == 1) {
                        assert offset < 0x80;
                        if (last)
                                offset |= 0x80;
                        writer.put((byte) offset);
                } else {
                        assert offset < 0x8000;
                        if (last)
                                offset |= 0x8000;
                        writer.putChar((char) offset);
                }
        }

        /**
         * Size in bytes of the Table C entry.
         */

        public int getSize() {
                int size = 3; // header length
                if(exceptMask != 0)
                        ++size;
                size += arcs.size() + (arcs.size()+1) * 2;
                return size;
        }

        public void setOffsetC(int offsetC) {
                this.offsetC = offsetC;
        }

        public int getOffsetC() {
                return offsetC;
        }

        public void setOffsetSize(byte size) {
                offsetSize = size;
        }

        public void setLast() {
                last = true;
        }
}