/*
* Copyright (C) 2014.
*
* 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 uk.me.parabola.mkgmap.osmstyle;
import java.util.List;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.trergn.MapObject;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.GType;
import uk.me.parabola.mkgmap.reader.osm.TagDict;
import uk.me.parabola.mkgmap.reader.osm.Way;
import static uk.me.parabola.imgfmt.app.net.AccessTagsAndBits.*;
/**
* Class that is used to connect an OSM way with the attributes of GType
* after Style processing.
*
* @author GerdP
*
*/
public class ConvertedWay {
private static final Logger log = Logger.getLogger(ConvertedWay.class);
private final int index;
private final Way way; // with tags after Style processing
private final GType gt;
private byte roadClass; // 0-4
private byte roadSpeed; // 0-7
private byte mkgmapAccess; // bit mask, see ACCESS_TAGS
private final byte routeFlags; // bit mask, see ROUTING_TAGS
private boolean isRoad;
private boolean reversed; // points were reversed
private boolean overlay; // this is a non-routable overlay line that for a road
public ConvertedWay(int index, Way way, GType type) {
this.index = index;
this.way = way;
this.gt = type;
// note that the gt.getType() may not be a routable type when overlays are used
if (type.isRoad() && MapObject.hasExtendedType(gt.getType()) == false) {
this.roadClass = (byte) gt.getRoadClass();
this.roadSpeed = (byte) gt.getRoadSpeed();
recalcRoadClass(way);
recalcRoadSpeed(way);
mkgmapAccess = evalAccessTags(way);
routeFlags = evalRouteTags(way);
isRoad = true;
} else {
roadClass = 0;
roadSpeed = 0;
mkgmapAccess = 0;
routeFlags = 0;
isRoad = false;
}
}
public ConvertedWay(ConvertedWay other, Way way){
this.way = way;
// copy all other attributes
this.index = other.index;
this.gt = other.gt;
this.roadClass = other.roadClass;
this.roadSpeed = other.roadSpeed;
this.mkgmapAccess = other.mkgmapAccess;
this.routeFlags = other.routeFlags;
}
public int getIndex(){
return index;
}
public GType getGType(){
return gt;
}
public Way getWay() {
return way;
}
public byte getAccess(){
return mkgmapAccess;
}
public byte getRoadClass(){
return roadClass;
}
public byte getRoadSpeed(){
return roadSpeed;
}
public byte getRouteFlags(){
return routeFlags;
}
/**
* Recalculates the road class based on the tags
* <ul>
* <li>{@code mkgmap:road-class}</li>
* <li>{@code mkgmap:road-class-min}</li>
* <li>{@code mkgmap:road-class-max}</li>
* </ul>
* The road class is changed if the tags modify its road class.
*
* @param el an element
* @return {@code true} the road class has been changed, else {@code false}
*/
private final static short roadClassTagKey = TagDict.getInstance().xlate("mkgmap:road-class");
public boolean recalcRoadClass(Element el) {
// save the original road class value
byte oldRoadClass = roadClass;
String val = el.getTag(roadClassTagKey);
if (val != null) {
if (val.startsWith("-")) {
roadClass -= Byte.decode(val.substring(1));
} else if (val.startsWith("+")) {
roadClass += Byte.decode(val.substring(1));
} else {
roadClass = Byte.decode(val);
}
}
val = el.getTag("mkgmap:road-class-max");
byte roadClassMax = 4;
if (val != null)
roadClassMax = Byte.decode(val);
val = el.getTag("mkgmap:road-class-min");
byte roadClassMin = 0;
if (val != null)
roadClassMin = Byte.decode(val);
if (roadClass > roadClassMax)
roadClass = roadClassMax;
else if (roadClass < roadClassMin)
roadClass = roadClassMin;
return (roadClass != oldRoadClass);
}
/**
* Recalculates the road speed
* <ul>
* <li>{@code mkgmap:road-speed-class}</li>
* <li>{@code mkgmap:road-speed}</li>
* <li>{@code mkgmap:road-speed-min}</li>
* <li>{@code mkgmap:road-speed-max}</li>
* </ul>
*
* @param el an element
* @return {@code true} the road speed has been changed, else {@code false}
*/
private final static short roadSpeedTagKey = TagDict.getInstance().xlate("mkgmap:road-speed");
private final static short roadSpeedClassTagKey = TagDict.getInstance().xlate("mkgmap:road-speed-class");
public boolean recalcRoadSpeed(Element el) {
// save the original road speed value
byte oldRoadSpeed = roadSpeed;
// check if the road speed is modified
String roadSpeedOverride = el.getTag(roadSpeedClassTagKey);
if (roadSpeedOverride != null) {
try {
byte rs = Byte.decode(roadSpeedOverride);
if (rs >= 0 && rs <= 7) {
// override the road speed class
roadSpeed = rs;
} else {
log.error(el.getDebugName()
+ " road classification mkgmap:road-speed-class="
+ roadSpeedOverride + " must be in [0;7]");
}
} catch (Exception exp) {
log.error(el.getDebugName()
+ " road classification mkgmap:road-speed-class="
+ roadSpeedOverride + " must be in [0;7]");
}
}
// check if the road speed should be modified more
String val = el.getTag(roadSpeedTagKey);
if(val != null) {
if(val.startsWith("-")) {
roadSpeed -= Byte.decode(val.substring(1));
}
else if(val.startsWith("+")) {
roadSpeed += Byte.decode(val.substring(1));
}
else {
roadSpeed = Byte.decode(val);
}
}
val = el.getTag("mkgmap:road-speed-max");
byte roadSpeedMax = 7;
if(val != null)
roadSpeedMax = Byte.decode(val);
val = el.getTag("mkgmap:road-speed-min");
byte roadSpeedMin = 0;
if(val != null)
roadSpeedMin = Byte.decode(val);
if(roadSpeed > roadSpeedMax)
roadSpeed = roadSpeedMax;
else if(roadSpeed < roadSpeedMin)
roadSpeed = roadSpeedMin;
return (oldRoadSpeed != roadSpeed);
}
public List<Coord> getPoints(){
return way.getPoints();
}
public boolean isValid() {
if (way == null)
return false;
if (way.getPoints() == null || way.getPoints().size()<2)
return false;
return true;
}
public String toString(){
return getGType() + " " + getWay().getId() + " " + getWay().toTagString();
}
public boolean isOneway(){
return (routeFlags & R_ONEWAY) != 0;
}
public boolean isRoundabout(){
return (routeFlags & R_ROUNDABOUT) != 0;
}
public boolean isToll(){
return (routeFlags & R_TOLL) != 0;
}
public boolean isUnpaved(){
return (routeFlags & R_UNPAVED) != 0;
}
public boolean isFerry(){
return (routeFlags & R_FERRY) != 0;
}
public boolean isCarpool(){
return (routeFlags & R_CARPOOL) != 0;
}
public boolean isThroughroute(){
return (routeFlags & R_THROUGHROUTE) != 0;
}
public boolean isRoad(){
return isRoad;
}
public boolean isReversed() {
return reversed;
}
public void setReversed(boolean reversed) {
this.reversed = reversed;
}
public void setOverlay(boolean b) {
this.overlay = b;
}
public boolean isOverlay() {
return overlay;
}
}