/*
* Copyright (C) 2010 - 2012.
*
* 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.reader.osm;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.Exit;
import uk.me.parabola.log.Logger;
import uk.me.parabola.util.EnhancedProperties;
/**
* Operations mostly on highways that have to be performed during reading
* the OSM input file.
*
* Some of this would be much better done in a style file or by extending the style system.
*/
public class HighwayHooks extends OsmReadingHooksAdaptor {
private static final Logger log = Logger.getLogger(HighwayHooks.class);
private final List<Way> motorways = new ArrayList<>();
private final List<Node> exits = new ArrayList<>();
private boolean makeOppositeCycleways;
private ElementSaver saver;
private boolean linkPOIsToWays;
private Node currentNodeInWay;
private final Set<String> usedTags = new HashSet<String>() {
{
add("highway");
add("access");
add("barrier");
add("FIXME");
add("fixme");
add("route");
add("oneway");
add("junction");
add("name");
add(Exit.TAG_ROAD_REF);
add("ref");
// the following two tags are only added if the cycleway options are set
// add("cycleway");
// add("bicycle");
}
};
public boolean init(ElementSaver saver, EnhancedProperties props) {
this.saver = saver;
if(props.getProperty("make-all-cycleways", false)) {
log.error("option make-all-cycleways is deprecated, please use make-opposite-cycleways");
makeOppositeCycleways = true;
}
else {
makeOppositeCycleways = props.getProperty("make-opposite-cycleways", false);
}
linkPOIsToWays = props.getProperty("link-pois-to-ways", false);
currentNodeInWay = null;
if (makeOppositeCycleways) {
// need the additional tags
usedTags.add("cycleway");
usedTags.add("bicycle");
usedTags.add("oneway:bicycle");
usedTags.add("bicycle:oneway");
usedTags.add("cycleway:left");
usedTags.add("cycleway:right");
}
return true;
}
public Set<String> getUsedTags() {
return usedTags;
}
public void onAddNode(Node node) {
String val = node.getTag("highway");
if (val != null && (val.equals("motorway_junction") || val.equals("services"))) {
exits.add(node);
node.addTag("mkgmap:osmid", String.valueOf(node.getId()));
}
}
public void onCoordAddedToWay(Way way, long id, Coord co) {
if (!linkPOIsToWays)
return;
currentNodeInWay = saver.getNode(id);
// if this Coord is also a POI, replace it with an
// equivalent CoordPOI that contains a reference to
// the POI's Node so we can access the POI's tags
if (!(co instanceof CoordPOI) && currentNodeInWay != null) {
// for now, only do this for nodes that have
// certain tags otherwise we will end up creating
// a CoordPOI for every node in the way
final String[] coordPOITags = { "barrier", "highway" };
for (String cpt : coordPOITags) {
if (currentNodeInWay.getTag(cpt) != null) {
// the POI has one of the approved tags so
// replace the Coord with a CoordPOI
CoordPOI cp = new CoordPOI(co);
saver.addPoint(id, cp);
// we also have to jump through hoops to
// make a new version of Node because we
// can't replace the Coord that defines
// its location
Node newNode = new Node(id, cp);
newNode.copyTags(currentNodeInWay);
saver.addNode(newNode);
// tell the CoordPOI what node it's
// associated with
cp.setNode(newNode);
co = cp;
// if original node is in exits, replace it
if (exits.remove(currentNodeInWay))
exits.add(newNode);
currentNodeInWay = newNode;
break;
}
}
}
if (co instanceof CoordPOI) {
// flag this Way as having a CoordPOI so it
// will be processed later
way.addTag("mkgmap:way-has-pois", "true");
if (log.isInfoEnabled())
log.info("Linking POI", currentNodeInWay.toBrowseURL(), "to way at", co.toOSMURL());
}
}
public void onAddWay(Way way) {
String highway = way.getTag("highway");
if (highway != null || "ferry".equals(way.getTag("route"))) {
// if the way is a roundabout but isn't already
// flagged as "oneway", flag it here
if ("roundabout".equals(way.getTag("junction"))) {
if (way.getTag("oneway") == null) {
way.addTag("oneway", "yes");
}
}
if (makeOppositeCycleways && !"cycleway".equals(highway)){
String onewayTag = way.getTag("oneway");
boolean oneway = way.tagIsLikeYes("oneway");
if (!oneway & onewayTag != null && ("-1".equals(onewayTag) || "reverse".equals(onewayTag)))
oneway = true;
if (oneway){
String cycleway = way.getTag("cycleway");
boolean addCycleWay = false;
// we have a oneway street, check if it allows bicycles to travel in opposite direction
if ("no".equals(way.getTag("oneway:bicycle")) || "no".equals(way.getTag("bicycle:oneway"))){
addCycleWay = true;
}
else if (cycleway != null && ("opposite".equals(cycleway) || "opposite_lane".equals(cycleway) || "opposite_track".equals(cycleway))){
addCycleWay = true;
}
else if ("opposite_lane".equals(way.getTag("cycleway:left")) || "opposite_lane".equals(way.getTag("cycleway:right"))){
addCycleWay = true;
}
else if ("opposite_track".equals(way.getTag("cycleway:left")) || "opposite_track".equals(way.getTag("cycleway:right"))){
addCycleWay = true;
}
if (addCycleWay)
way.addTag("mkgmap:make-cycle-way", "yes");
}
}
}
if("motorway".equals(highway) || "trunk".equals(highway))
motorways.add(way);
}
public void end() {
finishExits();
exits.clear();
motorways.clear();
}
private void finishExits() {
for (Node e : exits) {
String refTag = Exit.TAG_ROAD_REF;
if (e.getTag(refTag) == null) {
String exitName = e.getTag("name");
if (exitName == null)
exitName = e.getTag("ref");
String ref = null;
Way motorway = null;
for (Way w : motorways) {
// uses an implicit call of Coord.equals()
if (w.getPoints().contains(e.getLocation())) {
motorway = w;
ref = w.getTag("ref");
if(ref != null)
break;
}
}
if (ref != null) {
log.info("Adding", refTag + "=" + ref, "to exit", exitName);
e.addTag(refTag, ref);
} else if(motorway != null) {
log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag (" + e.getLocation().toOSMURL() + ")");
}
}
}
}
}