package edu.kit.pse.ws2013.routekit.map; import java.awt.geom.Line2D; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import edu.kit.pse.ws2013.routekit.util.Coordinates; public class ReducedGraphView extends GraphView { int[] startnodes; int[] endnodes; int[] mapping; int edgepos = 0; public ReducedGraphView(Graph g, HighwayType maxType, int zoom) { int maxTypeId = maxType.ordinal(); startnodes = new int[g.getNumberOfEdges()]; endnodes = new int[g.getNumberOfEdges()]; mapping = new int[g.getNumberOfEdges()]; int[] degree = new int[g.getNumberOfNodes()]; int[] outdegree = new int[g.getNumberOfNodes()]; int[] indegree = new int[g.getNumberOfNodes()]; int[] type = new int[g.getNumberOfNodes()]; boolean[] keep = new boolean[g.getNumberOfNodes()]; boolean[] hide = new boolean[g.getNumberOfEdges()]; for (int i = 0; i < g.getNumberOfEdges(); i++) { int correspondingEdge = g.getCorrespondingEdge(i); int t = g.getEdgeProperties(i).getType().ordinal(); if (t > maxTypeId || hide[i]) { hide[i] = true; if (correspondingEdge != -1) { hide[correspondingEdge] = true; } continue; } if (correspondingEdge < i) { // uniqe or not existent int start = g.getStartNode(i); int end = g.getTargetNode(i); if (type[start] != 0 && type[start] != t) { degree[start] += 3;// no! } else { type[start] = t; } if (type[end] != 0 && type[end] != t) { degree[end] += 3;// no! } else { type[end] = t; } outdegree[start]++; indegree[end]++; if (correspondingEdge != -1) { outdegree[end]++; indegree[start]++; } degree[start]++; degree[end]++; } } for (int i = 0; i < keep.length; i++) { if ((degree[i] == 2 && indegree[i] == 2 && outdegree[i] == 2) || degree[i] == 0 || (degree[i] == 2 && indegree[i] == 1 && outdegree[i] == 1)) { } else { keep[i] = true; } } boolean[] done = new boolean[g.getNumberOfEdges()]; Coordinates[] currentWay = new Coordinates[1024 * 4]; int[] currentWayid = new int[currentWay.length]; for (int i = 0; i < keep.length; i++) { if (!keep[i]) { continue; } for (Integer edge : g.getOutgoingEdges(i)) { int wp = 0; if (done[edge] || hide[edge]) { continue; } int target = g.getTargetNode(edge); // done[edge] = true; if (keep[target]) { addEdge(i, target, edge); continue; } currentWayid[wp] = i; currentWay[wp++] = g.getCoordinates(i); currentWayid[wp] = target; currentWay[wp++] = g.getCoordinates(target); int pretarget = i; int tedge = edge; while (!keep[target]) { int found = -1; for (Integer edge2 : g.getOutgoingEdges(target)) { if (g.getTargetNode(edge2) != pretarget && !hide[edge2]) { if (found != -1) { throw new Error(); } found = g.getTargetNode(edge2); tedge = edge2; } } if (found == -1) { throw new Error(); } pretarget = target; target = found; currentWayid[wp] = target; currentWay[wp++] = g.getCoordinates(target); } int rev = g.getCorrespondingEdge(tedge); if (rev != -1) { done[rev] = true; } boolean[] usedWay = new boolean[wp]; douglasPeuker(currentWay, 0, wp, g, usedWay, zoom); int prev = currentWayid[0]; for (int j = 1; j < wp; j++) { if (usedWay[j]) { int k = currentWayid[j]; addEdge(prev, k, edge); prev = k; } } } } int[] out = new int[edgepos]; System.arraycopy(startnodes, 0, out, 0, edgepos); startnodes = out; out = new int[edgepos]; System.arraycopy(endnodes, 0, out, 0, edgepos); endnodes = out; out = new int[edgepos]; System.arraycopy(mapping, 0, out, 0, edgepos); mapping = out; } private ReducedGraphView(Graph graph, int[] mapping, int[] startnodes, int[] endnodes) { this.mapping = mapping; this.startnodes = startnodes; this.endnodes = endnodes; } private void addEdge(int start, int end, int org) { startnodes[edgepos] = start; endnodes[edgepos] = end; mapping[edgepos++] = org; } private void douglasPeuker(Coordinates[] currentWay, int startIdx, int endIdx, Graph g, boolean[] used, int zoom) { Coordinates start = currentWay[startIdx]; Coordinates end = currentWay[endIdx - 1]; double maxdist = -1; int maxidx = -1; for (int j = startIdx + 1; j < endIdx - 1; j++) { Coordinates c = currentWay[j]; double dist = Line2D.ptLineDistSq(start.getSmtX(zoom), start.getSmtY(zoom), end.getSmtX(zoom), end.getSmtY(zoom), c.getSmtX(zoom), c.getSmtY(zoom)); if (dist > maxdist) { maxdist = dist; maxidx = j; } } if (maxdist > 5f / 256f / 256f) {// douglasPeuker(currentWay, startIdx, maxidx, g, used, zoom); used[maxidx] = true; douglasPeuker(currentWay, maxidx, endIdx, g, used, zoom); } else { used[startIdx] = true; used[endIdx - 1] = true; } } @Override public int getNumberOfEdges() { return edgepos; } @Override public int getStartNode(int edge) { return startnodes[edge]; } @Override public int getTargetNode(int edge) { return endnodes[edge]; } @Override public int translate(int edg) { return mapping[edg]; } @Override public void save(File file) throws IOException { try (RandomAccessFile raf = new RandomAccessFile(file, "rw"); FileChannel fc = raf.getChannel()) { MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, (1 + 3 * edgepos) * 4); mbb.order(ByteOrder.BIG_ENDIAN).asIntBuffer().put(edgepos) .put(mapping).put(startnodes).put(endnodes); mbb.force(); } } public static ReducedGraphView load(Graph graph, File file) throws IOException { try (FileInputStream fis = new FileInputStream(file); DataInputStream dis = new DataInputStream(fis); FileChannel fc = fis.getChannel()) { int edgepos = dis.readInt(); MappedByteBuffer mbb = fc .map(MapMode.READ_ONLY, 4, edgepos * 3 * 4); int[] mapping = new int[edgepos]; int[] startnodes = new int[edgepos]; int[] endnodes = new int[edgepos]; mbb.order(ByteOrder.BIG_ENDIAN).asIntBuffer().get(mapping) .get(startnodes).get(endnodes); return new ReducedGraphView(graph, mapping, startnodes, endnodes); } } }