Subversion Repositories mkgmap

Rev

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

/*
 * 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 static final long CYCLEWAY_ID_OFFSET = 0x10000000;

        private final List<Way> motorways = new ArrayList<Way>();
        private final List<Node> exits = new ArrayList<Node>();

        private boolean makeOppositeCycleways;
        private boolean makeCycleways;
        private String frigRoundabouts;
        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)) {
                        makeOppositeCycleways = makeCycleways = true;
                }
                else {
                        makeOppositeCycleways = props.getProperty("make-opposite-cycleways", false);
                        makeCycleways = props.getProperty("make-cycleways", false);
                }
                frigRoundabouts = props.getProperty("frig-roundabouts");
                linkPOIsToWays = props.getProperty("link-pois-to-ways", false);
                currentNodeInWay = null;

                if (makeCycleways || makeOppositeCycleways) {
                        // need the additional two tags
                        usedTags.add("cycleway");
                        usedTags.add("bicycle");
                }
               
                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) {
                currentNodeInWay = saver.getNode(id);

                if (linkPOIsToWays) {
                        // 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 = { "access", "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.getLatitude(), co.getLongitude());
                                                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");
                                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"))) {
                        boolean oneway = way.isBoolTag("oneway");

                        // 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 (way.getTag("mkgmap:frig_roundabout") == null) {
                                        if(frigRoundabouts != null)
                                                way.addTag("mkgmap:frig_roundabout", frigRoundabouts);
                                }
                        }

                        String cycleway = way.getTag("cycleway");
                        if (makeOppositeCycleways && cycleway != null && !"cycleway".equals(highway) && oneway &&
                           ("opposite".equals(cycleway) ||
                                "opposite_lane".equals(cycleway) ||
                                "opposite_track".equals(cycleway)))
                        {
                                // what we have here is a oneway street
                                // that allows bicycle traffic in both
                                // directions -- to enable bicycle routing
                                // in the reverse direction, we synthesise
                                // a cycleway that has the same points as
                                // the original way
                                Way cycleWay = makeCycleWay(way);
                                cycleWay.addTag("oneway", "no");

                        } else if (makeCycleways && cycleway != null && !"cycleway".equals(highway) &&
                                        ("track".equals(cycleway) ||
                                         "lane".equals(cycleway) ||
                                         "both".equals(cycleway) ||
                                         "left".equals(cycleway) ||
                                         "right".equals(cycleway)))
                        {
                                // what we have here is a highway with a
                                // separate track for cycles -- to enable
                                // bicycle routing, we synthesise a cycleway
                                // that has the same points as the original
                                // way
                                makeCycleWay(way);
                                if (way.getTag("bicycle") == null)
                                        way.addTag("bicycle", "no");
                        }
                }

                if("motorway".equals(highway) || "trunk".equals(highway))
                        motorways.add(way);
        }

        /**
         * Construct a cycleway that has the same points as an existing way.  Used for separate
         * cycle lanes.
         * @param way The original way.
         * @return The new way, which will have the same points and have suitable cycle tags.
         */

        private Way makeCycleWay(Way way) {
                long cycleWayId = way.getId() + CYCLEWAY_ID_OFFSET;
                Way cycleWay = new Way(cycleWayId);
                saver.addWay(cycleWay);

                // this reverses the direction of the way but
                // that isn't really necessary as the cycleway
                // isn't tagged as oneway
                List<Coord> points = way.getPoints();
                //for (int i = points.size() - 1; i >= 0; --i)
                //      cycleWay.addPoint(points.get(i));
                for (Coord point : points)
                        cycleWay.addPoint(point);
               
                cycleWay.copyTags(way);

                String name = way.getTag("name");
                if(name != null)
                        name += " (cycleway)";
                else
                        name = "cycleway";
                cycleWay.addTag("name", name);
                cycleWay.addTag("access", "no");
                cycleWay.addTag("bicycle", "yes");
                cycleWay.addTag("foot", "no");
                cycleWay.addTag("mkgmap:synthesised", "yes");

                return cycleWay;
        }

        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) {
                                        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() + ")");
                                }
                        }
                }
        }
}