/** * ***************************************************************************** * Copyright (c) 2012 Johannes Mitlmeier. All rights reserved. This program and * the accompanying materials are made available under the terms of the GNU * Affero Public License v3.0 which accompanies this distribution, and is * available at http://www.gnu.org/licenses/agpl-3.0.html * * Contributors: Johannes Mitlmeier - initial API and implementation * **************************************************************************** */ package de.fub.agg2graph.agg.strategy; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.agg.IMergeHandler; import de.fub.agg2graph.agg.ITraceDistance; import de.fub.agg2graph.structs.CartesianCalc; import de.fub.agg2graph.structs.ClassObjectEditor; import de.fub.agg2graph.structs.GPSCalc; import de.fub.agg2graph.structs.GPSPoint; import de.fub.agg2graph.structs.ILocation; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; public class PathScoreDistance implements ITraceDistance { private static final Logger logger = Logger .getLogger("agg2graph.agg.default.dist"); private double aggReflectionFactor = 4; private int maxOutliners = 10; private double maxDistance = 60;//7.5 private int maxLookahead = 10; private double maxPathDifference = 100;//10 private int minLengthFirstSegment = 1; private double maxAngle = 37; /** * Compute the difference of a path to the aggregation. This measure only * guarantees relative correctness when questioned repeatedly for different * paths. A difference of 0 indicates equality while larger values indicate * increasingly different paths. * * @param aggPath * @param tracePoints * @param startIndex * @param dmh * @return Object[] { double bestValue, int bestValueLength } */ @Override public Object[] getPathDifference(List<AggNode> aggPath, List<GPSPoint> tracePoints, int startIndex, IMergeHandler dmh) { double bestValue = Double.MAX_VALUE; double bestValueLength = 0; for (int i = startIndex; i < Math.min(startIndex + 20, tracePoints.size()); i++) { logger.log( Level.FINER, String.format("Testing %s and %s...", aggPath, tracePoints.subList(startIndex, i + 1))); List<AggNode> aggLocations = aggPath; List<GPSPoint> traceLocations = tracePoints.subList(startIndex, i + 1); // one-element similarity is not interesting if (traceLocations.size() <= getMinLengthFirstSegment() || aggLocations.size() <= getMinLengthFirstSegment()) { logger.log(Level.FINE, "Too short path"); // System.out.println("Too Short"); continue; } // angle okay? double angle = 180; if (dmh != null && dmh.getGpsPoints().size() > 0) { angle = GPSCalc.getAngleBetweenEdges(dmh.getGpsPoints().get(0), tracePoints.get(i), dmh.getAggNodes().get(0), aggPath.get(aggPath.size() - 1)); } else { angle = GPSCalc.getAngleBetweenEdges( tracePoints.get(startIndex), tracePoints.get(i), aggPath.get(0), aggPath.get(aggPath.size() - 1)); } if (!CartesianCalc.isAngleMax(angle, maxAngle)) { logger.log(Level.FINE, String.format("Angle is not good: %.1f", angle)); continue; } /* * for the new point in the new trace: find minimal distance to all * possibly matching edges of the aggregation */ double dist = Double.MAX_VALUE / 2; double[] traceToAggDistances = getPointToLineDistances( traceLocations, aggLocations); if (!outlinersOkay(traceToAggDistances)) { continue; } dist = Math.min(dist, getAverageDistance(traceToAggDistances)); double[] aggToTraceDistances = getPointToLineDistances( aggLocations, traceLocations); dist = Math.max(dist, dist + 1.0 / getAggReflectionFactor() * getAverageDistance(aggToTraceDistances)); // value formula logger.log(Level.FINE, "dist: {0}", dist); // consider length double value = dist * Math.pow(0.95, aggLocations.size() + traceLocations.size()); logger.log(Level.FINE, "value: {0}", value); if (value > getMaxPathDifference()) { value = Double.MAX_VALUE; } logger.log(Level.FINE, String.format("Value of path: %.3f", value)); if (value < bestValue) { logger.log(Level.FINE, "best"); bestValue = value; bestValueLength = traceLocations.size(); } } return new Object[]{bestValue, bestValueLength}; } private double[] getPointToLineDistances(List<? extends ILocation> from, List<? extends ILocation> to) { double[] result = new double[from.size()]; ILocation loc; for (int i = 0; i < from.size(); i++) { loc = from.get(i); result[i] = GPSCalc.getDistancePointToTraceMeter(loc, to)[0]; } return result; } private boolean outlinersOkay(double[] distances) { int outliers = 0; double distance; for (int dIndex = 0; dIndex < distances.length; dIndex++) { distance = distances[dIndex]; if (distance > getMaxDistance()) { if (outliers > getMaxOutliners()) { // this path is not good logger.log(Level.FINE, String.format( "Too many outliners (%d), limit is %d in a row.", outliers + 1, outliers)); return false; } else if (dIndex == 0) { logger.log( Level.FINE, String.format( "Outliner point at the start. Distance is %.3f, it should be below %.3f.", distance, getMaxDistance())); return false; } else if (dIndex == distances.length - 1) { logger.log( Level.FINE, String.format( "Outliner point at the end. Distance is %.3f, it should be below %.3f.", distance, getMaxDistance())); return false; } outliers++; } else { outliers = 0; } } return true; } private double getAverageDistance(double[] aggToTraceDistances) { double sum = 0; int maxCounter = 0; for (double d : aggToTraceDistances) { if (d < Double.MAX_VALUE) { sum += d; } else { maxCounter++; } } return sum / (aggToTraceDistances.length - maxCounter); } @Override public List<ClassObjectEditor> getSettings() { List<ClassObjectEditor> result = new ArrayList<ClassObjectEditor>(); result.add(new ClassObjectEditor(this)); return result; } /** * @return the aggReflectionFactor */ public double getAggReflectionFactor() { return aggReflectionFactor; } /** * @param aggReflectionFactor the aggReflectionFactor to set */ public void setAggReflectionFactor(double aggReflectionFactor) { this.aggReflectionFactor = aggReflectionFactor; } /** * @return the maxOutliners */ public int getMaxOutliners() { return maxOutliners; } /** * @param maxOutliners the maxOutliners to set */ public void setMaxOutliners(int maxOutliners) { this.maxOutliners = maxOutliners; } /** * @return the maxDistance */ public double getMaxDistance() { return maxDistance; } /** * @param maxDistance the maxDistance to set */ public void setMaxDistance(double maxDistance) { this.maxDistance = maxDistance; } /** * @return the maxLookahead */ public int getMaxLookahead() { return maxLookahead; } /** * @param maxLookahead the maxLookahead to set */ public void setMaxLookahead(int maxLookahead) { this.maxLookahead = maxLookahead; } /** * @return the maxPathDifference */ public double getMaxPathDifference() { return maxPathDifference; } /** * @param maxPathDifference the maxPathDifference to set */ public void setMaxPathDifference(double maxPathDifference) { this.maxPathDifference = maxPathDifference; } /** * @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; } }