/** * ***************************************************************************** * 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.AggConnection; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.agg.AggregationStrategyFactory; import de.fub.agg2graph.agg.IMergeHandler; import de.fub.agg2graph.agg.MergeHandlerFactory; import de.fub.agg2graph.agg.TraceDistanceFactory; import de.fub.agg2graph.management.MyStatistic; import de.fub.agg2graph.structs.GPSCalc; import de.fub.agg2graph.structs.GPSEdge; import de.fub.agg2graph.structs.GPSPoint; import de.fub.agg2graph.structs.GPSSegment; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; public class GpxmergeMatchIterativeMergeStrategy extends AbstractAggregationStrategy { MyStatistic statistic; int counter = 1; private int maxLookahead = 5; private double maxPathDifference = 35; private double maxInitDistance = 12.5; List<AggNode> lastNodes = new ArrayList<AggNode>(); List<GPSSegment> lastNewNodes = new ArrayList<GPSSegment>(); public enum State { NO_MATCH, IN_MATCH } private State state = State.NO_MATCH; /** * Preferably use the {@link AggregationStrategyFactory} for creating * instances of this class. */ public GpxmergeMatchIterativeMergeStrategy() { statistic = new MyStatistic( "agg2graph/test/exp/Evaluation-GPXMergeIterativeMerge.txt"); TraceDistanceFactory.setClass(GpxmergeTraceDistance.class); traceDistance = TraceDistanceFactory.getObject(); MergeHandlerFactory.setClass(IterativeClosestPointsMerge.class); baseMergeHandler = MergeHandlerFactory.getObject(); } /** * @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 maxInitDistance */ public double getMaxInitDistance() { return maxInitDistance; } /** * @param maxInitDistance the maxInitDistance to set */ public void setMaxInitDistance(double maxInitDistance) { this.maxInitDistance = maxInitDistance; } @Override public void aggregate(GPSSegment segment, boolean isAgg) { // reset all attributes lastNode = null; mergeHandler = null; matches = new ArrayList<IMergeHandler>(); state = State.NO_MATCH; // insert first segment without changes (assuming somewhat cleaned // data!) // attention: node counter is not necessarily accurate! if (aggContainer.getCachingStrategy() == null || aggContainer.getCachingStrategy().getNodeCount() == 0 || isAgg) { int i = 0; while (i < segment.size()) { GPSPoint pointI = segment.get(i); AggNode node = new AggNode(pointI, aggContainer); node.setK(pointI.getK()); node.setRelevant(pointI.isRelevant()); node.setID("A-" + pointI.getID()); addNodeToAgg(aggContainer, node); lastNode = node; i++; } lastNodes.add(lastNode); statistic.setAggLength(GPSCalc.traceLengthMeter(segment)); statistic.setAggPoints(segment.size()); return; } int i = 0; Set<AggConnection> nearEdges = null; statistic.setTraceLength(GPSCalc.traceLengthMeter(segment)); statistic.setTracePoints(segment.size()); long matchStart = System.currentTimeMillis(); while (i < segment.size()) { State lastState = state; if ((i == segment.size() - 1)) { if (lastState == State.IN_MATCH) { finishMatch(); } break; } // step 1: find starting point // get close points, within 10 meters (merge candidates) // START GPSPoint firstPoint = segment.get(i); GPSPoint secondPoint = segment.get(i + 1); GPSEdge currentEdge = new GPSEdge(firstPoint, secondPoint); nearEdges = aggContainer.getCachingStrategy().getCloseConnections( currentEdge, getMaxInitDistance()); // END boolean isMatch = true; if (nearEdges.isEmpty()) { isMatch = false; state = State.NO_MATCH; } else { Iterator<AggConnection> itNear = nearEdges.iterator(); Double grade = Double.MAX_VALUE; AggConnection bestConn = null; double dist = Double.MAX_VALUE; while (itNear.hasNext()) { AggConnection near = itNear.next(); Object[] distReturn = traceDistance.getPathDifference( near.toPointList(), segment, i, mergeHandler); dist = (Double) distReturn[0]; if (dist < getMaxPathDifference() && dist < grade) { grade = dist; bestConn = near; } } // do we have a successful match? if (bestConn == null) { isMatch = false; } // else if (bestPath.size() <= 1 && bestPathLength <= 1) { // // } state = isMatch ? State.IN_MATCH : State.NO_MATCH; if (isMatch) { // System.out.println("I = " + i); // System.out.println(bestConn.getFrom() + " : " + // bestConn.getTo()); // System.out.println(currentEdge.getFrom() + " : " + // currentEdge.getTo()); // make a merge handler if the match would start here if (lastState == State.NO_MATCH) { mergeHandler = baseMergeHandler.getCopy(); mergeHandler.setAggContainer(aggContainer); } // isMatch = false; if (!mergeHandler.getAggNodes() .contains(bestConn.getFrom())) { mergeHandler.addAggNode(bestConn.getFrom()); } if (!mergeHandler.getAggNodes().contains(bestConn.getTo())) { mergeHandler.addAggNode(bestConn.getTo()); } if (!mergeHandler.getGpsPoints().contains( currentEdge.getFrom())) ; mergeHandler.addGPSPoint(currentEdge.getFrom()); if (!mergeHandler.getGpsPoints().contains( currentEdge.getTo())) ; mergeHandler.addGPSPoint(currentEdge.getTo()); mergeHandler.setDistance(grade); i++; } } if (!isMatch && (lastState == State.IN_MATCH && (state == State.NO_MATCH || i == segment .size() - 1))) { finishMatch(); i++; } else if (!isMatch && lastState == State.NO_MATCH) { // if there is no close points or no valid match, add it to the // aggregation if (getAddAllowed()) { GPSPoint currentPoint = segment.get(i); AggNode node = new AggNode(currentPoint, aggContainer); node.setID("A-" + currentPoint.getID()); node.setK(1); addNodeToAgg(aggContainer, node); lastNode = node; } i++; } } long matchEnd = System.currentTimeMillis(); statistic.setRuntimeMatch(matchEnd - matchStart); // New Segment if (getAddAllowed() && lastNode != null) { List<AggNode> newSegment = new ArrayList<AggNode>(); AggNode currentLast = lastNode; newSegment.add(0, currentLast); while (!lastNode.getIn().isEmpty()) { lastNode = lastNode.getIn().iterator().next().getFrom(); if (GPSCalc.getDistanceTwoPointsMeter(currentLast, lastNode) < 100) { newSegment.add(0, lastNode); } else { if (newSegment.size() > 1) { lastNewNodes.add(new GPSSegment(newSegment)); } newSegment = new ArrayList<AggNode>(); } currentLast = lastNode; } if (newSegment.size() > 1) { lastNewNodes.add(new GPSSegment(newSegment)); } } // step 2 and 3 of 3: ghost points, merge everything System.out.println(counter + ". MATCHES : " + matches.size()); System.out.println("New Segment : " + getAddAllowed()); statistic.resetMatchedAggLength(); statistic.resetMatchedAggPoints(); statistic.resetMatchedTraceLength(); statistic.resetMatchedTracePoints(); long mergeStart = System.currentTimeMillis(); for (IMergeHandler match : matches) { statistic.setMatchedAggLength(GPSCalc.traceLengthMeter(match .getAggNodes())); statistic.setMatchedAggPoints(match.getAggNodes().size()); statistic.setMatchedTraceLength(GPSCalc.traceLengthMeter(match .getGpsPoints())); statistic.setMatchedTracePoints(match .getGpsPoints().size()); } long mergeEnd = System.currentTimeMillis(); statistic.setRuntimeMerge(mergeEnd - mergeStart); Runtime runtime = Runtime.getRuntime(); // Run the garbage collector runtime.gc(); // Calculate the used memory long memory = runtime.totalMemory() - runtime.freeMemory(); statistic.setMemoryUsed(bytesToMegabytes(memory)); for (GPSSegment lastNewNode : lastNewNodes) { statistic.setNewAggLength(GPSCalc.traceLengthMeter(lastNewNode)); statistic.setNewAggPoints(lastNewNode.size()); } /** * Save new Map */ // try { // List<GPSSegment> segments = new ArrayList<GPSSegment>(); // for (AggNode last : lastNodes) { // segments.add(SerializeAgg.getSegmentFromLastNode(last)); // } // // // Extension // if (lastNewNodes.size() > 0 && getAddAllowed()) // segments.addAll(lastNewNodes); // // GPXWriter.writeSegments(new File(new String("agg2graph/test/input/map 2.0a/" // + "map" + counter++ + ".gpx")), segments); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } /** * Statistic record */ // try { // statistic.writefile(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } statistic.resetAll(); lastNodes.clear(); lastNewNodes.clear(); } protected void finishMatch() { // last match is over now matches.add(mergeHandler); mergeHandler.processSubmatch(); /* * connect to previous node lastNode is the last non-matched node or the * outNode of the last match */ // aggContainer.connect(lastNode, mergeHandler.getInNode()); // mergeHandler.setBeforeNode(lastNode); // // remember outgoing node (for later connection) // lastNode = mergeHandler.getOutNode(); } @Override public String toString() { return "GPXMatch-DefaultMerge"; } private static final long MEGABYTE = 1024L * 1024L; public static long bytesToMegabytes(long bytes) { return bytes / MEGABYTE; } }