Subversion Repositories mkgmap

Rev

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


package uk.me.parabola.mkgmap.reader.osm;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashSet;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.CoordNode;
import uk.me.parabola.imgfmt.app.net.RouteRestriction;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.general.MapCollector;

/**
 * Representation of an OSM turn restriction
 *
 * @author Mark Burton
 */

public class RestrictionRelation extends Relation {

    private static final Logger log = Logger.getLogger(RestrictionRelation.class);

    private Way fromWay;
    private Way toWay;
    private Way viaWay;
    private Coord viaCoord;
    private final String restriction;

    private CoordNode fromNode;
    private CoordNode toNode;
    private CoordNode viaNode;
    private final List<CoordNode> otherNodes = new ArrayList<CoordNode>();
        private byte exceptMask;
    private String messagePrefix;

        /**
         * Create an instance based on an existing relation.  We need to do
         * this because the type of the relation is not known until after all
         * its tags are read in.
         * @param other The relation to base this one on.
         */

        public RestrictionRelation(Relation other) {

                setId(other.getId());

                final String browseURL = toBrowseURL();

                messagePrefix = "Turn restriction " + browseURL + " ";

                for (Map.Entry<String, Element> pair : other.getElements()) {
                        String role = pair.getKey();
                        Element el = pair.getValue();
                        addElement(role, el);

                        Coord location = null;

                        if(viaCoord != null)
                                location = viaCoord;
                        else if(fromWay != null && !fromWay.getPoints().isEmpty())
                                location = fromWay.getPoints().get(0);
                        else if(toWay != null && !toWay.getPoints().isEmpty())
                                location = toWay.getPoints().get(0);

                        if(location != null)
                                messagePrefix = "Turn restriction " + browseURL + " (at " + location.toOSMURL() + ") ";

                        if("to".equals(role)) {
                                if(toWay != null) {
                                        log.warn(messagePrefix + "has extra 'to' member " + el.toBrowseURL());
                                }
                                else if(!(el instanceof Way)) {
                                        log.warn(messagePrefix + "'to' member " + el.toBrowseURL() + " is not a way but it should be");
                                }
                                else if(((Way)el).getPoints().isEmpty()) {
                                        log.warn(messagePrefix + "ignoring empty 'to' way " + el.toBrowseURL());
                                }
                                else
                                        toWay = (Way)el;
                        }
                        else if("from".equals(role)) {
                                if(fromWay != null) {
                                        log.warn(messagePrefix + "has extra 'from' member " + el.toBrowseURL());
                                }
                                else if(!(el instanceof Way)) {
                                        log.warn(messagePrefix + "'from' member " + el.toBrowseURL() + " is not a way but it should be");
                                }
                                else if(((Way)el).getPoints().isEmpty()) {
                                        log.warn(messagePrefix + "ignoring empty 'from' way " + el.toBrowseURL());
                                }
                                else
                                        fromWay = (Way)el;
                        }
                        else if("via".equals(role)) {
                                if(viaCoord != null || viaWay != null) {
                                        log.warn(messagePrefix + "has extra 'via' member " + el.toBrowseURL());
                                }
                                else if(el instanceof Node) {
                                        viaCoord = ((Node)el).getLocation();
                                }
                                else if(el instanceof Way) {
                                        viaWay = (Way)el;
                                }
                                else {
                                        log.warn(messagePrefix + "'via' member " + el.toBrowseURL() + " is not a node or way");
                                }
                        }
                        else if("location_hint".equals(role)) {
                                // relax - we don't care about this
                        }
                        else {
                                log.warn(messagePrefix + "unknown member role '" + role + "'");
                        }
                }

                setName(other.getName());

                copyTags(other);

                restriction = getTag("restriction");

                // These tags are not loaded by default but if they exist issue a warning
                String[] unsupportedTags = {
                    "day_on",
                    "day_off",
                    "hour_on",
                    "hour_off" };
                for (String unsupportedTag : unsupportedTags) {
                        if (getTag(unsupportedTag) != null) {
                                log.warn(messagePrefix + "ignoring unsupported '" + unsupportedTag + "' tag");
                        }
                }

                String except = getTag("except");
                if(except == null)
                        except = getTag("exception"); // be nice
                if(except != null) {
                        for(String e : except.split("[,;]")) { // be nice
                                e = e.trim();
                                if(e.equals("motorcar") || e.equals("motorcycle"))
                                        exceptMask |= RouteRestriction.EXCEPT_CAR;
                                else if(e.equals("psv") || e.equals("bus"))
                                        exceptMask |= RouteRestriction.EXCEPT_BUS;
                                else if(e.equals("taxi"))
                                        exceptMask |= RouteRestriction.EXCEPT_TAXI;
                                else if(e.equals("delivery") || e.equals("goods"))
                                        exceptMask |= RouteRestriction.EXCEPT_DELIVERY;
                                else if(e.equals("bicycle"))
                                        exceptMask |= RouteRestriction.EXCEPT_BICYCLE;
                                else if(e.equals("hgv") || e.equals("truck"))
                                        exceptMask |= RouteRestriction.EXCEPT_TRUCK;
                                else
                                        log.warn(messagePrefix + "ignoring unsupported vehicle class '" + e + "' in turn restriction exception");
                        }
                }
        }

        public Way getFromWay() {
                return fromWay;
        }

        public Way getToWay() {
                return toWay;
        }

        public Coord getViaCoord() {
                return viaCoord;
        }

        public void setFromNode(CoordNode fromNode) {
                this.fromNode = fromNode;
                log.debug(messagePrefix + restriction + " 'from' node is " + fromNode.toOSMURL());
        }

        public void setToNode(CoordNode toNode) {
                this.toNode = toNode;
                log.debug(messagePrefix + restriction + " 'to' node is " + toNode.toOSMURL());
        }

        public void setViaNode(CoordNode viaNode) {
                if(this.viaNode == null)
                        log.debug(messagePrefix + restriction + " 'via' node is " + viaNode.toOSMURL());
                else if(!this.viaNode.equals(viaNode))
                        log.warn(messagePrefix + restriction + " 'via' node redefined from " +
                                         this.viaNode.toOSMURL() + " to " + viaNode.toOSMURL());
                this.viaNode = viaNode;
        }

        public void addOtherNode(CoordNode otherNode) {
                otherNodes.add(otherNode);
                log.debug(messagePrefix + restriction + " adding 'other' node " + otherNode.toOSMURL());
        }

        public boolean isValid() {
                boolean result = true;

                if(restriction == null) {
                        log.warn(messagePrefix + "lacks 'restriction' tag (e.g. no_left_turn)");
                        result = false;
                }

                if(fromWay == null) {
                        log.warn(messagePrefix + "lacks 'from' way");
                }

                if(toWay == null) {
                        log.warn(messagePrefix + "lacks 'to' way");
                }

                if(fromWay == null || toWay == null)
                        return false;

                if(viaCoord == null && viaWay == null) {
                        List<Coord>fromPoints = fromWay.getPoints();
                        List<Coord>toPoints = toWay.getPoints();
                        for(Coord fp : fromPoints) {
                                for(Coord tp : toPoints) {
                                        if(fp.equals(tp)) {
                                                if(viaCoord == null) {
                                                        viaCoord = fp;
                                                }
                                                else {
                                                        log.warn(messagePrefix + "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways connect in more than one place");
                                                        return false;
                                                }
                                        }
                                }
                        }

                        if(viaCoord == null) {
                                log.warn(messagePrefix + "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways don't connect");
                                return false;
                        }

                        log.warn(messagePrefix + "lacks 'via' node (guessing it should be at " + viaCoord.toOSMURL() + ", why don't you add it to the OSM data?)");
                }

                Coord v1 = viaCoord;
                Coord v2 = null;

                if(viaWay != null) {
                        v1 = viaWay.getPoints().get(0);
                        v2 = viaWay.getPoints().get(viaWay.getPoints().size() - 1);
                }

                Coord e1 = fromWay.getPoints().get(0);
                Coord e2 = fromWay.getPoints().get(fromWay.getPoints().size() - 1);
                if(!e1.equals(v1) && !e2.equals(v1) &&
                   !e1.equals(v2) && !e2.equals(v2)) {
                        log.warn(messagePrefix + "'from' way " + fromWay.toBrowseURL() + " doesn't start or end at 'via' node or way");
                        result = false;
                }

                e1 = toWay.getPoints().get(0);
                e2 = toWay.getPoints().get(toWay.getPoints().size() - 1);
                if(!e1.equals(v1) && !e2.equals(v1) &&
                   !e1.equals(v2) && !e2.equals(v2)) {
                        log.warn(messagePrefix + "'to' way " + toWay.toBrowseURL() + " doesn't start or end at 'via' node or way");
                        result = false;
                }

                if (result && viaWay != null) {
                        log.warn(messagePrefix + "sorry, 'via' ways are not supported - ignoring restriction");
                        result = false;
                }

                return result;
        }

        public void addRestriction(MapCollector collector) {

                if(restriction == null || viaNode == null || fromNode == null || toNode == null) {
                        // restriction must have some error (reported earlier)
                        return;
                }

                if(restriction.equals("no_left_turn") ||
                   restriction.equals("no_right_turn") ||
                   restriction.equals("no_straight_on") ||
                   restriction.equals("no_u_turn") ||
                   restriction.startsWith("no_turn")) {
                        collector.addRestriction(fromNode, toNode, viaNode, exceptMask);
                        if(restriction.startsWith("no_turn"))
                                log.warn(messagePrefix + "has bad type '" + restriction + "' it should be of the form no_X_turn rather than no_turn_X - I added the restriction anyway - blocks routing to way " + toWay.toBrowseURL());
                        else
                                log.info(messagePrefix + restriction + " added - blocks routing to way " + toWay.toBrowseURL());
                }
                else if(restriction.equals("only_left_turn") ||
                                restriction.equals("only_right_turn") ||
                                restriction.startsWith("only_straight") ||
                                restriction.startsWith("only_turn")) {
                        if(restriction.startsWith("only_turn"))
                                log.warn(messagePrefix + "has bad type '" + restriction + "' it should be of the form only_X_turn rather than only_turn_X - I added the restriction anyway - allows routing to way " + toWay.toBrowseURL());
                        log.info(messagePrefix + restriction + " added - allows routing to way " + toWay.toBrowseURL());
                        HashSet<CoordNode> otherNodesUnique = new HashSet<CoordNode>(otherNodes);      
                        for(CoordNode otherNode : otherNodesUnique) {
                                if (!otherNode.equals(fromNode) && !otherNode.equals(toNode)) {
                                        log.info(messagePrefix + restriction + "  blocks routing to node " + otherNode.toOSMURL());
                                        collector.addRestriction(fromNode, otherNode, viaNode, exceptMask);
                                }
                        }
                }
                else {
                        log.warn(messagePrefix + "has unsupported type '" + restriction + "'");
                }
        }

        /** Process the members in this relation.
         */

        public void processElements() {
                // relax
        }

        public String toString() {
                return "[restriction = " + restriction + ", from = " + fromWay.toBrowseURL() + ", to = " + toWay.toBrowseURL() + ", via = " + viaCoord.toOSMURL() + "]";
        }
}