Subversion Repositories display

Rev

Rev 542 | 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 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.Collections;
import java.util.List;

import uk.me.parabola.imgfmt.app.BitReader;
import uk.me.parabola.imgfmt.app.BufferedImgFileReader;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.CoordNode;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import uk.me.parabola.imgfmt.app.ImgReader;
import uk.me.parabola.imgfmt.app.Label;
import uk.me.parabola.imgfmt.app.lbl.LBLFileReader;
import uk.me.parabola.imgfmt.app.lbl.POIRecord;
import uk.me.parabola.imgfmt.app.trergn.RGNHeader;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.util.EnhancedProperties;

import test.elements.Line;
import test.elements.Point;
import test.elements.Polygon;

/**
 * Read in the RGN file.  This is independent of RgnFileReader in mkgmap
 * since we want to read more and do extra checking. Plus having an
 * independent implementation for testing is good.
 *
 * @author Steve Ratcliffe
 */

public class RgnFile extends ImgReader {
        private static final int FLAG_NETINFO = 0x800000;
        private static final int FLAG_EXTRA = 0x400000;
        private static final int MASK_LABEL = 0x3fffff;

        private final RGNHeader rgnHeader;
        private LBLFileReader lblFile;
        private NetFile netFile;

        public RgnFile(ImgChannel chan) {
                rgnHeader = new RGNHeader();
                setHeader(rgnHeader);

                setReader(new BufferedImgFileReader(chan));
                rgnHeader.readHeader(getReader());
        }

        public void config(EnhancedProperties props) {
                //config = props;
        }

        /**
         * Get a list of all points for the given subdivision.  This includes
         * both the indexed points section and the points section.
         *
         * The numbering of the points carries through the sections.
         * @param sd The subdivision that we are interested in.
         * @return A list of all points for the subdiv.
         */

        public List<Point> pointsForSubdiv(Subdivision sd) {
                if (!sd.hasIndPoints() && !sd.hasPoints())
                        return Collections.emptyList();

                RgnOffsets rgnOffsets = getOffsets(sd);
                ArrayList<Point> list = new ArrayList<>();

                // Even though the indexed points are after the points, the numbering
                // starts with 1 for the first indexed point and carries on into the
                // points section.
                fetchPointsCommon(sd, rgnOffsets.getIndPointStart(), rgnOffsets.getIndPointEnd(), list);
                fetchPointsCommon(sd, rgnOffsets.getPointStart(), rgnOffsets.getPointEnd(), list);
                return list;
        }

        /**
         * The indexed points and the points sections are both read just the same.
         */

        private void fetchPointsCommon(Subdivision sd, long start, long end, List<Point> points) {
                position(start);
                ImgFileReader reader = getReader();

                int number = points.size() + 1;
                while (position() < end) {
                        Point p = new Point(sd);

                        byte t = reader.get();
                        int val = reader.get3u();
                        boolean hasSubtype = false;
                        if ((val & 0x800000) != 0)
                                hasSubtype = true;

                        boolean hasPoi = false;
                        if ((val & 0x400000) != 0)
                                hasPoi = true;

                        Label l;
                        int labelOffset = val & 0x3fffff;
                        if (hasPoi) {
                                POIRecord record = lblFile.fetchPoi(labelOffset);
                                if (record != null) {
                                        l = record.getNameLabel();
                                        p.setPOIRecord(record);
                                } else
                                        l = lblFile.fetchLabel(0);
                        } else {
                                l = lblFile.fetchLabel(labelOffset);
                        }
                        p.setLabel(l);

                        p.setDeltaLong((short)reader.get2u());
                        p.setDeltaLat((short)reader.get2u());

                        if (hasSubtype) {
                                byte st = reader.get();
                                p.setType(((t & 0xff) << 8) | (st & 0xff));
                                //p.setHasSubtype(true);
                        } else {
                                p.setType((t & 0xff) << 8);
                        }

                        p.setNumber(number++);
                        points.add(p);
                }
        }

        public List<Line> linesForSubdiv(Subdivision div) {
                if (!div.hasPolylines())
                        return Collections.emptyList();

                RgnOffsets rgnOffsets = getOffsets(div);
                ArrayList<Line> list = new ArrayList<>();

                int start = rgnOffsets.getLineStart();
                int end = rgnOffsets.getLineEnd();

                position(start);
                int count = 1;
                while (position() < end) {
                        Line line = new Line(div);
                        line.setLineNumber(count++);

                        if (readLineCommon(div, line))
                                break;

                        list.add(line);
                }

                return list;
        }

        public List<Polygon> shapesForSubdiv(Subdivision div) {
                if (!div.hasPolygons())
                        return Collections.emptyList();

                RgnOffsets rgnOffsets = getOffsets(div);
                ArrayList<Polygon> list = new ArrayList<>();

                int start = rgnOffsets.getPolygonStart();
                int end = rgnOffsets.getPolygonEnd();

                position(start);
                int count = 1;
                while (position() < end) {
                        Polygon shape = new Polygon(div);
                        shape.setLineNumber(++count);

                        if (readLineCommon(div, shape))
                                break;

                        list.add(shape);
                }

                return list;
        }

        private boolean readLineCommon(Subdivision div, Line line) {
                ImgFileReader reader = getReader();

                byte type = reader.get();
                line.setType(type & 0x3f);

                int labelOffset = reader.get3u();
                boolean extra = (labelOffset & FLAG_EXTRA) != 0;
                line.setNodeFlags(extra);
                Label label;
                if ((labelOffset & FLAG_NETINFO) == 0) {
                        label = lblFile.fetchLabel(labelOffset & MASK_LABEL);
                } else {
                        int netoff = labelOffset & 0x3fffff;
                        line.setNetOffset(netoff);

                        RoadData road = netFile.getRoad(netoff);
                        label = road.getLabel(0);
                }
                line.setLabel(label);

                short dlon = (short) reader.get2u();
                line.setDeltaLong(dlon);

                short dlat = (short) reader.get2u();
                line.setDeltaLat(dlat);

                int currLon = fixDelta(div, div.getLongitude(), dlon);
                int currLat = fixDelta(div, div.getLatitude(), dlat);

                int len;
                if ((type & 0x80) == 0)
                        len = reader.get1u();
                else
                        len = reader.get2u();

                int base = reader.get1u();

                int xbase = 2;
                int n = base & 0xf;
                if (n <= 9)
                        xbase += n;
                else
                        xbase += (2 * n) - 9;

                n = (base >>> 4) & 0xf;
                int ybase = 2;
                if (n <= 9)
                        ybase += n;
                else
                        ybase += (2 * n) - 9;

                if (len == 0) {
                        System.out.println("Line with no data");
                        return true;
                }

                byte[] bytes = reader.get(len);
                BitReader br = new BitReader(bytes);

                boolean xneg = false;
                boolean xsame = br.get1();
                if (xsame) {
                        xneg = br.get1();
                } else
                        xbase++;

                boolean ysame = br.get1();
                boolean yneg = false;
                if (ysame) {
                        yneg = br.get1();
                } else
                        ybase++;

                if (extra) {
                        boolean firstextra = br.get1();
                        // This is always false in our maps, but presumably serves a purpose.
                        if (firstextra)
                                System.out.println("First extra bit is true");
                }

                Coord co;

                if (extra)
                        co = new CoordNode(currLat, currLon, 1, false, false);
                else
                        co = new Coord(currLat, currLon);
                line.addCoord(co);

                Coord lastCoord = co;
                int bitPerPoint = 8 * len - ((extra ? 1 : 0) + xbase + ybase);
                while (br.getBitPosition() <= bitPerPoint) {
                        int dx;
                        if (xsame) {
                                dx = br.get(xbase);
                                if (xneg)
                                        dx = -dx;
                        } else {
                                dx = br.sget2(xbase);
                        }

                        int dy;
                        if (ysame) {
                                dy = br.get(ybase);
                                if (yneg)
                                        dy = -dy;
                        } else {
                                dy = br.sget2(ybase);
                        }

                        currLat += dy << div.getShift();
                        currLon += dx << div.getShift();

                        boolean isnode = false;
                        if (extra) {
                                isnode = br.get1();
                                //fmt.format(", node=%b", isnode);
                        }

                        if (isnode) {
                                co = new CoordNode(currLat, currLon, 1, false, false); // XXX boundary and id
                        } else {
                                co = new Coord(currLat, currLon);
                        }

                        if ((dx != 0 || dy != 0) || lastCoord.getId() != 0 || isnode || line.getCoords().size() < 2)
                                line.addCoord(co);
                        lastCoord = co;
                }
                return false;
        }

        private int fixDelta(Subdivision subdiv, int base, short dlat) {
                return base + (dlat << subdiv.getShift());
        }

        /**
         * Get the offsets to the points, lines etc in RGN for the given subdiv.
         * @param sd The subdivision is needed to work out the starting points.
         * @return An Offsets class that allows you to obtain the offsets.
         */

        private RgnOffsets getOffsets(Subdivision sd) {
                int off = sd.getStartRgnPointer();
                position(rgnHeader.getDataOffset() + off);

                return new RgnOffsets(sd);
        }

        public void setLblFile(LBLFileReader lblFile) {
                this.lblFile = lblFile;
        }

        public void setNetFile(NetFile netFile) {
                this.netFile = netFile;
        }

        /**
         * Class to hold the start and end points of point, lines etc within
         * the area for a given subdivision in the RGN data.
         */

        private class RgnOffsets {
                private final int pointOffset;
                private int pointEnd;

                private int indPointOffset;
                private int indPointEnd;

                private int lineOffset;
                private int lineEnd;

                private int polygonOffset;
                private int polygonEnd;

                private final int start;
                private int headerLen;

                /**
                 * Calculate the offsets for the given subdivision.
                 * After this is called the position will be set after any pointers that
                 * exist at the beginning of the area.
                 *
                 * @param sd The subdivision.
                 */

                private RgnOffsets(Subdivision sd) {
                        ImgFileReader reader = getReader();

                        start = (int) position();

                        pointOffset = 0;

                        if (sd.needsIndPointPtr()) {
                                indPointOffset = reader.get2u();
                                headerLen += 2;
                        }

                        if (sd.needsPolylinePtr()) {
                                lineOffset = reader.get2u();
                                headerLen += 2;
                        }

                        if (sd.needsPolygonPtr()) {
                                polygonOffset = reader.get2u();
                                headerLen += 2;
                        }


                        if (sd.hasPoints()) {
                                if (sd.hasIndPoints())
                                        pointEnd = indPointOffset;
                                else if (sd.hasPolylines())
                                        pointEnd = lineOffset;
                                else if (sd.hasPolygons())
                                        pointEnd = polygonOffset;
                                else
                                        pointEnd = sd.getEndRgnPointer() - sd.getStartRgnPointer();
                        }
                        if (sd.hasIndPoints()) {
                                if (sd.hasPolylines())
                                        indPointEnd = lineOffset;
                                else if (sd.hasPolygons())
                                        indPointEnd = polygonOffset;
                                else
                                        indPointEnd = sd.getEndRgnPointer() - sd.getStartRgnPointer();
                        }
                        if (sd.hasPolylines()) {
                                if (sd.hasPolygons())
                                        lineEnd = polygonOffset;
                                else
                                        lineEnd = sd.getEndRgnPointer() - sd.getStartRgnPointer();
                        }
                        if (sd.hasPolygons()) {
                                polygonEnd = sd.getEndRgnPointer() - sd.getStartRgnPointer();
                        }
                }

                public String toString() {
                        return String.format("rgn div offsets: %x-%x/%x-%x/%x-%x/%x-%x",
                                        pointOffset, pointEnd, indPointOffset, indPointEnd,
                                        lineOffset, lineEnd, polygonOffset, polygonEnd);
                }

                public long getPointStart() {
                        return pointOffset == 0 ? start + headerLen : start + pointOffset;
                }

                public long getPointEnd() {
                        return start + pointEnd;
                }

                public long getIndPointStart() {
                        return indPointOffset == 0 ? start + headerLen : start + indPointOffset;
                }

                public long getIndPointEnd() {
                        return start + indPointEnd;
                }

                public int getLineStart() {
                        return lineOffset == 0? start + headerLen: start + lineOffset;
                }

                public int getLineEnd() {
                        return start + lineEnd;
                }

                public int getPolygonStart() {
                        return polygonOffset == 0? start + headerLen: start + polygonOffset;
                }
                public int getPolygonEnd() {
                        return start + polygonEnd;
                }
        }

}