001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import java.awt.geom.Line2D;
005
006/**
007 * A segment consisting of 2 consecutive nodes out of a way.
008 */
009public final class WaySegment implements Comparable<WaySegment> {
010
011    /**
012     * The way.
013     */
014    public Way way;
015
016    /**
017     * The index of one of the 2 nodes in the way.  The other node has the
018     * index <code>lowerIndex + 1</code>.
019     */
020    public int lowerIndex;
021
022    /**
023     * Constructs a new {@code WaySegment}.
024     * @param w The way
025     * @param i The node lower index
026     */
027    public WaySegment(Way w, int i) {
028        way = w;
029        lowerIndex = i;
030    }
031
032    /**
033     * Returns the first node of the way segment.
034     * @return the first node
035     */
036    public Node getFirstNode() {
037        return way.getNode(lowerIndex);
038    }
039
040    /**
041     * Returns the second (last) node of the way segment.
042     * @return the second node
043     */
044    public Node getSecondNode() {
045        return way.getNode(lowerIndex + 1);
046    }
047
048    /**
049     * Determines and returns the way segment for the given way and node pair.
050     * @throws IllegalArgumentException if the node pair is not part of way
051     */
052    public static WaySegment forNodePair(Way way, Node first, Node second) {
053        int endIndex = way.getNodesCount() - 1;
054        while (endIndex > 0) {
055            final int indexOfFirst = way.getNodes().subList(0, endIndex).lastIndexOf(first);
056            if (second.equals(way.getNode(indexOfFirst + 1))) {
057                return new WaySegment(way, indexOfFirst);
058            }
059            endIndex--;
060        }
061        throw new IllegalArgumentException("Node pair is not part of way!");
062    }
063
064    /**
065     * Returns this way segment as complete way.
066     * @return the way segment as {@code Way}
067     */
068    public Way toWay() {
069        Way w = new Way();
070        w.addNode(getFirstNode());
071        w.addNode(getSecondNode());
072        return w;
073    }
074
075    @Override
076    public boolean equals(Object o) {
077        return o instanceof WaySegment
078            && ((WaySegment) o).way == way
079            && ((WaySegment) o).lowerIndex == lowerIndex;
080    }
081
082    @Override
083    public int hashCode() {
084        return way.hashCode() ^ lowerIndex;
085    }
086
087    @Override
088    public int compareTo(WaySegment o) {
089        return equals(o) ? 0 : toWay().compareTo(o.toWay());
090    }
091
092    /**
093     * Checks whether this segment crosses other segment
094     *
095     * @param s2 The other segment
096     * @return true if both segments crosses
097     */
098    public boolean intersects(WaySegment s2) {
099        if (getFirstNode().equals(s2.getFirstNode()) || getSecondNode().equals(s2.getSecondNode()) ||
100                getFirstNode().equals(s2.getSecondNode()) || getSecondNode().equals(s2.getFirstNode()))
101            return false;
102
103        return Line2D.linesIntersect(
104                getFirstNode().getEastNorth().east(), getFirstNode().getEastNorth().north(),
105                getSecondNode().getEastNorth().east(), getSecondNode().getEastNorth().north(),
106                s2.getFirstNode().getEastNorth().east(), s2.getFirstNode().getEastNorth().north(),
107                s2.getSecondNode().getEastNorth().east(), s2.getSecondNode().getEastNorth().north());
108    }
109
110    @Override
111    public String toString() {
112        return "WaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
113    }
114}