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.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 IterativeClosestPointsMerge 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 max = 0;
private int maxLookahead = 4;
private double minContinuationAngle = 45;
// helper stuff
// private Map<AggNode, List<GPSPoint>> kNeighbours = new HashMap<AggNode,
// List<GPSPoint>>();
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, 125);
// private static AggCleaner cleaner = new AggCleaner().enableDefault();
private double maxPointGhostDist = 40; // meters
private double distance = 10;
@SuppressWarnings("unused")
private AggNode beforeNode;
private double delta = 0.003;
private final int k = 3;
public IterativeClosestPointsMerge() {
// 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 IterativeClosestPointsMerge(AggContainer aggContainer) {
this();
this.aggContainer = aggContainer;
}
public IterativeClosestPointsMerge(AggContainer aggContainer,
List<AggNode> aggNodes, List<GPSPoint> gpsPoints) {
this.aggNodes = aggNodes;
this.gpsPoints = gpsPoints;
}
/**
* @return the max
*/
public int getMax() {
return max;
}
/**
* @param max the max to set
*/
public void setMax(int max) {
this.max = max;
}
/**
* @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 delta
*/
public double getDelta() {
return delta;
}
/**
* @param delta the delta to set
*/
public void setDelta(double delta) {
this.delta = delta;
}
public double getMaxPointGhostDist() {
return maxPointGhostDist;
}
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>();
// 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++) {
AggNode node = getAggNodes().get(pointIndex);
logger.log(Level.FINER, "agg node {0}", node);
// loop over all possible opposing lines
PointGhostPointPair pair = null;
// START
List<GPSPoint> neighbour;
if (pointIndex == 0 || pointIndex == getAggNodes().size() - 1) {
neighbour = getKSmallest(gpsPoints, node, 1);
} else {
neighbour = getKSmallest(gpsPoints, node, k);
}
if (neighbour.size() > 0) {
pair = PointGhostPointPair.createIterative(
node, neighbour, 0);
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
}
}
private static List<GPSPoint> getKSmallest(List<GPSPoint> trace,
AggNode from, int k) {
double currentMaxDistance = 0;
GPSPoint currentMax = null;
if (trace.isEmpty()) {
return null;
} else if (trace.size() <= k) {
return trace;
} else {
List<GPSPoint> dist = new ArrayList<GPSPoint>(k);
for (GPSPoint p : trace) {
if (dist.size() < k) {
dist.add(p);
} else {
currentMaxDistance = GPSCalc.getDistanceTwoPointsMeter(
from, p);
currentMax = p;
for (GPSPoint d : dist) {
if (currentMaxDistance < GPSCalc
.getDistanceTwoPointsMeter(from, d)) {
currentMaxDistance = GPSCalc
.getDistanceTwoPointsMeter(from, d);
currentMax = d;
}
}
if (dist.contains(currentMax)) {
dist.remove(currentMax);
dist.add(p);
}
}
}
return dist;
}
}
@SuppressWarnings("unused")
private ILocation testLength(GPSPoint point, ILocation newPoint) {
if (newPoint != null) {
if (GPSCalc.getDistanceTwoPointsMeter(point, newPoint) > maxPointGhostDist) {
return null;
}
}
return newPoint;
}
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) {
List<ILocation> line = new ArrayList<ILocation>(2);
for (int j = 0; j < pgpp.ghostPoints.size(); j++) {
line.add(new GPSPoint(pgpp.source));
line.add(new GPSPoint(pgpp.ghostPoints.get(j)));
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() {
this.setMax(aggNodes.size());
showDebugInfo();
// 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) {
closestPointsMerge(pgpp.source, pgpp.ghostPoints);
}
}
public void closestPointsMerge(AggNode a, List<GPSPoint> ts) {
AggNode toMean = GPSCalc.calculateMean(a, ts, getDelta(), aggContainer, true);
AggNode to = GPSCalc.moveLocation(a, toMean, aggContainer);
// GPSCalc.moveLocation(map, a, toCopy, aggContainer);
a.setK(a.getK() + 1);
aggContainer.moveNodeTo(a, to);
// System.out.println("t = " + to.getLat() + " <> " +
// to.getLon());
}
@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() {
IterativeClosestPointsMerge object = new IterativeClosestPointsMerge();
object.aggContainer = this.aggContainer;
object.setMaxLookahead(this.getMaxLookahead());
object.setMinContinuationAngle(this.getMinContinuationAngle());
object.maxPointGhostDist = this.maxPointGhostDist;
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.
}
}