Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (c) 2009.
 *
 * 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.
 *
 * Created: 13 Sep 2009
 * By: steve
 */


package uk.me.parabola.mkgmap.reader.osm;

import java.util.Formatter;

import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.MapPoint;
import uk.me.parabola.mkgmap.general.MapShape;

/**
 * Holds the garmin type of an element and all the information that
 * will be needed to represent it on the map.  So we have a range of
 * resolutions at which it will be present.
 */

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

        private final FeatureKind featureKind;
        private final int type;

        private int minResolution = 24;
        private int maxResolution = 24;

        private int maxLevel = -1;
        private int minLevel;

        private String defaultName;

        // road class and speed will be set on roads.
        private int roadClass;
        private int roadSpeed;

        private boolean hasRoadAttribute;
        private boolean levelsWereFixed = false;

        /** If this is set, then we look for further types after this one is matched */
        private boolean continueSearch;

        // by default, a rule's actions are skipped when searching for
        // further rules to match - by setting this true, the rule's
        // actions will always be executed
        private boolean propogateActionsOnContinue;

        @SuppressWarnings("incomplete-switch")
        public static boolean checkType(FeatureKind featureKind, int type) {
                if (type >= 0x010000) {
                        if ((type & 0xff) > 0x1f)
                                return false;
                } else {
                        switch (featureKind) {
                        case POLYLINE:
                                if (type > 0x3f)
                                        return false;
                                break;
                        case POLYGON:
                                if (type > 0x7f || type == 0x4a)
                                        return false;
                                break;
                        case POINT:
                                if (type < 0x0100)
                                        return false;
                                int subtype = type & 0xff;
                                if (MapPoint.isCityType(type)) {
                                        if (subtype != 0)
                                                return false;
                                } else if (type >= 0x1600 && type < 0x1e00 || // Andrzej Popowski says.
                                                   type >= 0x2a00 && type < 0x3100 || // These may be indexed and this
                                                   type >= 0x6400 && type < 0x6700) { // confines subtype to 5 bits
                                        if (subtype > 0x1f)
                                                return false;
                                } else {
                                        if (subtype > 0x3f)
                                                return false;
                                }
                                break;
                        }
                }
                return true;
        }
       
        public GType(FeatureKind featureKind, String type) {
                this.featureKind = featureKind;
                try {
                        int t = Integer.decode(type);
                        if (featureKind == FeatureKind.POLYGON && t >= 0x100 && t < 0x10000 && (t & 0xff) == 0) {
                                // allow 0xYY00 instead of 0xYY
                                t >>= 8;
                        }
                        this.type = t;
                } catch (NumberFormatException e) {
                        log.error("not numeric " + type);
                        throw new ExitException("non-numeric type in style file");
                }
        }

        public FeatureKind getFeatureKind() {
                return featureKind;
        }

        public int getType() {
                return type;
        }

        public int getMinResolution() {
                return minResolution;
        }

        public void setMinResolution(int minResolution) {
                this.minResolution = minResolution;
        }

        public int getMaxResolution() {
                return maxResolution;
        }

        public void setMaxResolution(int maxResolution) {
                this.maxResolution = maxResolution;
        }

        public String getDefaultName() {
                return defaultName;
        }

        public void setDefaultName(String defaultName) {
                this.defaultName = defaultName;
        }

        /**
         * Set minLevel and maxLevel based on the resolution values set and
         * the given levels info.  We do this because we used to work only
         * on resolution, but we want to move more towards working with
         * levels.
         */

        public void fixLevels(LevelInfo[] levels) {
                for (LevelInfo info : levels) {
                        if (info.getBits() <= minResolution)
                                maxLevel = info.getLevel();
                        if (info.getBits() <= maxResolution)
                                minLevel = info.getLevel();
                }
                levelsWereFixed = true;
        }

        public String toString() {
                StringBuilder sb = new StringBuilder();
                Formatter fmt = new Formatter(sb);
                sb.append('[');
                fmt.format("%#x", type);
                if (maxLevel == -1) {
                        if (maxResolution == 24)
                                fmt.format(" resolution %d", minResolution);
                        else
                                fmt.format(" resolution %d-%d", maxResolution, minResolution);
                } else {
                        if (minLevel == 0)
                                fmt.format(" level %d", maxLevel);
                        else
                                fmt.format(" level %d-%d", minLevel, maxLevel);
                }
                if (hasRoadAttribute)
                        fmt.format(" road_class=%d road_speed=%d", roadClass, roadSpeed);
               
                if (continueSearch)
                        sb.append(" continue");
                if (propogateActionsOnContinue)
                        sb.append(" propagate");
                sb.append(']');
                String res = sb.toString();
                fmt.close();
                return res;
        }

        public int getMinLevel() {
                return minLevel;
        }

        public int getMaxLevel() {
                return maxLevel;
        }

        public int getRoadClass() {
                return roadClass;
        }

        public void setRoadClass(int roadClass) {
                // road class might also be set for nodes used by the link-pois-to-ways option
                if (getFeatureKind() == FeatureKind.POLYLINE)
                        hasRoadAttribute = true;
                this.roadClass = roadClass;
        }

        public int getRoadSpeed() {
                return roadSpeed;
        }

        public void setRoadSpeed(int roadSpeed) {
                // road speed might also be set for nodes used by the link-pois-to-ways option
                if (getFeatureKind() == FeatureKind.POLYLINE)
                        hasRoadAttribute = true;
                this.roadSpeed = roadSpeed;
        }

        public boolean hasRoadAttribute() {
                return hasRoadAttribute;
        }

        /**
         * @return true if the object has valid attributes to be used as a routable way
         */

        public boolean isRoad() {
                if (!levelsWereFixed)
                        log.error("internal: isRoad() called before fixLevels()");
                return hasRoadAttribute && minLevel == 0;
        }

        public boolean isContinueSearch() {
                return continueSearch;
        }

        public void propagateActions(boolean propagate) {
                propogateActionsOnContinue = propagate;
        }

        public boolean isPropogateActions() {
                return !continueSearch || propogateActionsOnContinue;
        }

        public void setContinueSearch(boolean continueSearch) {
                this.continueSearch = continueSearch;
        }
       
        /**
         *
         * @param type the type value
         * @return true if the type can be used for routable lines
         */

        public static boolean isRoutableLineType(int type){
                return type >= 0x01 && type <= 0x3f;
        }
        /**
         *  
         * @param type the type value
         * @return true if the type is known as routable in Garmin maps. These are
         * known to cause routing errors if used for non-routable lines.
         */

        public static boolean isSpecialRoutableLineType(int type){
                return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1a || type == 0x1b || type >= 0x2c && type <= 0x2f;
        }
       
        /**
         * Return a type value in the commonly used hex format
         * @param type the integer value
         * @return a hex string with even number of digits
         */

        public static String formatType(int type){
                String s = String.format("%x", type);
                return (s.length() % 2 != 0 ? "0x0":"0x") + s;
        }

        public static boolean isContourLine(MapLine line) {
                return line.getType() >= 0x20 && line.getType() <= 0x25 && !(line instanceof MapShape);
        }
       
}