Subversion Repositories mkgmap

Rev

Rev 3339 | 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;

/**
 * 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;

        public static boolean checkType(FeatureKind featureKind, int type) {
                if (type >= 0x010000){
                        if ((type & 0xff) > 0x1f)
                                return false;
                } else {
                        if (featureKind == FeatureKind.POLYLINE && type > 0x3f)
                                return false;
                        else if (featureKind == FeatureKind.POLYGON && (type> 0x7f || type == 0x4a))
                                return false;
                        else if (featureKind == FeatureKind.POINT){
                                if (type < 0x0100 || (type & 0x00ff) > 0x1f)
                                        return false;
                        }
                }
                return true;
        }
       
        public GType(FeatureKind featureKind, String type) {
                this.featureKind = featureKind;
                try {
                        int t = Integer.decode(type);
                        if (featureKind == FeatureKind.POLYGON){
                                // allow 0xYY00 instead of 0xYY
                                if (t >= 0x100 && t < 0x10000 && (t & 0xff) == 0)
                                        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)
                        fmt.format(" continue");
                if (propogateActionsOnContinue)
                        fmt.format(" 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 == 0x1b;
        }
       
        /**
         * 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;
        }
       
}