Subversion Repositories display

Rev

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

/*
 * Copyright (C) 2014 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 3 or
 * 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.
 */

package test.files;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.ImgFileReader;

import test.display.check.Log;

import static test.util.AccessBits.*;
import static test.util.RestrictionBits.*;

/**
 * The tables in the NOD1 section.
 *
 * @author Steve Ratcliffe
 */

public class RouteCenter {
        private Coord coord;
        private final long offset;
        private final int ptrShift;
        private final int nodesStart;
        private long next;   // The next node starts at this offset.

        private final List<TableA> tableA = new ArrayList<>();
        private final int tableARecordLen;
        private final List<TableB> tableB = new ArrayList<>();
    private final TableC tableC = new TableC();

    private final Map<Integer, RouteNode> nodes = new LinkedHashMap<>();
    private int index;

        public static void setHasExtraZeros(Boolean hasExtraZeros) {
                RouteCenter.hasExtraZeros = hasExtraZeros;
        }

        private static Boolean hasExtraZeros;

        public RouteCenter(long start, int tableARecordLen, int ptrShift, int nodesStart) {
            this.tableARecordLen = tableARecordLen;
        offset = start;
                this.ptrShift = ptrShift;
                this.nodesStart = nodesStart;
        }

    public void readTable(ImgFileReader r, long start) {
        r.position(nodesStart + start);
        readTables(r);
    }

    public long getOffset() {
        return offset;
    }

    public long getNext() {
        return next;
    }

    public int getCPointerSize() {
        return tableC.getPointerSize();
    }

    public Coord getCoord() {
        return coord;
    }
    public int getLat() {
        return coord.getLatitude();
    }
    public int getLon() {
        return coord.getLongitude();
    }

    /**
     * Read in all the node table information for a route center.
     *
     * Each route center has a location, a table of pointers to NET, a table of pointers to
     * NOD1 (for links to other route centers) and a table of route restrictions.
     *
     * @param r The img file reader, which should be positioned at the start of the tables.
     *          At the end it will be just after, ready to read the next node.
     */

    private void readTables(ImgFileReader r) {
        int restrformat = r.get1u();

        int lon = r.get3s();
        int lat = r.get3s();
        coord = new Coord(lat, lon);

        int nTabA = r.get1u();
        int nTabB = r.get1u();

        // Now do 'Table A' (segments)
        for (int i = 0; i < nTabA; i++) {
            TableA a = readTableARecord(r);
            tableA.add(a);
        }

        // 'Table B' (inter-section pointers)
        for (int i = 0; i < nTabB; i++) {
            TableB b = new TableB();
            b.setNodOffset(r.get3u() << ptrShift);
            tableB.add(b);
        }

        // 'Table C' (restrictions)
        int size = 0;
        if ((restrformat & 1) != 0) {
            size = r.get1u();
        } else if ((restrformat & 2) != 0) {
            size = r.get2u();
        }

                tableC.setPtrSize(size > 127? 2: 1);
                //byte[] raw = r.get(size);
                //tableC.setRaw(raw);
                readTableC(r, size, restrformat);

        if ((restrformat & 4) != 0) tableC.setRoundabout(r.get());

        if ((restrformat & 8) != 0) tableC.setUnpaved(r.get());

        if ((restrformat & 0x10) != 0) tableC.setFerry(r.get());

            if ((restrformat & 0x03) == 0) {
                    // Sometimes there is an extra zero here. Eg mkgmap currently adds one.
                    // If there is no zero then we know that that isn't the case for this file.
                    // If it is however, the it could be the start of a node with a very short section.
                    // So we can't really tell, but if we found a node at this position from NOD 2,
                    // then we know that there definitely is not.
                    long pos = r.position();
                    byte b = r.get();
                    r.position(pos);

                    // Is there a node at pos? If so then there are not extra bytes.

                    // Since the file either has this or not, then remember this the first time. It will
                    // then always be right or always wrong.  Initialise hasExtraZeros at the top if
                    // looking at a file where this heuristic does not work.
                    if (hasExtraZeros == null) {
                            if (b == 0) {
                                    System.out.println("setting extra zero option");
                                    hasExtraZeros = true;
                            } else {
                                    hasExtraZeros = false;
                            }
                    }
                    if (hasExtraZeros) {
                            r.get();
                    }
            }

        next = r.position();
    }

        /**
         * Read the restriction in table C.
         * @param r The file reader.
         * @param size The size of the Table C section.
         * @param format The restriction format flags.
         */

        private void readTableC(ImgFileReader r, int size, int format) {
                int start = (int) r.position();
                int end = (int) (r.position() + size - (format & 0x3));

                while (r.position() < end) {
                        Restriction restriction = new Restriction((int) r.position() - start);

                        // read the type
                        int restrType = r.get1u();

                        if ((restrType & 0xf) != 5) {
                                Log.error("Unknown restriction type %x", restrType);
                                continue;
                        }

                        int flags = r.get1u();
                        restriction.setFlags(flags);
                        int numItems = (flags >>> 5) & 0xff;
                        boolean extra = (flags & RESTR_EXTRA) != 0;

                        r.get(); // unknown, usually 0

                        if ((flags & MORE_EXCEPTIONS) != 0) {
                                //0x01: car; 0x02: bus; 0x04: taxi; 0x10: delivery; 0x20: bicycle; 0x40: truck
                                int vehicleExceptions = r.get1u();
                                restriction.setVehicleExceptions(vehicleExceptions);
                        }

                        // The list of nodes that are involved
                        for (int i = 0; i < numItems + 1; i++) {
                                int off = r.get2u();
                                int nodeOffset;
                                if ((off & 0x8000) == 0) {
                                        int tabB = off & 0x00ff;
                                        nodeOffset = tableB.get(tabB).getNodOffset();
                                } else {
                                        nodeOffset = (int) offset - (off & 0x7fff);
                                }

                                restriction.addNodeOffset(nodeOffset);
                        }

                        // The list of roads that are involved.
                        for (int i = 0; i < numItems; i++) {
                                int ri = r.get1u();

                                int roadOffset = tableA.get(ri).getNetOffset();
                                restriction.addRoadOffset(roadOffset);
                        }

                        if (extra) {
                                r.get();

                                int b = r.get1u();
                                if (b == 0x88) {
                                        //d.byteValue("??");
                                        r.get();
                                } else if (b == 0x8c) {
                                        r.get2u();
                                } else if (b == 0x8d) {
                                        //d.intValue(3, "??");
                                        //d.byteValue("??");
                                        r.get();
                                        byte f1 = r.get();
                                        if ((f1 & 0x80) == 0) {
                                                r.get();
                                        } else {
                                                r.get2u();
                                        }

                                } else if (b == 0x08 || b == 0x0c) {
                                        r.get(5);
                                        //} else if ( b == 0x0c) {
                                        //      int val = d.intValue("??");
                                        //      //d.charValue("??");
                                        //      while ((val & 0x100000) == 0) {
                                        //              d.charValue("??");
                                        //              val = d.intValue("??");
                                        //      }
                                }
                        }

                        tableC.add(restriction);
                }
        }

        private TableA readTableARecord(ImgFileReader r) {
                TableA a = new TableA();

                // Get the NET pointer.
                int off = r.get3u();

                // top 2 bits of net pointer are access bits
                int access = off & 0xc0_00_00;

                off &= 0x3fffff;
                a.setNetOffset(off);

                int paramA = r.get1u();
                int paramB = 0;
                if (tableARecordLen >= 5) {
                        paramB = r.get1u();
                }

                a.setParam(paramA);
                a.setClass((paramA & CLASS) >> 4);
                a.setSpeed((paramA & SPEED));
                a.setAccess(access | paramB);
                return a;
        }

    public void addNode(RouteNode node) {
        nodes.put(node.getOffset(), node);
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public int getIndex() {
        return index;
    }

    public Map<Integer, RouteNode> nodes() {
        return nodes;
    }

    public int linkId(int idx) {
        if (idx < 0 || idx >= tableB.size())
            return -1;

        TableB b = tableB.get(idx);
        return b.getNodOffset();
    }

    public TableA getNet(int idx) {
        if (idx < 0 || idx >= tableA.size())
            return null;
        TableA a = tableA.get(idx);
        return a;
    }

        public List<TableA> getTableA() {
                return tableA;
        }

        public List<TableB> getTableB() {
                return tableB;
        }

        public List<Restriction> getRestrictions() {
                return tableC.getRestrictions();
        }

        public Restriction getRestriction(int val) {
                return tableC.getRestriction(val);
        }

        /**
     * A table A record contains a link to NET1 and access restriction information.
     */

        public class TableA {
        private int netOffset;
        private int roadClass;
        private int speed;
        private int access;
                private int param;

                public void setNetOffset(int netOffset) {
            this.netOffset = netOffset;
        }

        public int getNetOffset() {
            return netOffset;
        }

        public void setClass(int aClass) {
            this.roadClass = aClass;
        }

        public int getRoadClass() {
            return roadClass;
        }

                public void setSpeed(int speed) {
            this.speed = speed;
        }

        public int getSpeed() {
            return speed;
        }

        public void setAccess(int access) {
            this.access = access;
        }

        public int getAccess() {
            return access;
        }

        public String noAccessString() {
            StringBuilder sb = new StringBuilder();

            if ((access & NOEMERGENCY) != 0) sb.append("emergency,");
            if ((access & NODELIVERY) != 0) sb.append("delivery,");
            if ((access & NOCAR) != 0) sb.append("car,");
            if ((access & NOBUS) != 0) sb.append("bus,");
            if ((access & NOTAXI) != 0) sb.append("taxi,");
            if ((access & NOCARPOOL) != 0) sb.append("carpool,");
            if ((access & NOFOOT) != 0) sb.append("foot,");
            if ((access & NOBIKE) != 0) sb.append("bike,");
            if ((access & NOTRUCK) != 0) sb.append("truck,");

            // Trim final comma, if there is anything.
            if (sb.length() > 1)
                sb.setLength(sb.length() - 1);

            return sb.toString();
        }

                public void setParam(int param) {
                        this.param = param;
                }
                public boolean isOneway() {
                        return (this.param & 0x8) != 0;
                }
        }

    /**
     * Table B consists of pointers into NOD1.  Used for jumps between different route centres.
     */

    public class TableB {
        private int nodOffset;

        public void setNodOffset(int nodOffset) {
            this.nodOffset = nodOffset;
        }

        public int getNodOffset() {
            return nodOffset;
        }
    }

        public class Restriction {
                private final int offset;
                private int restrType;
                private int flags;
                private int vehicleExceptions;

                private List<Integer> nodeOffsets = new ArrayList<>();
                private List<Integer> roadOffsets = new ArrayList<>();

                public Restriction(int offset) {
                        this.offset = offset;
                }

                public int getOffset() {
                        return offset;
                }

                public void setRestrType(int restrType) {
                        this.restrType = restrType;
                }

                void addNodeOffset(int off) {
                        nodeOffsets.add(off);
                }

                void addRoadOffset(int off) {
                        roadOffsets.add(off);
                }

                public void setFlags(int flags) {
                        this.flags = flags;
                }

                public int getFlags() {
                        return flags;
                }

                public void setVehicleExceptions(int vehicleExceptions) {
                        this.vehicleExceptions = vehicleExceptions;
                }

                public int getVehicleExceptions() {
                        return vehicleExceptions;
                }

                public List<Integer> getNodeOffsets() {
                        return nodeOffsets;
                }

                public List<Integer> getRoadOffsets() {
                        return roadOffsets;
                }
        }

        public class TableC {
                // The raw value of table C (restrictions)
                private byte roundabout;
                private byte unpaved;
                private byte ferry;
                private int ptrSize;

                Map<Integer, Restriction> restrictions = new LinkedHashMap<>();

                public void add(Restriction rstr) {
                        restrictions.put(rstr.getOffset(), rstr);
                }

                public List<Restriction> getRestrictions() {
                        return new ArrayList<>(restrictions.values());
                }

                public int getPointerSize() {
                        return ptrSize;
                }

                public void setPtrSize(int ptrSize) {
                        this.ptrSize = ptrSize;
                }

                public byte getRoundabout() {
                        return roundabout;
                }

                public void setRoundabout(byte roundabout) {
                        this.roundabout = roundabout;
                }

                public byte getUnpaved() {
                        return unpaved;
                }

                public void setUnpaved(byte unpaved) {
                        this.unpaved = unpaved;
                }

                public byte getFerry() {
                        return ferry;
                }

                public void setFerry(byte ferry) {
                        this.ferry = ferry;
                }

                public Restriction getRestriction(int n) {
                        return restrictions.get(n);
                }
        }
}