// 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);
}
}