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     * @param way way
051     * @param first first node
052     * @param second second node
053     * @return way segment
054     * @throws IllegalArgumentException if the node pair is not part of way
055     */
056    public static WaySegment forNodePair(Way way, Node first, Node second) {
057        int endIndex = way.getNodesCount() - 1;
058        while (endIndex > 0) {
059            final int indexOfFirst = way.getNodes().subList(0, endIndex).lastIndexOf(first);
060            if (second.equals(way.getNode(indexOfFirst + 1))) {
061                return new WaySegment(way, indexOfFirst);
062            }
063            endIndex--;
064        }
065        throw new IllegalArgumentException("Node pair is not part of way!");
066    }
067
068    /**
069     * Returns this way segment as complete way.
070     * @return the way segment as {@code Way}
071     */
072    public Way toWay() {
073        Way w = new Way();
074        w.addNode(getFirstNode());
075        w.addNode(getSecondNode());
076        return w;
077    }
078
079    @Override
080    public boolean equals(Object o) {
081        return o instanceof WaySegment
082            && ((WaySegment) o).way == way
083            && ((WaySegment) o).lowerIndex == lowerIndex;
084    }
085
086    @Override
087    public int hashCode() {
088        return way.hashCode() ^ lowerIndex;
089    }
090
091    @Override
092    public int compareTo(WaySegment o) {
093        return equals(o) ? 0 : toWay().compareTo(o.toWay());
094    }
095
096    /**
097     * Checks whether this segment crosses other segment
098     *
099     * @param s2 The other segment
100     * @return true if both segments crosses
101     */
102    public boolean intersects(WaySegment s2) {
103        if (getFirstNode().equals(s2.getFirstNode()) || getSecondNode().equals(s2.getSecondNode()) ||
104                getFirstNode().equals(s2.getSecondNode()) || getSecondNode().equals(s2.getFirstNode()))
105            return false;
106
107        return Line2D.linesIntersect(
108                getFirstNode().getEastNorth().east(), getFirstNode().getEastNorth().north(),
109                getSecondNode().getEastNorth().east(), getSecondNode().getEastNorth().north(),
110                s2.getFirstNode().getEastNorth().east(), s2.getFirstNode().getEastNorth().north(),
111                s2.getSecondNode().getEastNorth().east(), s2.getSecondNode().getEastNorth().north());
112    }
113
114    @Override
115    public String toString() {
116        return "WaySegment [way=" + way.getUniqueId() + ", lowerIndex=" + lowerIndex + ']';
117    }
118}