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.ITraceDistance;
import de.fub.agg2graph.input.Trace;
import de.fub.agg2graph.structs.ClassObjectEditor;
import de.fub.agg2graph.structs.GPSEdge;
import de.fub.agg2graph.structs.GPSPoint;
import de.fub.agg2graph.structs.ILocation;
import de.fub.agg2graph.structs.frechet.BidirectionalFrechetDistance;
import de.fub.agg2graph.structs.frechet.IAggregatedMap;
import de.fub.agg2graph.structs.frechet.ITrace;
import de.fub.agg2graph.structs.frechet.Pair;
import de.fub.agg2graph.structs.frechet.TreeAggMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
public class FreeSpaceMatch implements ITraceDistance {
private double maxDistance = 7.5;
private int minLengthFirstSegment = 1;
private double maxAngle = 37;
public static AggContainer aggContainer;
public IAggregatedMap map;
public ITrace trace;
public ILocation start;
public double bestValue;
interface Pather {
double expectedfd();
boolean consume();
}
/**
* This class is responsible to calculate the expected changes to the
* frechet distance while adding a possible extension of the matched map
* path.
*
*/
static class MapPather implements Pather {
IAggregatedMap map;
ILocation nl;
ILocation pl;
List<AggConnection> path = new ArrayList<AggConnection>();
private BidirectionalFrechetDistance bfd;
private AggConnection proposedConsumee = null;
MapPather(IAggregatedMap map, ILocation start,
BidirectionalFrechetDistance bfd) {
this.map = map;
this.nl = map.searchNN(new AggNode(start, aggContainer));
this.pl = nl;
this.bfd = bfd;
}
/**
* Calculate the expected frechet distances while return the minimum of
* all choices.
*/
public double expectedfd() {
Collection<AggConnection> ncandidates = map.getOutConnections(
(AggNode) nl, null);
// Collection<AggConnection> pcandidates = map.getInConnections(
// (AggNode) pl, null);
double min = Double.POSITIVE_INFINITY;
for (AggConnection n : ncandidates) {
bfd.appendToP(n);
double appx = bfd.approximateDistance();
bfd.removeLastOfP();
if (appx < min) {
min = appx;
proposedConsumee = n;
}
}
// for (AggConnection p : pcandidates) {
// bfd.prependToP(p);
// double appx = bfd.approximateDistance();
// bfd.removeFirstOfP();
// if (appx < min) {
// min = appx;
// proposedConsumee = p;
// }
// }
return min;
}
/**
* Extend the path by the edge calculated with expectedfd.
*
* expectedfd needs to be called first.
*/
@Override
public boolean consume() {
if (proposedConsumee != null) {
// if (proposedConsumee.getTo().compareTo((GPSPoint) pl) == 0) {
// bfd.prependToP(proposedConsumee);
// if (bfd.isInDistance()) {
// path.add(0, proposedConsumee);
// pl = proposedConsumee.getFrom();
// } else {
// bfd.removeFirstOfP();
// return false;
// }
//
// } else
if (proposedConsumee.getFrom().compareTo((GPSPoint) nl) == 0) {
bfd.appendToP(proposedConsumee);
if (bfd.isInDistance()) {
path.add(proposedConsumee);
nl = proposedConsumee.getTo();
} else {
bfd.removeLastOfP();
return false;
}
}
}
return false;
}
public List<AggConnection> getPath() {
return path;
}
}
/**
* Hold the trace path, calculate the expected frechet distance by either
* appending or prepending the edges.
*
*/
static class TracePather implements Pather {
List<GPSEdge> path = new ArrayList<GPSEdge>();
private BidirectionalFrechetDistance bfd;
ListIterator<GPSEdge> forward;
ListIterator<GPSEdge> backward;
GPSEdge next = null;
GPSEdge previous = null;
private boolean proposeForward;
TracePather(ITrace trace, ILocation start,
BidirectionalFrechetDistance bfd) {
this.bfd = bfd;
this.forward = trace.edgeListIterator(start);
this.backward = trace.edgeListIterator(start);
}
public boolean initialNext() {
if (forward.hasNext()) {
GPSEdge test = forward.next();
bfd.appendToQ(test);
next = test;
path.add(test);
return true;
}
next = null;
return false;
}
// public boolean initialPrevious() {
// if (backward.hasPrevious()) {
// GPSEdge test = backward.previous();
// bfd.prependToQ(test);
// previous = test;
// path.add(0, test);
// return true;
// }
//
// previous = null;
// return false;
// }
@Override
public double expectedfd() {
double deltab = Double.POSITIVE_INFINITY;
double deltaf = Double.POSITIVE_INFINITY;
if (forward.hasNext()) {
bfd.appendToQ(forward.next());
deltaf = bfd.approximateDistance();
bfd.removeLastOfQ();
forward.previous();
}
// if (backward.hasPrevious()) {
// bfd.prependToQ(backward.previous());
// deltab = bfd.approximateDistance();
// bfd.removeFirstOfQ();
// backward.next();
// }
if (deltaf < deltab) {
proposeForward = true;
} else {
proposeForward = false;
}
return Math.min(deltaf, deltab);
}
@Override
public boolean consume() {
if (proposeForward) {
if (forward.hasNext()) {
GPSEdge test = forward.next();
bfd.appendToQ(test);
if (bfd.isInDistance()) {
next = test;
path.add(test);
return true;
} else {
bfd.removeLastOfQ();
forward.previous();
}
}
next = null;
return false;
}
return false;
// else {
// if (backward.hasPrevious()) {
// GPSEdge test = backward.previous();
// bfd.prependToQ(test);
// if (bfd.isInDistance()) {
// previous = test;
// path.add(0, test);
// return true;
// } else {
// bfd.removeFirstOfQ();
// backward.next();
// }
// }
// previous = null;
// return false;
// }
}
List<GPSEdge> getPath() {
return path;
}
}
/**
* @return the maxDistance
*/
public double getMaxDistance() {
return maxDistance;
}
/**
* @param maxDistance the maxDistance to set
*/
public void setMaxDistance(double maxDistance) {
this.maxDistance = maxDistance;
}
/**
* @return the minLengthFirstSegment
*/
public int getMinLengthFirstSegment() {
return minLengthFirstSegment;
}
/**
* @param minLengthFirstSegment the minLengthFirstSegment to set
*/
public void setMinLengthFirstSegment(int minLengthFirstSegment) {
this.minLengthFirstSegment = minLengthFirstSegment;
}
/**
* @return the maxAngle
*/
public double getMaxAngle() {
return maxAngle;
}
/**
* @param maxAngle the maxAngle to set
*/
public void setMaxAngle(double maxAngle) {
this.maxAngle = maxAngle;
}
public Pair<List<AggConnection>, List<GPSEdge>> match(IAggregatedMap map,
ITrace trace, ILocation start, double epsilon) {
BidirectionalFrechetDistance bfd = new BidirectionalFrechetDistance(
epsilon);
bfd.setEpsilon(epsilon);
final TracePather tp = new TracePather(trace, start, bfd);
final MapPather mp = new MapPather(map, start, bfd);
// Set up the initial path to match against.
if (!tp.initialNext()) {
// if (!tp.initialPrevious()) {
// give up
return null;
// }
}
mp.expectedfd();
mp.consume();
// Further extend the paths as long as the criteria are matched.
boolean proceed = true;
while (proceed) {
double dm = mp.expectedfd();
double dt = tp.expectedfd();
if (dm <= dt) {
if (!mp.consume()) {
proceed = tp.consume();
} else {
proceed = true;
}
} else {
if (!tp.consume()) {
proceed = mp.consume();
} else {
proceed = true;
}
}
if (bfd.approximateDistance() > epsilon) {
break;
}
}
Pair<List<AggConnection>, List<GPSEdge>> result = new Pair<List<AggConnection>, List<GPSEdge>>(
mp.getPath(), tp.getPath());
this.bestValue = 0;
// fd.getDistance(mp.path, tp.path);
// System.out.printf("FD: %.8f Soll: %.8f\n",
// bestValue, epsilon);
return result;
}
@Override
public Object[] getPathDifference(List<AggNode> aggPath,
List<GPSPoint> tracePoints, int startIndex, IMergeHandler dmh) {
double bestValue = Double.MIN_VALUE;
double bestValueLength = 0;
List<AggNode> aggResult = new ArrayList<AggNode>();
List<GPSPoint> traceResult = new ArrayList<GPSPoint>();
List<AggNode> aggLocations = aggPath;
List<GPSPoint> traceLocations = tracePoints;
map = new TreeAggMap(aggContainer);
AggNode last = null;
for (AggNode node : aggLocations) {
if (last != null) {
map.insertConnection(new AggConnection(last, node, aggContainer));
}
last = node;
}
start = traceLocations.get(startIndex);
trace = new Trace();
for (int i = startIndex; i < traceLocations.size(); i++) {
trace.insertEdgeLocation(i - startIndex, traceLocations.get(i));
}
Pair<List<AggConnection>, List<GPSEdge>> res = match(map, trace, start,
getMaxDistance() / 92500);
bestValue = this.bestValue;
if (res == null) {
return null;
} else if (res.a.isEmpty() || res.b.isEmpty()) {
return null;
}
for (int i = 0; i < res.a.size(); i++) {
aggResult.add(res.a.get(i).getFrom());
}
aggResult.add(res.a.get(res.a.size() - 1).getTo());
for (int i = 0; i < res.b.size(); i++) {
traceResult.add(res.b.get(i).getFrom());
}
traceResult.add(res.b.get(res.b.size() - 1).getTo());
bestValueLength = traceResult.size();
bestValue = 0;
return new Object[]{bestValue, bestValueLength, aggResult,
traceResult};
}
@Override
public List<ClassObjectEditor> getSettings() {
List<ClassObjectEditor> result = new ArrayList<ClassObjectEditor>();
result.add(new ClassObjectEditor(this));
return result;
}
}