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