package de.fub.agg2graph.agg.strategy; import de.fub.agg2graph.agg.AggCleaner; 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.Pair; 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.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; public class AttractionForceMerge 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 Map<AggConnection, List<PointGhostPointPair>> newNodesPerConn; private List<PointGhostPointPair> pointGhostPointPairs = new ArrayList<PointGhostPointPair>(); private AggNode inNode; private AggNode outNode; private AggContainer aggContainer; private RenderingOptions roMatchGPS; // cleaning stuff private final RamerDouglasPeuckerFilter rdpf = new RamerDouglasPeuckerFilter(0, 50); private static AggCleaner cleaner = new AggCleaner().enableDefault(); private double maxPointGhostDist = 10; // meters private double distance = 10; @SuppressWarnings("unused") private AggNode beforeNode; private final AttractionValue av = new AttractionValue(); public AttractionForceMerge() { // 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 AttractionForceMerge(AggContainer aggContainer) { this(); this.aggContainer = aggContainer; } public AttractionForceMerge(AggContainer aggContainer, List<AggNode> aggNodes, List<GPSPoint> gpsPoints) { this.aggNodes = aggNodes; this.gpsPoints = gpsPoints; } /** * @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() { newNodesPerConn = new HashMap<AggConnection, List<PointGhostPointPair>>(); pointGhostPointPairs = new ArrayList<PointGhostPointPair>(); Pair<AggNode, AggNode> pairAgg = null; Pair<GPSPoint, GPSPoint> pairTraj = null; // Not interested with too few points if (getAggNodes().size() < 3 || getGpsPoints().size() < 2) { return; } inNode = aggNodes.get(0); outNode = aggNodes.get(aggNodes.size() - 1); // projections of the aggregation to the trace for (int pointIndex = 0; pointIndex < getAggNodes().size(); pointIndex++) { double current, best = Double.MAX_VALUE; int bestI = 0; AggNode node = getAggNodes().get(pointIndex); logger.log(Level.FINER, "agg node {0}", node); // loop over all possible opposing lines List<GPSPoint> internalGpsPoints = getGpsPoints(); PointGhostPointPair pair = null; // START // For all point with exception start and end point if (!(pointIndex == 0 || pointIndex == getAggNodes().size() - 1)) { for (int i = 0; i < internalGpsPoints.size() - 1; i++) { current = GPSCalc.getDistancePointToEdgeMeter(getAggNodes() .get(pointIndex), internalGpsPoints.get(i), internalGpsPoints.get(i + 1)); if (current < best && GPSCalc.getProjectionPoint( getAggNodes().get(pointIndex), internalGpsPoints.get(i), internalGpsPoints.get(i + 1)) != null) { best = current; bestI = i; } } if (best < Double.MAX_VALUE) { pairAgg = new Pair<AggNode, AggNode>(getAggNodes().get( pointIndex - 1), getAggNodes().get(pointIndex + 1)); pairTraj = new Pair<GPSPoint, GPSPoint>( internalGpsPoints.get(bestI), internalGpsPoints.get(bestI + 1)); } if (pairAgg != null && pairTraj != null) { pair = PointGhostPointPair.createAttraction(getAggNodes() .get(pointIndex), pairAgg, pairTraj, 0); pointGhostPointPairs.add(pair); } } else { int temp = pointIndex == 0 ? 0 : getAggNodes().size() - 1; for (int i = 0; i < internalGpsPoints.size(); i++) { current = GPSCalc.getDistanceTwoPointsMeter(getAggNodes() .get(temp), internalGpsPoints.get(i)); if (current < best) { best = current; bestI = i; } } pair = PointGhostPointPair.createAttraction( getAggNodes().get(temp), internalGpsPoints.get(bestI)); pointGhostPointPairs.add(pair); } if (pair != null && pointIndex < getAggNodes().size() - 1) { AggConnection conn = getAggNodes().get(pointIndex) .getConnectionTo(getAggNodes().get(pointIndex + 1)); if (!newNodesPerConn.containsKey(conn)) { newNodesPerConn.put(conn, new ArrayList<PointGhostPointPair>()); } newNodesPerConn.get(conn).add(pair); } // END } } public Pair<GPSPoint, GPSPoint> getBestEdge(AggNode node, GPSPoint start, GPSPoint end) { return null; } private void showDebugInfo() { TestUI ui = (TestUI) Globals.get("ui"); if (ui == null) { return; } Layer matchingLayer = ui.getLayerManager().getLayer("matching"); Layer mergingLayer = ui.getLayerManager().getLayer("merging"); // 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); for (PointGhostPointPair pgpp : pointGhostPointPairs) { if (!pgpp.isEnd) { List<ILocation> line = new ArrayList<ILocation>(2); List<ILocation> line2 = new ArrayList<ILocation>(2); line.add(new GPSPoint(pgpp.source)); line.add(new GPSPoint(pgpp.pairTraj.a)); line2.add(new GPSPoint(pgpp.source)); line2.add(new GPSPoint(pgpp.pairTraj.b)); mergingLayer.addObject(line); mergingLayer.addObject(line2); } else { List<ILocation> line = new ArrayList<ILocation>(2); line.add(new GPSPoint(pgpp.source)); line.add(new GPSPoint(pgpp.proj)); mergingLayer.addObject(line); } } } @Override public double getDistance() { return distance; } @Override public void setDistance(double bestDifference) { this.distance = bestDifference; } @Override public void mergePoints() { if (getAggNodes().size() < 3 || getGpsPoints().size() < 2) { return; } showDebugInfo(); // add nodes // List<AggConnection> changedAggConnections = new ArrayList<AggConnection>( // 10); // 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; } conn.tryToFill(); lastNode = node; } for (PointGhostPointPair pgpp : pointGhostPointPairs) { if (!pgpp.isEnd) { attractionForce(pgpp.source, pgpp.pairAgg.a, pgpp.pairAgg.b, pgpp.pairTraj.a, pgpp.pairTraj.b); } else { attractionForce(pgpp.source, pgpp.proj); } } } // AttractionForce Parameter // double M = 1; // double N = 20; // double x = 0; // double y = 0; // double s1 = 5; // double s2 = 5; // public void attractionForce(AggNode currentNode, GPSPoint projection) { double distance = GPSCalc.getDistanceTwoPointsMeter(currentNode, projection); Double aValue = av.getValue(distance); currentNode.setK(currentNode.getK() + 1); if (aValue != null) { //To damp the movement due to k, log2 is used double dampingFactor = Math.log10(currentNode.getK()) / Math.log10(2); ILocation newPos = GPSCalc.getPointAt( (av.getKey(distance) - aValue) / (av.getKey(distance) * dampingFactor), currentNode, projection); aggContainer.moveNodeTo(currentNode, newPos); } } public void attractionForce(AggNode currentNode, AggNode before, AggNode after, GPSPoint trajStart, GPSPoint trajEnd) { // TODO Null gefahr double angle = GPSCalc.getAngleBetweenEdges(before, after, trajStart, trajEnd); ILocation aggProj = GPSCalc.getProjectionPoint(currentNode, before, after); if (aggProj == null) { return; } ILocation trajProj = GPSCalc.intersection(currentNode, aggProj, trajStart, trajEnd); if (trajProj == null) { return; } double distance = GPSCalc.getDistanceTwoPointsMeter(currentNode, trajProj); Double aValue = av.getValue(distance); currentNode.setK(currentNode.getK() + 1); if (aValue != null) { //To damp the movement due to k, log2 is used double dampingFactor = Math.log10(currentNode.getK()) / Math.log10(2); ILocation newPos = GPSCalc.getPointAt( (av.getKey(distance) - aValue) / (av.getKey(distance) * dampingFactor), currentNode, trajProj); aggContainer.moveNodeTo(currentNode, newPos); } } @Override public AggNode getInNode() { return inNode; } @Override public AggNode getOutNode() { return outNode; } // TODO FAUL @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() { AttractionForceMerge object = new AttractionForceMerge(); 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. } /** * @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; } }