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