package edu.kit.pse.ws2013.routekit.map; import java.awt.geom.Line2D; import java.util.AbstractSet; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import edu.kit.pse.ws2013.routekit.util.Coordinates; import edu.kit.pse.ws2013.routekit.util.PointOnEdge; /** * A {@link GraphIndex} implementation using a linked tree of nodes with * sub-nodes or content. */ public class TreeGraphIndex implements GraphIndex { private Graph graph; private GraphView view; private static boolean isSmaller(boolean lat, float threshhold, Coordinates from, Coordinates to) { return (lat ? from.getLatitude() : from.getLongitude()) <= threshhold || (lat ? to.getLatitude() : to.getLongitude()) <= threshhold; } private static boolean isGreater(boolean lat, float threshhold, Coordinates from, Coordinates to) { return (lat ? from.getLatitude() : from.getLongitude()) >= threshhold || (lat ? to.getLatitude() : to.getLongitude()) >= threshhold; } class Node { Node left; Node right; float threshhold; Set<Integer> contents; boolean splitLat; public Node(Set<Integer> contents, boolean splitLat) { if (contents.size() < 50) { this.contents = contents; } else { this.splitLat = splitLat; Set<Integer> low = new HashSet<>(); Set<Integer> high = new HashSet<>(); float sum = 0; if (splitLat) { for (Integer integer : contents) { int edge = integer; Coordinates a = graph.getCoordinates(view .getStartNode(edge)); Coordinates b = graph.getCoordinates(view .getTargetNode(edge)); float f1 = a.getLatitude(); float f2 = b.getLatitude(); sum += f1 + f2; } } else { for (Integer integer : contents) { int edge = integer; float f1 = graph .getCoordinates(view.getStartNode(edge)) .getLongitude(); float f2 = graph.getCoordinates( view.getTargetNode(edge)).getLongitude(); sum += f1 + f2; } } sum /= 2 * contents.size(); threshhold = sum; for (Integer integer : contents) { int edge = integer; Coordinates f1 = graph.getCoordinates(view .getStartNode(edge)); Coordinates f2 = graph.getCoordinates(view .getTargetNode(edge)); if (isSmaller(splitLat, sum, f1, f2)) { low.add(edge); } if (isGreater(splitLat, sum, f1, f2)) { high.add(edge); } } left = new Node(low, !splitLat); right = new Node(high, !splitLat); } } public void addAll(Graph g, Coordinates leftTop, Coordinates rightBottom, Set<Integer> target) { if (contents != null) { // TODO is more efficient possible for (Integer edg : contents) { int edge = edg; Coordinates from = g .getCoordinates(view.getStartNode(edge)); Coordinates to = g.getCoordinates(view.getTargetNode(edge)); float minA = Math.min(from.getLatitude(), to.getLatitude()); float minO = Math.min(from.getLongitude(), to.getLongitude()); float maxA = Math.max(from.getLatitude(), to.getLatitude()); float maxO = Math.max(from.getLongitude(), to.getLongitude()); if (minA <= rightBottom.getLatitude() && minO <= rightBottom.getLongitude() && maxA >= leftTop.getLatitude() && maxO >= leftTop.getLongitude()) { target.add(edg); } } } else { if (isSmaller(splitLat, threshhold, leftTop, rightBottom)) { left.addAll(g, leftTop, rightBottom, target); } if (isGreater(splitLat, threshhold, leftTop, rightBottom)) { right.addAll(g, leftTop, rightBottom, target); } } } } /** * Constructs the data structure for the given {@link Graph} and maximum * {@link HighwayType}. * * @param graph * The graph. * @param maxType * The maximum {@link HighwayType} to include. */ public TreeGraphIndex(final Graph graph, HighwayType maxType, final GraphView view) { this.graph = graph; this.view = view; final int numberOfEdges = view.getNumberOfEdges(); final int maxTypeInt = maxType.ordinal(); root = new Node(new AbstractSet<Integer>() { @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { int current = -1; boolean eaten = true; private void nextValid() { if (eaten) { eaten = false; while (++current < numberOfEdges) { EdgeProperties props = graph.getEdgeProperties(view .translate(current)); if (props.getType().ordinal() <= maxTypeInt) { return; } } } } @Override public boolean hasNext() { nextValid(); return current < numberOfEdges; } @Override public Integer next() { nextValid(); eaten = true; return current; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public int size() { return numberOfEdges; } }, false); } Node root; @Override public PointOnEdge findNearestPointOnEdge(Coordinates coords) { final Coordinates left = new Coordinates(coords.getLatitude() - 0.1f, coords.getLongitude() - 0.1f); final Coordinates right = new Coordinates(coords.getLatitude() + 0.1f, coords.getLongitude() + 0.1f); double nearest = Double.POSITIVE_INFINITY; int emax = -1; final double cx = coords.getSmtX(19); final double cy = coords.getSmtY(19); for (Integer e : getEdgesInRectangle(left, right)) { final Coordinates start = graph.getCoordinates(graph .getStartNode(e)); final Coordinates end = graph .getCoordinates(graph.getTargetNode(e)); final double sx = start.getSmtX(19); final double sy = start.getSmtY(19); final double ex = end.getSmtX(19); final double ey = end.getSmtY(19); double l = Line2D.ptSegDistSq(sx, sy, ex, ey, cx, cy); if (l < nearest) { nearest = l; emax = e; } } if (emax == -1) { return null; } final Coordinates start = graph .getCoordinates(graph.getStartNode(emax)); final Coordinates end = graph.getCoordinates(graph.getTargetNode(emax)); final double sx = start.getSmtX(19); final double sy = start.getSmtY(19); final double ex = end.getSmtX(19); final double ey = end.getSmtY(19); double path = (cx - sx) * (ex - sx) + (cy - sy) * (ey - sy); path /= (ex - sx) * (ex - sx) + (ey - sy) * (ey - sy); return new PointOnEdge(emax, path < 0 ? 0 : path > 1 ? 1 : (float) path); } @Override public Set<Integer> getEdgesInRectangle(Coordinates leftTop, Coordinates rightBottom) { Set<Integer> ints = new TreeSet<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { int a = Integer.compare(graph.getEdgeProperties(o1).getType() .ordinal(), graph.getEdgeProperties(o2).getType() .ordinal()); if (a != 0) { return -a; } return o1.compareTo(o2); } }); root.addAll(graph, leftTop, rightBottom, ints); return ints; } public Graph getGraph() { return graph; } @Override public GraphView getView() { return view; } }