package de.fub.agg2graph.agg.strategy; import de.fub.agg2graph.agg.AggConnection; import de.fub.agg2graph.agg.AggContainer; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.agg.IMergeHandler; import de.fub.agg2graph.agg.PointGhostPointPair; import de.fub.agg2graph.graph.RamerDouglasPeuckerFilter; import de.fub.agg2graph.input.Globals; import de.fub.agg2graph.structs.ClassObjectEditor; import de.fub.agg2graph.structs.GPSCalc; import de.fub.agg2graph.structs.GPSEdge; import de.fub.agg2graph.structs.GPSPoint; import de.fub.agg2graph.structs.ILocation; import de.fub.agg2graph.structs.frechet.FrechetDistance; import de.fub.agg2graph.ui.gui.RenderingOptions; import de.fub.agg2graph.ui.gui.jmv.Layer; import de.fub.agg2graph.ui.gui.jmv.TestUI; import java.awt.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; public class FrechetBasedMerge implements IMergeHandler { private static final Logger logger = Logger .getLogger("agg2graph.agg.default.merge"); // contains only matched points/nodes private List<AggNode> aggNodes = null; private List<GPSPoint> gpsPoints = null; private int maxLookahead = 10; private double minContinuationAngle = 45; // helper stuff private AggNode inNode; private AggNode outNode; private AggContainer aggContainer; private RenderingOptions roMatchGPS; // cleaning stuff private final RamerDouglasPeuckerFilter rdpf = new RamerDouglasPeuckerFilter(0, 125); // private static AggCleaner cleaner = new AggCleaner().enableDefault(); private double maxPointGhostDist = 12.5; // meters private double distance = 12.5; @SuppressWarnings("unused") private AggNode beforeNode; public HashSet<GPSEdge> criticalEdges = new HashSet<GPSEdge>(); HashMap<AggNode, TreeSet<AggNode>> AtoT = new HashMap<AggNode, TreeSet<AggNode>>(); public FrechetBasedMerge() { // debugging logger.setLevel(Level.ALL); roMatchGPS = new RenderingOptions(); roMatchGPS.setColor(Color.PINK); logger.setLevel(Level.OFF); aggNodes = new ArrayList<AggNode>(); gpsPoints = new ArrayList<GPSPoint>(); } public FrechetBasedMerge(AggContainer aggContainer) { this(); this.aggContainer = aggContainer; } public FrechetBasedMerge(AggContainer aggContainer, List<AggNode> aggNodes, List<GPSPoint> gpsPoints) { this.aggNodes = aggNodes; this.gpsPoints = gpsPoints; } /** * @return the maxLookahead */ public int getMaxLookahead() { return maxLookahead; } /** * @param maxLookahead the maxLookahead to set */ public void setMaxLookahead(int maxLookahead) { this.maxLookahead = maxLookahead; } /** * @return the minContinuationAngle */ public double getMinContinuationAngle() { return minContinuationAngle; } /** * @param minContinuationAngle the minContinuationAngle to set */ public void setMinContinuationAngle(double minContinuationAngle) { this.minContinuationAngle = minContinuationAngle; } /** * @return the maxPointGhostDist */ public double getMaxPointGhostDist() { return maxPointGhostDist; } /** * @param maxPointGhostDist the maxPointGhostDist to set */ public void setMaxPointGhostDist(double maxPointGhostDist) { this.maxPointGhostDist = maxPointGhostDist; } @Override public AggContainer getAggContainer() { return aggContainer; } @Override public void setAggContainer(AggContainer aggContainer) { this.aggContainer = aggContainer; } @Override public List<AggNode> getAggNodes() { return this.aggNodes; } @Override public void addAggNode(AggNode aggNode) { if (this.aggNodes.size() > 0 && this.aggNodes.get(this.aggNodes.size() - 1).equals(aggNode)) { this.aggNodes.remove(this.aggNodes.size() - 1); } this.aggNodes.add(aggNode); } @Override public void addAggNodes(List<AggNode> aggNodes) { int i = 0; while (aggNodes.size() > i && this.aggNodes.size() > 0 && this.aggNodes.get(this.aggNodes.size() - 1).equals( aggNodes.get(i))) { this.aggNodes.remove(this.aggNodes.size() - 1); i++; } this.aggNodes.addAll(aggNodes); } @Override public List<GPSPoint> getGpsPoints() { return gpsPoints; } @Override public void addGPSPoints(List<GPSPoint> gpsPoints) { int i = 0; while (gpsPoints.size() > i && this.gpsPoints.size() > 0 && this.gpsPoints.get(this.gpsPoints.size() - 1).equals( gpsPoints.get(i))) { this.gpsPoints.remove(this.gpsPoints.size() - 1); i++; } this.gpsPoints.addAll(gpsPoints); } @Override public void addGPSPoint(GPSPoint gpsPoint) { if (this.gpsPoints.size() > 0 && this.gpsPoints.get(this.gpsPoints.size() - 1).equals( gpsPoint)) { return; } this.gpsPoints.add(gpsPoint); } @Override public void processSubmatch() { } private void showDebugMerge(AggNode locationToMove, TreeSet<GPSPoint> affectedTrace) { TestUI ui = (TestUI) Globals.get("ui"); Layer mergingLayer = ui.getLayerManager().getLayer("merging"); List<ILocation> line = new ArrayList<ILocation>(2); for (ILocation to : affectedTrace) { line.add(new GPSPoint(locationToMove)); line.add(new GPSPoint(to)); mergingLayer.addObject(line); line = new ArrayList<ILocation>(2); } } private void showDebugInfo() { TestUI ui = (TestUI) Globals.get("ui"); if (ui == null) { return; } Layer matchingLayer = ui.getLayerManager().getLayer("matching"); // clone the lists List<ILocation> aggNodesClone = new ArrayList<ILocation>( aggNodes.size()); for (ILocation loc : aggNodes) { aggNodesClone.add(new GPSPoint(loc)); } matchingLayer.addObject(aggNodesClone); matchingLayer.addObject(gpsPoints); // , roMatchGPS); // // Iterator<AggNode> it = AtoT.keySet().iterator(); // AggNode nextKey; // while (it.hasNext()) { // nextKey = it.next(); // if (AtoT.get(nextKey) == null) // continue; // List<ILocation> line = new ArrayList<ILocation>(2); // for (ILocation to : AtoT.get(nextKey)) { // line.add(new GPSPoint(nextKey)); // line.add(new GPSPoint(to)); // mergingLayer.addObject(line); // line = new ArrayList<ILocation>(2); // } // } } @Override public double getDistance() { return distance; } @Override public void setDistance(double bestDifference) { this.distance = bestDifference; } @Override public void mergePoints() { // add nodes AggNode lastNode = null; AggConnection conn = null; for (AggNode node : getAggNodes()) { if (lastNode == null) { lastNode = node; continue; } /* Make sure that they are connected */ conn = lastNode.getConnectionTo(node); if (conn == null) { continue; } // aConn.add(new AggConnection(lastNode, node, aggContainer)); conn.tryToFill(); lastNode = node; } frechetMerge(aggNodes, gpsPoints); } public void frechetMerge(List<AggNode> agg, List<GPSPoint> tra) { // Convert to vectors for frechet input. AggNode lastAgg = null; GPSPoint lastTra = null; List<GPSEdge> Av = new ArrayList<GPSEdge>(); List<GPSEdge> Tv = new ArrayList<GPSEdge>(); AggNode to; for (AggNode a : agg) { if (lastAgg == null) { lastAgg = a; continue; } Av.add(new GPSEdge(lastAgg, a)); lastAgg = a; } for (GPSPoint t : tra) { if (lastTra == null) { lastTra = t; continue; } Tv.add(new GPSEdge(lastTra, t)); lastTra = t; } // // Value of epsilon not relevant in our use case. FrechetDistance fd = new FrechetDistance(Av, Tv, distance / 92500.0); // Compute critical values and the meta information needed by our // algorithm. double epsilon = fd.computeEpsilon(); // System.out.println("FrechetBasedMerge: Use epsilon of: " + epsilon // + " isOk " + fd.isInDistance()); fd.computeMetaData(); // // Map points from A to T for (Entry<Integer, TreeSet<AggNode>> entry : fd.fromP.entrySet()) { int i = entry.getKey(); if (i < fd.P.size()) { AtoT.put(fd.P.get(i).getFrom(), entry.getValue()); } else if (i == fd.P.size()) { AtoT.put(fd.P.get(i - 1).getTo(), entry.getValue()); } } showDebugInfo(); // // Move the aggregate by the mean of the segments introduced by // Frechet critical values. See paperwork for clarification. List<AggNode> keySet = new ArrayList<AggNode>(AtoT.keySet()); for (int i = 0; i < keySet.size() - 1; i++) { // TODO AggNode locationToMove = keySet.get(i); if (AtoT.containsKey(locationToMove)) { TreeSet<AggNode> affectedTraceLocations = AtoT .get(locationToMove); if (affectedTraceLocations != null) { TreeSet<GPSPoint> affectedTrace = new TreeSet<GPSPoint>(); // Save meta information for display in the gui. for (AggNode ti : affectedTraceLocations) { double dist = GPSCalc.getDistanceTwoPointsDouble( locationToMove, ti); if (dist <= epsilon) { criticalEdges.add(new GPSEdge(new GPSPoint( locationToMove), new GPSPoint(ti))); affectedTrace.add(new GPSPoint(ti)); } } showDebugMerge(locationToMove, affectedTrace); AggNode weightedpos = GPSCalc.calculateMean(locationToMove, affectedTrace, epsilon, aggContainer, false); AggNode toMean = new AggNode(weightedpos, aggContainer); if (toMean.compareTo(locationToMove) != 0) { to = GPSCalc.moveLocation(locationToMove, toMean, aggContainer); for (AggNode a : aggNodes) { if (locationToMove.getLat() == a.getLat() && locationToMove.getLon() == a.getLon()) { locationToMove = a; } } aggContainer.moveNodeTo(locationToMove, to); } } } } // Also the last point. AggNode locationToMove = keySet.get(keySet.size() - 1); if (AtoT.containsKey(locationToMove)) { TreeSet<AggNode> affectedTraceLocations = AtoT.get(locationToMove); if (affectedTraceLocations != null) { TreeSet<GPSPoint> affectedTrace = new TreeSet<GPSPoint>(); // Save meta information for display in the gui. for (AggNode ti : affectedTraceLocations) { // double dist = locationToMove.getDistanceTo(ti); double dist = GPSCalc.getDistanceTwoPointsDouble( locationToMove, ti); // TODO epsilon, nicht delta if (dist <= epsilon) { criticalEdges.add(new GPSEdge(new GPSPoint( locationToMove), new GPSPoint(ti))); // System.err.printf("\\draw[dashed,thin] (%.8f, %.8f) to (%.8f, %.8f);\n", // locationToMove.getLongitude(), // locationToMove.getLatitude(), // ti.getLongitude(), ti.getLatitude()); } } AggNode weightedpos = GPSCalc.calculateMean(locationToMove, affectedTrace, epsilon, aggContainer, false); AggNode toMean = new AggNode(weightedpos, aggContainer); if (toMean.compareTo(locationToMove) != 0) { to = GPSCalc.moveLocation(locationToMove, toMean, aggContainer); for (AggNode a : aggNodes) { if (locationToMove.getLat() == a.getLat() && locationToMove.getLon() == a.getLon()) { locationToMove = a; } } aggContainer.moveNodeTo(locationToMove, to); } } } } @Override public AggNode getInNode() { return inNode; } @Override public AggNode getOutNode() { return outNode; } @Override public String toString() { StringBuilder gps = new StringBuilder(); for (GPSPoint point : gpsPoints) { gps.append(point).append(", "); } StringBuilder agg = new StringBuilder(); for (AggNode node : aggNodes) { agg.append(node).append(", "); } return String.format("MergeHandler:\n\tGPS: %s\n\tAgg: %s", gps, agg); } @Override public boolean isEmpty() { return gpsPoints.isEmpty() && gpsPoints.isEmpty(); } @Override public void setBeforeNode(AggNode lastNode) { this.beforeNode = lastNode; } @Override public void addAggNodes(AggConnection bestConn) { List<AggNode> agg = new ArrayList<AggNode>(); agg.add(bestConn.getFrom()); agg.add(bestConn.getTo()); addAggNodes(agg); } @Override public void addGPSPoints(GPSEdge edge) { List<GPSPoint> tra = new ArrayList<GPSPoint>(); tra.add(edge.getFrom()); tra.add(edge.getTo()); addGPSPoints(tra); } @Override public IMergeHandler getCopy() { FrechetBasedMerge object = new FrechetBasedMerge(); object.aggContainer = this.aggContainer; object.setMaxLookahead(this.getMaxLookahead()); object.setMinContinuationAngle(this.getMinContinuationAngle()); object.setMaxPointGhostDist(this.getMaxPointGhostDist()); return object; } @Override public List<ClassObjectEditor> getSettings() { List<ClassObjectEditor> result = new ArrayList<ClassObjectEditor>(); result.add(new ClassObjectEditor(this, Arrays.asList(new String[]{ "aggContainer", "distance", "rdpf"}))); result.add(new ClassObjectEditor(this.rdpf)); return result; } @Override public List<PointGhostPointPair> getPointGhostPointPairs() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } }