diff --git a/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/onlinemm/OnlineViterbi.java b/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/onlinemm/OnlineViterbi.java index a8b4e77..03d7db6 100644 --- a/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/onlinemm/OnlineViterbi.java +++ b/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/onlinemm/OnlineViterbi.java @@ -29,14 +29,17 @@ public class OnlineViterbi extends TiViterbi { public boolean isBrokenBefore; // Current convergence point - private OnlineExtendedState currentRoot; + public OnlineExtendedState currentRoot; // Previous convergence point - private OnlineExtendedState previousRoot; + public OnlineExtendedState previousRoot; // Time diff between root and previous root public int timeDelta; // Starting insert position for global sequence after algorithm interruption public int insertPosition; + public int windowSize; + private int lastCompressTime; + /** * Constructs an OnlineViterbi instance with an initial insert position of zero. @@ -50,6 +53,24 @@ public OnlineViterbi() { previousRoot = null; timeDelta = 0; insertPosition = 0; + windowSize = 0; + lastCompressTime = 0; + } + + /** + * Constructs an OnlineViterbi instance with an initial insert position of zero. + */ + public OnlineViterbi(int windowSize) { + stateList = new LinkedList<>(); + sequenceStates = new ArrayList<>(); + isConverge = false; + isBrokenBefore = false; + currentRoot = null; + previousRoot = null; + timeDelta = 0; + insertPosition = 0; + this.windowSize = windowSize; + lastCompressTime = 0; } /** @@ -58,7 +79,7 @@ public OnlineViterbi() { * * @param insertPosition The starting insert position for the global sequence. */ - public OnlineViterbi(int insertPosition) { + public OnlineViterbi(int insertPosition, int windowSize) { stateList = new LinkedList<>(); sequenceStates = new ArrayList<>(); isConverge = false; @@ -67,6 +88,8 @@ public OnlineViterbi(int insertPosition) { previousRoot = null; timeDelta = 0; this.insertPosition = insertPosition; + this.windowSize = windowSize; + lastCompressTime = 0; } /** @@ -76,7 +99,7 @@ public OnlineViterbi(int insertPosition) { * @param insertPosition The starting insert position for the global sequence. * @param isBrokenBefore A boolean indicating whether the algorithm was broken before this instance was created. */ - public OnlineViterbi(int insertPosition, boolean isBrokenBefore) { + public OnlineViterbi(int insertPosition, int windowSize, boolean isBrokenBefore) { stateList = new LinkedList<>(); sequenceStates = new ArrayList<>(); isConverge = false; @@ -85,6 +108,8 @@ public OnlineViterbi(int insertPosition, boolean isBrokenBefore) { previousRoot = null; timeDelta = 0; this.insertPosition = insertPosition; + this.windowSize = windowSize; + lastCompressTime = 0; } @@ -95,10 +120,10 @@ public OnlineViterbi(int insertPosition, boolean isBrokenBefore) { * @param observation The current GPS observation. * @param candidates List of candidate points for the current step. * @param emissionLogProbabilities Map of emission log probabilities for candidate points. - * @param transitionLogProbabilities Map of transition log probabilities between candidate points. + * @param transitionLogProbabilities Map of transition log probabilities between candidate points. * @param time The current time step. * @throws IllegalStateException if the method is called without initializing - * with an observation or after an HMM break. + * with an observation or after an HMM break. */ public void nextStep( GPSPoint observation, @@ -141,7 +166,7 @@ public void nextStep( * @param curCandidates List of current candidate points. * @param message Map of state probabilities. * @param emissionLogProbabilities Map of emission probabilities for each candidate. - * @param transitionLogProbabilities Map of transition probabilities between candidates. + * @param transitionLogProbabilities Map of transition probabilities between candidates. * @param time The current time step. * @return Results after forward extension, including updated state probabilities and states. */ @@ -259,14 +284,16 @@ else if (time > 1) { current.setNumOfState(validStateCount); } - // Compress unnecessary states - compress(time); - // Free up dummy states - freeDummyState(time); - // Check for convergence point and record local solutions - if (searchForNewRoot()) { - isConverge = true; - traceback(); + if (time > this.windowSize + this.lastCompressTime) { + // Compress unnecessary states + compress(time); + // Free up dummy states + freeDummyState(time); + // Check for convergence point and record local solutions + if (searchForNewRoot()) { + isConverge = true; + traceback(); + } } } @@ -359,7 +386,7 @@ private boolean searchForNewRoot() { OnlineExtendedState ancestor = null; // Time difference between the current and previous convergence points - timeDelta = current.getTime(); + int timeDelta = current.getTime(); while (current != null) { if (current.getNumOfChild() >= 2) ancestor = current; @@ -370,7 +397,11 @@ private boolean searchForNewRoot() { if (currentRoot == null) { currentRoot = ancestor; timeDelta = timeDelta - ancestor.getTime(); - return timeDelta != 0; + if (timeDelta == 0) return false; + else { + lastCompressTime = ancestor.getTime(); + return true; + } } else { if (ancestor != currentRoot) { timeDelta = timeDelta - ancestor.getTime(); @@ -378,6 +409,7 @@ private boolean searchForNewRoot() { else { previousRoot = currentRoot; currentRoot = ancestor; + lastCompressTime = ancestor.getTime(); return true; } } @@ -397,7 +429,12 @@ private void traceback() { interLocalPath.add(new SequenceState(currentRoot.getState(), currentRoot.getObservation())); int depth = isConvergedBefore() ? currentRoot.getTime() - previousRoot.getTime() - 1 : currentRoot.getTime(); + + timeDelta = depth; + depth = Math.min(windowSize, depth); + System.out.println("current root time: " + currentRoot.getTime()); + System.out.println("previous root time: " + (isConvergedBefore() ? previousRoot.getTime() : 0)); ExtendedState current = currentRoot.getBackPointer(); for (int i = 0; i < depth; i++) { @@ -405,7 +442,7 @@ private void traceback() { current = current.getBackPointer(); } - assert current == null || current.getState() == previousRoot.getState(); +// assert current == null || current.getState() == previousRoot.getState(); System.out.println("local added sequence length: " + interLocalPath.size()); Collections.reverse(interLocalPath); sequenceStates.addAll(interLocalPath); diff --git a/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/stream/StreamMapMatcher.java b/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/stream/StreamMapMatcher.java index 0c9f2b6..fc56578 100644 --- a/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/stream/StreamMapMatcher.java +++ b/src/main/java/org/urbcomp/cupid/db/algorithm/mapmatch/stream/StreamMapMatcher.java @@ -129,11 +129,11 @@ public MapMatchedTrajectory streamMapMatch(Trajectory trajectory) throws Algorit * @return MapMatchedTrajectory after online matching * @throws AlgorithmExecuteException In case of algorithm errors */ - public MapMatchedTrajectory onlineStreamMapMatch(Trajectory trajectory) throws AlgorithmExecuteException { + public MapMatchedTrajectory onlineStreamMapMatch(Trajectory trajectory, int windowSize) throws AlgorithmExecuteException { TimeStep previousTimeStep = null; List sequence = new ArrayList<>(); - OnlineViterbi viterbi = new OnlineViterbi(); + OnlineViterbi viterbi = new OnlineViterbi(windowSize); int currentTime = 0; int trajectorySize = trajectory.getGPSPointList().size(); @@ -148,7 +148,7 @@ public MapMatchedTrajectory onlineStreamMapMatch(Trajectory trajectory) throws A currentTime = (viterbi.message == null) ? 0 : 1; } - result = this.computeViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi, currentTime); + result = this.computeOnlineViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi, currentTime); sequence = result._1(); previousTimeStep = result._2(); @@ -246,7 +246,7 @@ private Tuple3, TimeStep, TiViterbi> computeViterbiSequence( * @param time The current time index. * @return A tuple containing the updated sequence, previous time step, and Viterbi object. */ - private Tuple3, TimeStep, OnlineViterbi> computeViterbiSequence( + private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterbiSequence( GPSPoint point, List seq, TimeStep preTimeStep, @@ -256,7 +256,6 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque System.out.println("current time: " + time); windowBearing.addPoint(point); TimeStep currentTimeStep = this.createTimeStep(point); // Create time step with point and candidate set. - int convergeStartIndex = viterbi.getSequenceStates().size(); if (currentTimeStep == null) { @@ -272,7 +271,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque // System.out.println("======================================================"); // Record the start position for global sequence insertion. - viterbi = new OnlineViterbi(seq.size()); + viterbi = new OnlineViterbi(seq.size(), viterbi.windowSize); preTimeStep = null; } else { if (preTimeStep != null) { @@ -309,7 +308,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque if (viterbi.isBroken) { // Handle the case where the Viterbi algorithm encounters an issue. -// System.out.println("Viterbi is broken."); + System.out.println("Viterbi is broken."); // System.out.println("======================================================"); // System.out.println("Sequence length before traceback last part: " + seq.size()); // @@ -325,7 +324,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque seq.add(viterbi.computeMostLikelySequence().get(viterbi.computeMostLikelySequence().size() - 1)); // Record the start position for global sequence insertion. - viterbi = new OnlineViterbi(seq.size(), true); + viterbi = new OnlineViterbi(seq.size(), viterbi.windowSize, true); viterbi.startWithInitialObservation( currentTimeStep.getObservation(), currentTimeStep.getCandidates(), @@ -339,12 +338,13 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque System.out.println("Sequence length before merging converge part: " + seq.size()); List sequenceStates = viterbi.getSequenceStates(); + int size = sequenceStates.size(); + // Record converged sequence. - convergedSequence.addAll(sequenceStates.subList(convergeStartIndex, sequenceStates.size())); + convergedSequence.addAll(sequenceStates.subList(convergeStartIndex, size - 1)); - int size = sequenceStates.size(); System.out.println("Local sequence length: " + size); - System.out.println("Insert position: " + viterbi.insertPosition); + System.out.println("Initial start index: " + viterbi.insertPosition); System.out.println("Converge start index: " + convergeStartIndex); // 之前算法没有发生过中断,第一次收敛的序列从[index==0]开始复制(初始化的元素需要添加) @@ -353,26 +353,40 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque int isBrokenBefore = viterbi.isBrokenBefore ? 1 : 0; convergeStartIndex = viterbi.isConvergedBefore() ? convergeStartIndex : isBrokenBefore; - for (int i = convergeStartIndex; i < size; i++) { + // The time difference from the start of the current convergent sequence to the left boundary of the window. + int lengthDelta = viterbi.isConvergedBefore() ? viterbi.timeDelta - 1: viterbi.timeDelta; + int offset = Math.max(lengthDelta - viterbi.windowSize, 0); + + System.out.println("Time delta: " + viterbi.timeDelta); + System.out.println("Length delta: " + lengthDelta); + System.out.println("Insert offset: " + offset); + System.out.println("Sequence size: " + size); + + for (int i = convergeStartIndex; i < size - 1; i++) { // 算法中断前,从索引0开始复制,无需减1 // 算法中断后,从索引1开始复制,需要减1 - int insertPosition = viterbi.isBrokenBefore ? i + viterbi.insertPosition - 1 : i + viterbi.insertPosition; - if (i == convergeStartIndex) System.out.println("insert position: " + insertPosition); - seq.set(insertPosition, sequenceStates.get(i)); + int insertIndex = viterbi.isBrokenBefore ? i + viterbi.insertPosition - 1: i + viterbi.insertPosition; + insertIndex += offset; + if (i == convergeStartIndex) System.out.println("Insert start index: " + insertIndex); + seq.set(insertIndex, sequenceStates.get(i)); } + // Update offset +// viterbi.preInsertPosition += size - convergeStartIndex; // Reset convergence state until the next convergence occurs. viterbi.isConverge = false; System.out.println("Sequence length after merging converge part: " + seq.size()); } - +// if (seq.size() == 206) { +// System.out.println(206); +// } // Find the candidate point with the maximum probability and add to the sequence. CandidatePoint maxPoint = StreamMapMatcher.findMaxValuePoint(viterbi.message); seq.add(new SequenceState(maxPoint, point)); } System.out.println("After add current time point, sequence length: " + seq.size()); - System.out.println("################################"); + System.out.println("======================================================"); preTimeStep = currentTimeStep; } diff --git a/src/main/java/org/urbcomp/cupid/db/util/GeoJSONParser.java b/src/main/java/org/urbcomp/cupid/db/util/GeoJSONParser.java new file mode 100644 index 0000000..958cae9 --- /dev/null +++ b/src/main/java/org/urbcomp/cupid/db/util/GeoJSONParser.java @@ -0,0 +1,94 @@ +package org.urbcomp.cupid.db.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.urbcomp.cupid.db.model.point.GPSPoint; +import org.urbcomp.cupid.db.model.point.CandidatePoint; + +import java.io.File; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +public class GeoJSONParser { + /** + * Parses the raw points (latitude, longitude) from the GeoJSON string. + * + * @param geoJsonPath Path to the GeoJSON file. + * @return A list of raw GPS points extracted from the GeoJSON. + * @throws Exception In case of parsing errors. + */ + public static List parseRawPointsFromGeoJSON(String geoJsonPath) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(new File(geoJsonPath)); + + List rawPoints = new ArrayList<>(); + + // Iterate through the features array + JsonNode features = root.get("features"); + for (JsonNode feature : features) { + JsonNode properties = feature.get("properties"); + + // Extract rawLat, rawLon, and time from properties + double rawLat = properties.get("rawLat").asDouble(); + double rawLon = properties.get("rawLon").asDouble(); + String timeString = properties.get("time").asText(); + Timestamp timestamp = parseTimestamp(timeString); + + GPSPoint rawPoint = new GPSPoint(timestamp, rawLon, rawLat); + rawPoints.add(rawPoint); + } + + return rawPoints; + } + + /** + * Parses the candidate points (latitude, longitude) from the GeoJSON string. + * + * @param geoJsonPath Path to the GeoJSON file. + * @return A list of candidate points extracted from the GeoJSON. + * @throws Exception In case of parsing errors. + */ + public static List parseCandidatePointsFromGeoJSON(String geoJsonPath) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(new File(geoJsonPath)); + + List candidatePoints = new ArrayList<>(); + + // Iterate through the features array + JsonNode features = root.get("features"); + for (JsonNode feature : features) { + JsonNode properties = feature.get("properties"); + JsonNode geometry = feature.get("geometry"); + JsonNode coordinates = geometry.get("coordinates"); + + // Extract candidate point's longitude and latitude from coordinates + double errorDistance = properties.get("errorDistanceInMeter").asDouble(); + double offsetInMeter = properties.get("offsetInMeter").asDouble(); + int matchedIndex = properties.get("matchedIndex").asInt(); + int roadSegmentId = properties.get("roadSegmentId").asInt(); + + double candidateLon = coordinates.get(0).asDouble(); + double candidateLat = coordinates.get(1).asDouble(); + + CandidatePoint candidatePoint = new CandidatePoint(candidateLon, candidateLat, roadSegmentId, matchedIndex, errorDistance, offsetInMeter); + candidatePoints.add(candidatePoint); + } + + return candidatePoints; + } + + /** + * Converts a time string to a Timestamp object. + * + * @param timeString The time string to convert. + * @return The corresponding Timestamp object. + * @throws ParseException If the time string is not in the expected format. + */ + private static Timestamp parseTimestamp(String timeString) throws ParseException { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S"); + return new Timestamp(dateFormat.parse(timeString).getTime()); + } +} diff --git a/src/main/resources/weight2.txt b/src/main/resources/weight2.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/java/OnlineMapMatcherTest.java b/src/test/java/OnlineMapMatcherTest.java index 10bd7f2..89141f8 100644 --- a/src/test/java/OnlineMapMatcherTest.java +++ b/src/test/java/OnlineMapMatcherTest.java @@ -20,11 +20,9 @@ import org.urbcomp.cupid.db.util.GeoJSONParser; import java.io.*; -import java.text.ParseException; import java.util.List; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; /** * Test class for the OnlineMapMatcher functionality, including tests for @@ -60,8 +58,9 @@ public void setUp() { */ @Test public void matchSingleTrajectory() throws AlgorithmExecuteException, IOException { + int windowSize = 10; trajectory = ModelGenerator.generateTrajectory(6); - MapMatchedTrajectory mmTrajectory = streamMapMatcher.onlineStreamMapMatch(trajectory); + MapMatchedTrajectory mmTrajectory = streamMapMatcher.onlineStreamMapMatch(trajectory, windowSize); System.out.println(trajectory.toGeoJSON()); System.out.println(mmTrajectory.toGeoJSON()); @@ -82,6 +81,7 @@ public void matchSingleTrajectory() throws AlgorithmExecuteException, IOExceptio */ @Test public void matchMultiTrajectory() throws AlgorithmExecuteException { + int windowSize = 10; for (int i = 1; i <= 10; i++) { System.out.println("------------------------------"); System.out.println("index: " + i); @@ -89,7 +89,7 @@ public void matchMultiTrajectory() throws AlgorithmExecuteException { trajectory = ModelGenerator.generateTrajectory(i); MapMatchedTrajectory mmTrajectory = streamMapMatcher.streamMapMatch(trajectory); - MapMatchedTrajectory onlineMMTrajectory = streamMapMatcher.onlineStreamMapMatch(trajectory); + MapMatchedTrajectory onlineMMTrajectory = streamMapMatcher.onlineStreamMapMatch(trajectory, windowSize); List mmPtList = mmTrajectory.getMmPtList(); List onlineMMPtList = onlineMMTrajectory.getMmPtList(); @@ -106,38 +106,42 @@ public void matchMultiTrajectory() throws AlgorithmExecuteException { */ @Test public void onlineMatchAccuracy() throws AlgorithmExecuteException, IOException { - int testNum = 6; + int testNum = 3; int sampleRate = 0; - for (int i = 6; i <= testNum; i++) { - System.out.println("==========================="); + int windowSize = 50; + + for (int i = 1; i <= testNum; i++) { + System.out.println("*********************************************************"); System.out.println("index: " + i); - System.out.println("==========================="); + System.out.println("*********************************************************"); + System.out.println(); + trajectory = ModelGenerator.generateTrajectory(i); Trajectory sampledTrajectory = ModelGenerator.generateTrajectory(i, sampleRate); assert sampledTrajectory != null; MapMatchedTrajectory baseMapMatchedTrajectory = baseMapMatcher.mapMatch(trajectory); - MapMatchedTrajectory streamOnlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory); + MapMatchedTrajectory streamOnlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory, windowSize); assert baseMapMatchedTrajectory.getMmPtList().size() == streamOnlineMapMatchedTrajectory.getMmPtList().size(); EvaluateUtils.getAccuracy(baseMapMatchedTrajectory, streamOnlineMapMatchedTrajectory, sampleRate); - System.out.println("==========================="); + System.out.println("*********************************************************"); System.out.println("results: "); System.out.println("currAcc: " + EvaluateUtils.getCurrAcc()); System.out.println("totalAcc: " + EvaluateUtils.getTotalAcc()); System.out.println("pointNum: " + EvaluateUtils.getTotalNum()); - System.out.println("==========================="); + System.out.println("*********************************************************"); System.out.println(); - String outputFile = "trajectory_" + i + ".geojson"; - BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile)); - writer.write(baseMapMatchedTrajectory.toGeoJSON(true)); - writer.flush(); - writer.close(); +// String outputFile = "trajectory_" + i + ".geojson"; +// BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile)); +// writer.write(baseMapMatchedTrajectory.toGeoJSON(true)); +// writer.flush(); +// writer.close(); } } @@ -204,6 +208,7 @@ public void testNoOnlineStreamMatch() throws AlgorithmExecuteException { public void testOnlineStreamMatch() throws AlgorithmExecuteException { int testNum = 100; int sampleRate = 0; + int windowSize = 10; // Create CSV file for online results try (PrintWriter onlineWriter = new PrintWriter(new FileWriter("onlineStreamResult.csv"))) { @@ -224,7 +229,7 @@ public void testOnlineStreamMatch() throws AlgorithmExecuteException { // Match using baseMatch MapMatchedTrajectory baseMapMatchedTrajectory = baseMapMatcher.mapMatch(trajectory); // Match using onlineStreamMatch - MapMatchedTrajectory onlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory); + MapMatchedTrajectory onlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory, windowSize); // Record number of matched points int onlineCurrPointNum = onlineMapMatchedTrajectory.getMmPtList().size(); @@ -253,11 +258,12 @@ public void testOnlineStreamMatch() throws AlgorithmExecuteException { @Test public void testConvergedSequenceAccuracy() throws Exception { - int testNum = 6; // Number of trajectories to test + int testNum = 1; // Number of trajectories to test int sampleRate = 0; // Sample rate for trajectory generation + int windowSize = 10; double epsilon = 1e-6; // Allowable error for latitude/longitude comparison - for (int i = 6; i <= testNum; i++) { + for (int i = 1; i <= testNum; i++) { System.out.println("==========================="); System.out.println("Testing trajectory index: " + i); System.out.println("==========================="); @@ -269,22 +275,15 @@ public void testConvergedSequenceAccuracy() throws Exception { assert sampledTrajectory != null; // Perform online map matching - MapMatchedTrajectory streamOnlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory); + MapMatchedTrajectory streamOnlineMapMatchedTrajectory = streamMapMatcher.onlineStreamMapMatch(sampledTrajectory, windowSize); // Get converged sequence from the streamMapMatcher List convergedSequence = streamMapMatcher.convergedSequence; - // Load the corresponding GeoJSON file (assumed to be pre-generated and available in the path) + // Load the corresponding GeoJSON file String geojsonFilePath = "trajectory_" + i + ".geojson"; - BufferedReader geojsonReader = new BufferedReader(new FileReader(geojsonFilePath)); - StringBuilder geojsonContent = new StringBuilder(); - String line; - while ((line = geojsonReader.readLine()) != null) { - geojsonContent.append(line); - } - geojsonReader.close(); - // Parse the GeoJSON to extract rawPoints (assuming a utility class GeoJSONParser exists) + // Parse the GeoJSON to extract rawPoints List rawPointsFromGeoJSON = GeoJSONParser.parseRawPointsFromGeoJSON(geojsonFilePath); List candidatePointsFromGeoJSON = GeoJSONParser.parseCandidatePointsFromGeoJSON(geojsonFilePath); @@ -292,34 +291,67 @@ public void testConvergedSequenceAccuracy() throws Exception { System.out.println("Converged sequence size: " + convergedSequence.size()); System.out.println("Global sequence size: " + rawPointsFromGeoJSON.size()); - // Compare each point in the converged sequence with the corresponding points in the GeoJSON file - for (int j = 0; j < convergedSequence.size(); j++) { - SequenceState sequenceState = convergedSequence.get(j); + // Use two pointers to compare raw points + int seqPointer = 0; + int geoPointer = 0; + + while (seqPointer < convergedSequence.size() && geoPointer < rawPointsFromGeoJSON.size()) { + SequenceState sequenceState = convergedSequence.get(seqPointer); GPSPoint rawPointFromSequence = sequenceState.getObservation(); CandidatePoint candidatePointFromSequence = sequenceState.getState(); - GPSPoint rawPointFromGeoJSON = rawPointsFromGeoJSON.get(j); - CandidatePoint candidatePointFromGeoJSON = candidatePointsFromGeoJSON.get(j); - System.out.println("The " + j + "-th point"); - System.out.println("rawPointFromSequence:" + rawPointFromSequence.getLat() + "," + rawPointFromSequence.getLng()); - System.out.println("rawPointFromGeoJSON:" + rawPointFromGeoJSON.getLat() + "," + rawPointFromGeoJSON.getLng()); + GPSPoint rawPointFromGeoJSON = rawPointsFromGeoJSON.get(geoPointer); + CandidatePoint candidatePointFromGeoJSON = candidatePointsFromGeoJSON.get(geoPointer); // Compare raw points' latitude and longitude - assertTrue(Math.abs(rawPointFromSequence.getLat() - rawPointFromGeoJSON.getLat()) < epsilon); - assertTrue(Math.abs(rawPointFromSequence.getLng() - rawPointFromGeoJSON.getLng()) < epsilon); - - // Compare candidate points' latitude and longitude -// if (candidatePointFromSequence != null && candidatePointFromGeoJSON != null) { -// assertTrue(Math.abs(candidatePointFromSequence.getX() - candidatePointFromGeoJSON.getX()) < epsilon); -// assertTrue(Math.abs(candidatePointFromSequence.getY() - candidatePointFromGeoJSON.getY()) < epsilon); -// } else { -// // Both candidate points must be null -// assertEquals(candidatePointFromSequence, candidatePointFromGeoJSON); -// } + if (Math.abs(rawPointFromSequence.getLat() - rawPointFromGeoJSON.getLat()) < epsilon && + Math.abs(rawPointFromSequence.getLng() - rawPointFromGeoJSON.getLng()) < epsilon) { + + // If raw points match, compare the candidate points + boolean candidateMatch = false; + while (geoPointer < candidatePointsFromGeoJSON.size()) { + candidatePointFromGeoJSON = candidatePointsFromGeoJSON.get(geoPointer); + + if (candidatePointFromSequence != null && candidatePointFromGeoJSON != null) { + // Compare candidate points + if (Math.abs(candidatePointFromSequence.getLat() - candidatePointFromGeoJSON.getLat()) < epsilon && + Math.abs(candidatePointFromSequence.getLng() - candidatePointFromGeoJSON.getLng()) < epsilon) { + candidateMatch = true; + break; + } + } else if (candidatePointFromSequence == null && candidatePointFromGeoJSON == null) { + // Both candidate points are null + candidateMatch = true; + break; + } + + // If the candidate points do not match, move the geoPointer forward + geoPointer++; + } + + // If a matching candidate point is found, move both pointers forward + if (candidateMatch) { + seqPointer++; + geoPointer++; + } else { + // No matching candidate point found for this GPS point + throw new AssertionError("Candidate point for GPS point at index " + seqPointer + " does not match."); + } + } else { + // If raw points do not match, move the geoPointer forward + geoPointer++; + } } - System.out.println("Trajectory " + i + " passed the accuracy test."); + // If all sequence points have been matched, the test passes + if (seqPointer == convergedSequence.size()) { + System.out.println("Trajectory " + i + " passed the accuracy test."); + } else { + System.out.println("Trajectory " + i + " did not pass the accuracy test."); + throw new AssertionError("Converged sequence does not match the GeoJSON data."); + } } } + }