001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.relation.sort;
003
004import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.NONE;
005import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.ROUNDABOUT_LEFT;
006import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.ROUNDABOUT_RIGHT;
007
008import org.openstreetmap.josm.data.coor.EastNorth;
009import org.openstreetmap.josm.data.osm.INode;
010import org.openstreetmap.josm.data.osm.IRelationMember;
011import org.openstreetmap.josm.data.osm.IWay;
012import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction;
013
014/**
015 * Utility classes for the {@link RelationSorter}.
016 */
017final class RelationSortUtils {
018
019    private RelationSortUtils() {
020        // Hide default constructor for utils classes
021    }
022
023    /**
024     * determine, if the way i is a roundabout and if yes, what type of roundabout
025     * @param member relation member
026     * @return roundabout type
027     * @since 17862 (generics)
028     */
029    static Direction roundaboutType(IRelationMember<?> member) {
030        if (member == null || !member.isWay()) return NONE;
031        return roundaboutType((IWay<?>) member.getWay());
032    }
033
034    /**
035     * Check if a way is a roundabout type
036     * @param w The way to check
037     * @param <W> The way type
038     * @return The roundabout type
039     * @since 17862 (generics)
040     */
041    static <W extends IWay<?>> Direction roundaboutType(W w) {
042        if (w != null && w.hasTag("junction", "circular", "roundabout")) {
043            int nodesCount = w.getNodesCount();
044            if (nodesCount > 2 && nodesCount < 200) {
045                INode n1 = w.getNode(0);
046                INode n2 = w.getNode(1);
047                INode n3 = w.getNode(2);
048                if (n1 != null && n2 != null && n3 != null && w.isClosed()) {
049                    /** do some simple determinant / cross product test on the first 3 nodes
050                        to see, if the roundabout goes clock wise or ccw */
051                    EastNorth en1 = n1.getEastNorth();
052                    EastNorth en2 = n2.getEastNorth();
053                    EastNorth en3 = n3.getEastNorth();
054                    if (en1 != null && en2 != null && en3 != null) {
055                        en1 = en2.subtract(en1);
056                        en2 = en3.subtract(en2);
057                        return en1.north() * en2.east() - en2.north() * en1.east() > 0 ? ROUNDABOUT_LEFT : ROUNDABOUT_RIGHT;
058                    }
059                }
060            }
061        }
062        return NONE;
063    }
064
065    static boolean isBackward(final IRelationMember<?> member) {
066        return "backward".equals(member.getRole());
067    }
068
069    static boolean isForward(final IRelationMember<?> member) {
070        return "forward".equals(member.getRole());
071    }
072
073    static boolean isOneway(final IRelationMember<?> member) {
074        return isForward(member) || isBackward(member);
075    }
076}