// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.turnlanes.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; public final class Route { public static final class Segment { private final Node start; private final Way way; private final Node end; private final List<Node> nodes; Segment(Node start, Way way, Node end) { this.start = start; this.way = way; this.end = end; final List<Node> ns = way.getNodes(); if (way.lastNode().equals(start)) { Collections.reverse(ns); } this.nodes = Collections.unmodifiableList(ns); } public Node getStart() { return start; } public Way getWay() { return way; } public Node getEnd() { return end; } public List<Node> getNodes() { return nodes; } public double getLength() { double length = 0; Node last = nodes.get(0); for (Node n : nodes.subList(1, nodes.size())) { length += last.getCoor().greatCircleDistance(n.getCoor()); last = n; } return length; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((end == null) ? 0 : end.hashCode()); result = prime * result + ((start == null) ? 0 : start.hashCode()); result = prime * result + ((way == null) ? 0 : way.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Segment other = (Segment) obj; if (end == null) { if (other.end != null) return false; } else if (!end.equals(other.end)) return false; if (start == null) { if (other.start != null) return false; } else if (!start.equals(other.start)) return false; if (way == null) { if (other.way != null) return false; } else if (!way.equals(other.way)) return false; return true; } } public static Route load(Relation r) { final Node end = Utils.getMemberNode(r, Constants.LENGTHS_ROLE_END); final List<Way> ws = Utils.getMemberWays(r, Constants.LENGTHS_ROLE_WAYS); return create(ws, end); } public static Route load(Relation left, Relation right, Way w) { left = left == null ? right : left; right = right == null ? left : right; if (left == null) { throw new IllegalArgumentException("At least one relation must not be null."); } final Route leftRoute = load(left); final Route rightRoute = load(right); int iLeft = 0; while (!w.equals(leftRoute.getSegments().get(iLeft++).getWay())) ; int iRight = 0; while (!w.equals(rightRoute.getSegments().get(iRight++).getWay())) ; final int min = Math.min(iLeft, iRight); final List<Segment> leftSegments = leftRoute.getSegments().subList(iLeft - min, iLeft); final List<Segment> rightSegments = rightRoute.getSegments().subList(iRight - min, iRight); if (!leftSegments.equals(rightSegments)) { throw new IllegalArgumentException("Routes are split across different ways."); } return new Route(iLeft == min ? rightSegments : leftSegments); } public static Route create(List<Way> ws, Node end) { final List<Segment> segments = new ArrayList<>(ws.size()); for (Way w : ws) { if (!w.isFirstLastNode(end)) { throw new IllegalArgumentException("Ways must be ordered."); } final Node start = Utils.getOppositeEnd(w, end); segments.add(0, new Segment(start, w, end)); end = start; } return new Route(segments); } private final List<Segment> segments; private Route(List<Segment> segments) { this.segments = Collections.unmodifiableList(new ArrayList<>(segments)); } public List<Segment> getSegments() { return segments; } public List<Node> getNodes() { final List<Node> ns = new ArrayList<>(); ns.add(segments.get(0).getStart()); for (Segment s : segments) { ns.addAll(s.getNodes().subList(1, s.getNodes().size())); } return Collections.unmodifiableList(ns); } public double getLengthFrom(Way w) { double length = Double.NEGATIVE_INFINITY; for (Segment s : getSegments()) { length += s.getLength(); if (w.equals(s.getWay())) { length = 0; } } if (length < 0) { throw new IllegalArgumentException("Way must be part of the route."); } return length; } public double getLength() { double length = 0; for (Segment s : getSegments()) { length += s.getLength(); } return length; } public Node getStart() { return getFirstSegment().getStart(); } public Node getEnd() { return getLastSegment().getEnd(); } public Segment getFirstSegment() { return getSegments().get(0); } public Segment getLastSegment() { return getSegments().get(getSegments().size() - 1); } public Route subRoute(int fromIndex, int toIndex) { return new Route(segments.subList(fromIndex, toIndex)); } public List<Way> getWays() { final List<Way> ws = new ArrayList<>(); for (Segment s : segments) { ws.add(s.getWay()); } return Collections.unmodifiableList(ws); } }