Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2007 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: Dec 14, 2007
 */

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

import uk.me.parabola.imgfmt.ReadFailedException;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.CommonHeader;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.Section;
import uk.me.parabola.log.Logger;
import uk.me.parabola.util.EnhancedProperties;

/**
 * @author Steve Ratcliffe
 */

public class TREHeader extends CommonHeader {
        private static final Logger log = Logger.getLogger(TREHeader.class);

        // The tre section comes in different versions with different length
        // headers.  We just refer to them by the header length for lack of any
        // better description.
        public static final int TRE_120 = 120;
        public static final int TRE_184 = 184;
        private static final int TRE_188 = 188;

        // The header length to use when creating a file.
        private static final int DEFAULT_HEADER_LEN = TRE_188;

        // A map has a display priority that determines which map is on top
        // when two maps cover the same area.
        private static final int DEFAULT_DISPLAY_PRIORITY = 0x19;

        static final int MAP_LEVEL_REC_SIZE = 4;
        private static final char POLYLINE_REC_LEN = 2;
        private static final char POLYGON_REC_LEN = 2;
        private static final char POINT_REC_LEN = 3;
        private static final char COPYRIGHT_REC_SIZE = 0x3;
        private static final char EXT_TYPE_OFFSETS_REC_LEN = 13;
        private static final char EXT_TYPE_OVERVIEWS_REC_LEN = 4;
        static final int SUBDIV_REC_SIZE = 14;
        static final int SUBDIV_REC_SIZE2 = 16;
       
        public static final int POI_FLAG_DETAIL = 0x1;
        public static final int POI_FLAG_TRANSPARENT = 0x2;
        public static final int POI_FLAG_STREET_BEFORE_HOUSENUMBER = 0x4;
        public static final int POI_FLAG_POSTALCODE_BEFORE_CITY = 0x8;
        public static final int POI_FLAG_DRIVE_ON_LEFT = 0x20;

        // Bounding box.  All units are in map units.
        private Area area = new Area(0,0,0,0);

        private int mapInfoSize;

        private int mapLevelPos;
        private int mapLevelsSize;

        private int subdivPos;
        private int subdivSize;

        private byte poiDisplayFlags;

        private int displayPriority = DEFAULT_DISPLAY_PRIORITY;

        private final Section copyright = new Section(COPYRIGHT_REC_SIZE);
        private final Section polyline = new Section(POLYLINE_REC_LEN);
        private final Section polygon = new Section(POLYGON_REC_LEN);
        private final Section points = new Section(POINT_REC_LEN);
        private final Section extTypeOffsets = new Section(points, EXT_TYPE_OFFSETS_REC_LEN);
        private final Section extTypeOverviews = new Section(extTypeOffsets, EXT_TYPE_OVERVIEWS_REC_LEN);

        private int numExtTypeAreaTypes;
        private int numExtTypeLineTypes;
        private int numExtTypePointTypes;

        private int mapId;

        private boolean custom;

        public TREHeader() {
                super(DEFAULT_HEADER_LEN, "GARMIN TRE");
        }

        /**
         * Read the rest of the header.  Specific to the given file.  It is guaranteed
         * that the file position will be set to the correct place before this is
         * called.
         *
         * @param reader The header is read from here.
         */

        protected void readFileHeader(ImgFileReader reader) throws ReadFailedException {
                assert reader.position() == COMMON_HEADER_LEN;
                int maxLat = reader.get3();
                int maxLon = reader.get3();
                int minLat = reader.get3();
                int minLon = reader.get3();
                // fix problem with value 0x800000 that is interpreted as a negative value
                if (maxLon <  minLon && maxLon == -8388608 )
                        maxLon = 8388608; // its 180 degrees, not -180
               
                setBounds(new Area(minLat, minLon, maxLat, maxLon));
                log.info("read area is", getBounds());

                // more to do...
                mapLevelPos = reader.getInt();
                mapLevelsSize = reader.getInt();
                subdivPos = reader.getInt();
                subdivSize = reader.getInt();

                copyright.readSectionInfo(reader, true);
                reader.getInt();

                poiDisplayFlags = reader.get();
                displayPriority = reader.get3();
                reader.getInt();
                reader.getChar();
                reader.get();

                polyline.readSectionInfo(reader, true);
                reader.getInt();
                polygon.readSectionInfo(reader, true);
                reader.getInt();
                points.readSectionInfo(reader, true);
                reader.getInt();

                int mapInfoOff = mapLevelPos;
                if (subdivPos < mapInfoOff)
                        mapInfoOff = subdivPos;
                if (copyright.getPosition() < mapInfoOff)
                        mapInfoOff = copyright.getPosition();

                mapInfoSize = mapInfoOff - getHeaderLength();
                if (getHeaderLength() > 116) {
                        reader.position(116);
                        mapId = reader.getInt();
                }
                if (getHeaderLength() > 120) {
                        reader.getInt();
                        extTypeOffsets.readSectionInfo(reader, true);
                }
        }

        /**
         * Write the rest of the header.  It is guaranteed that the writer will be set
         * to the correct position before calling.
         *
         * @param writer The header is written here.
         */

        protected void writeFileHeader(ImgFileWriter writer) {
                writer.put3(area.getMaxLat());
                writer.put3(area.getMaxLong());
                writer.put3(area.getMinLat());
                writer.put3(area.getMinLong());

                writer.putInt(getMapLevelsPos());
                writer.putInt(getMapLevelsSize());

                writer.putInt(getSubdivPos());
                writer.putInt(getSubdivSize());

                copyright.writeSectionInfo(writer);
                writer.putInt(0);

                writer.put(getPoiDisplayFlags());

                writer.put3(displayPriority);
                if (custom)
                        writer.putInt(0x170401);
                else
                        writer.putInt(0x110301);
               
                writer.putChar((char) 1);
                writer.put((byte) 0);

                polyline.writeSectionInfo(writer);
                writer.putInt(0);
                polygon.writeSectionInfo(writer);
                writer.putInt(0);
                points.writeSectionInfo(writer);
                writer.putInt(0);

                // There are a number of versions of the header with increasing lengths
                if (getHeaderLength() > 116)
                        writer.putInt(getMapId());

                if (getHeaderLength() > 120) {
                        writer.putInt(0);

                        // The record size must be zero if the section is empty for compatibility
                        // with cpreview.
                        if (extTypeOffsets.getSize() == 0)
                                extTypeOffsets.setItemSize((char) 0);
                        extTypeOffsets.writeSectionInfo(writer, true);

                        // the second byte value of 6 appears to mean "extended
                        // type info present" - a value of 4 has been seen in some
                        // maps but those maps contain something else in these two
                        // sections and not extended type info - the 7 in the
                        // bottom byte could possibly be a bitmask to say which
                        // types are present (line, area, point) but this is just
                        // conjecture
                        writer.putInt(0x0607);

                        extTypeOverviews.writeSectionInfo(writer);
                        writer.putChar((char)numExtTypeLineTypes);
                        writer.putChar((char)numExtTypeAreaTypes);
                        writer.putChar((char)numExtTypePointTypes);
                }

                if (getHeaderLength() > 154) {
                        MapValues mv = new MapValues(mapId, getHeaderLength());
                        mv.calculate();
                        writer.putInt(mv.value(0));
                        writer.putInt(mv.value(1));
                        writer.putInt(mv.value(2));
                        writer.putInt(mv.value(3));

                        writer.putInt(0);
                        writer.putInt(0);
                        writer.putInt(0);
                        writer.putChar((char) 0);
                        writer.putInt(0);
                }
               
                writer.position(getHeaderLength());
        }

        public void config(EnhancedProperties props) {
                String key = "draw-priority";
                if (props.containsKey(key))
                        setDisplayPriority(props.getProperty(key, 0x19));

                if (props.containsKey("transparent"))
                        poiDisplayFlags |= POI_FLAG_TRANSPARENT;
                custom = props.containsKey("custom");
                       
        }
       
        /**
         * Set the bounds based upon the latitude and longitude in degrees.
         * @param area The area bounded by the map.
         */

        public void setBounds(Area area) {
                this.area = area;
        }

        public Area getBounds() {
                return area;
        }

        public void setMapId(int id) {
                mapId = id;
        }

        public void setDriveOnLeft(boolean dol) {
                if (dol)
                        this.poiDisplayFlags |= POI_FLAG_DRIVE_ON_LEFT;
        }

        public void addPoiDisplayFlags(byte poiDisplayFlags) {
                this.poiDisplayFlags |= poiDisplayFlags;
        }      

        public int getMapInfoSize() {
                return mapInfoSize;
        }

        public void setMapInfoSize(int mapInfoSize) {
                this.mapInfoSize = mapInfoSize;
        }

        public int getMapLevelsPos() {
                return mapLevelPos;
        }

        public void setMapLevelPos(int mapLevelPos) {
                this.mapLevelPos = mapLevelPos;
        }

        public int getMapLevelsSize() {
                return mapLevelsSize;
        }

        public void setMapLevelsSize(int mapLevelsSize) {
                this.mapLevelsSize = mapLevelsSize;
        }

        public int getSubdivPos() {
                return subdivPos;
        }

        public void setSubdivPos(int subdivPos) {
                this.subdivPos = subdivPos;
        }

        public int getSubdivSize() {
                return subdivSize;
        }

        public void setSubdivSize(int subdivSize) {
                this.subdivSize = subdivSize;
        }

        public void setCopyrightPos(int copyrightPos) {
                //this.copyrightPos = copyrightPos;
                copyright.setPosition(copyrightPos);
        }

        public void incCopyrightSize() {
                copyright.inc();
        }

        protected byte getPoiDisplayFlags() {
                return poiDisplayFlags;
        }

        public void setPolylinePos(int polylinePos) {
                polyline.setPosition(polylinePos);
        }

        public void incPolylineSize() {
                polyline.inc();
        }

        public void setPolygonPos(int polygonPos) {
                polygon.setPosition(polygonPos);
        }

        public void incPolygonSize() {
                polygon.inc();
        }

        public void setPointPos(int pointPos) {
                points.setPosition(pointPos);
        }

        public void incPointSize() {
                points.inc();
        }

        public void setExtTypeOffsetsPos(int pos) {
                extTypeOffsets.setPosition(pos);
        }

        public void incExtTypeOffsetsSize() {
                extTypeOffsets.inc();
        }

        public void setExtTypeOverviewsPos(int pos) {
                extTypeOverviews.setPosition(pos);
        }

        public void incExtTypeOverviewsSize() {
                extTypeOverviews.inc();
        }

        public void incNumExtTypeAreaTypes() {
                ++numExtTypeAreaTypes;
        }

        public void incNumExtTypeLineTypes() {
                ++numExtTypeLineTypes;
        }
        public void incNumExtTypePointTypes() {
                ++numExtTypePointTypes;
        }

        public int getMapId() {
                return mapId;
        }

        protected void setDisplayPriority(int displayPriority) {
                this.displayPriority = displayPriority;
        }

        public int getDisplayPriority() {
                return displayPriority;
        }

        public int getExtTypeOffsetsPos() {
                return extTypeOffsets.getPosition();
        }
        public int getExtTypeOffsetsSize() {
                return extTypeOffsets.getSize();
        }
        public int getExtTypeSectionSize() {
                return extTypeOffsets.getItemSize();
        }

        public Section getCopyrightSection() {
                return copyright;
        }
}