From a70af40128150e62a8c0e90fd601a7243ccd54fc Mon Sep 17 00:00:00 2001 From: t1anyu Date: Mon, 14 Oct 2024 21:59:04 +0800 Subject: [PATCH 1/2] Added backtracking length limit. --- .../mapmatch/onlinemm/OnlineViterbi.java | 15 ++- .../mapmatch/stream/StreamMapMatcher.java | 19 ++-- .../urbcomp/cupid/db/util/GeoJSONParser.java | 94 +++++++++++++++++++ src/test/java/OnlineMapMatcherTest.java | 94 ++++++++++++------- 4 files changed, 175 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/urbcomp/cupid/db/util/GeoJSONParser.java 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..7076721 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 @@ -105,7 +105,8 @@ public void nextStep( List candidates, Map emissionLogProbabilities, Map, Double> transitionLogProbabilities, - int time + int time, + int limit ) { if (message == null) throw new IllegalStateException("start with initial observation() must be called first."); if (isBroken) throw new IllegalStateException("Method must not be called after an HMM break."); @@ -118,7 +119,8 @@ public void nextStep( message, emissionLogProbabilities, transitionLogProbabilities, - time + time, + limit ); isBroken = hmmBreak(forwardStepResult.getNewMessage()); @@ -152,7 +154,8 @@ protected ForwardStepResult forwardStep( Map message, Map emissionLogProbabilities, Map, Double> transitionLogProbabilities, - int time + int time, + int limit ) { assert !prevCandidates.isEmpty(); @@ -266,7 +269,7 @@ else if (time > 1) { // Check for convergence point and record local solutions if (searchForNewRoot()) { isConverge = true; - traceback(); + traceback(limit); } } @@ -391,12 +394,14 @@ private boolean searchForNewRoot() { * Performs traceback to find the local path from the * current root state to the previous root state. */ - private void traceback() { + private void traceback(int limit) { System.out.println("Local path found!"); List interLocalPath = new ArrayList<>(); interLocalPath.add(new SequenceState(currentRoot.getState(), currentRoot.getObservation())); int depth = isConvergedBefore() ? currentRoot.getTime() - previousRoot.getTime() - 1 : currentRoot.getTime(); + depth = Math.min(depth, limit); + System.out.println("current root time: " + currentRoot.getTime()); ExtendedState current = currentRoot.getBackPointer(); 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..c8da4d1 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 @@ -102,7 +102,7 @@ public MapMatchedTrajectory streamMapMatch(Trajectory trajectory) throws Algorit for (GPSPoint gpsPoint : trajectory.getGPSPointList()) { Tuple3, TimeStep, TiViterbi> result = - this.computeViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi); + this.computeOnlineViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi); sequence = result._1(); previousTimeStep = result._2(); @@ -136,6 +136,7 @@ public MapMatchedTrajectory onlineStreamMapMatch(Trajectory trajectory) throws A OnlineViterbi viterbi = new OnlineViterbi(); int currentTime = 0; + int limit = 10; int trajectorySize = trajectory.getGPSPointList().size(); for (GPSPoint gpsPoint : trajectory.getGPSPointList()) { @@ -148,7 +149,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, limit); sequence = result._1(); previousTimeStep = result._2(); @@ -246,12 +247,13 @@ 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, OnlineViterbi viterbi, - int time + int time, + int limit ) { System.out.println("current time: " + time); windowBearing.addPoint(point); @@ -294,7 +296,8 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque currentTimeStep.getCandidates(), currentTimeStep.getEmissionLogProbabilities(), currentTimeStep.getTransitionLogProbabilities(), - time + time, + limit ); } else { @@ -309,7 +312,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()); // @@ -365,7 +368,9 @@ private Tuple3, TimeStep, OnlineViterbi> computeViterbiSeque 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)); 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/test/java/OnlineMapMatcherTest.java b/src/test/java/OnlineMapMatcherTest.java index 10bd7f2..6a4a015 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 @@ -106,9 +104,9 @@ public void matchMultiTrajectory() throws AlgorithmExecuteException { */ @Test public void onlineMatchAccuracy() throws AlgorithmExecuteException, IOException { - int testNum = 6; + int testNum = 100; int sampleRate = 0; - for (int i = 6; i <= testNum; i++) { + for (int i = 1; i <= testNum; i++) { System.out.println("==========================="); System.out.println("index: " + i); System.out.println("==========================="); @@ -253,11 +251,11 @@ 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 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("==========================="); @@ -274,17 +272,10 @@ public void testConvergedSequenceAccuracy() throws Exception { // 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 +283,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."); + } } } + } From 3aac278ff578c7d113bc062716a24c72a514dc7c Mon Sep 17 00:00:00 2001 From: t1anyu Date: Tue, 15 Oct 2024 16:56:47 +0800 Subject: [PATCH 2/2] Introducing dynamic windows to online viterbi backtracking. --- .../mapmatch/onlinemm/OnlineViterbi.java | 84 +++++++++++++------ .../mapmatch/stream/StreamMapMatcher.java | 55 +++++++----- src/main/resources/weight2.txt | 0 src/test/java/OnlineMapMatcherTest.java | 38 +++++---- 4 files changed, 113 insertions(+), 64 deletions(-) delete mode 100644 src/main/resources/weight2.txt 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 7076721..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,18 +120,17 @@ 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, List candidates, Map emissionLogProbabilities, Map, Double> transitionLogProbabilities, - int time, - int limit + int time ) { if (message == null) throw new IllegalStateException("start with initial observation() must be called first."); if (isBroken) throw new IllegalStateException("Method must not be called after an HMM break."); @@ -119,8 +143,7 @@ public void nextStep( message, emissionLogProbabilities, transitionLogProbabilities, - time, - limit + time ); isBroken = hmmBreak(forwardStepResult.getNewMessage()); @@ -143,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. */ @@ -154,8 +177,7 @@ protected ForwardStepResult forwardStep( Map message, Map emissionLogProbabilities, Map, Double> transitionLogProbabilities, - int time, - int limit + int time ) { assert !prevCandidates.isEmpty(); @@ -262,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(limit); + 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(); + } } } @@ -362,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; @@ -373,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(); @@ -381,6 +409,7 @@ private boolean searchForNewRoot() { else { previousRoot = currentRoot; currentRoot = ancestor; + lastCompressTime = ancestor.getTime(); return true; } } @@ -394,15 +423,18 @@ private boolean searchForNewRoot() { * Performs traceback to find the local path from the * current root state to the previous root state. */ - private void traceback(int limit) { + private void traceback() { System.out.println("Local path found!"); List interLocalPath = new ArrayList<>(); interLocalPath.add(new SequenceState(currentRoot.getState(), currentRoot.getObservation())); int depth = isConvergedBefore() ? currentRoot.getTime() - previousRoot.getTime() - 1 : currentRoot.getTime(); - depth = Math.min(depth, limit); + + 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++) { @@ -410,7 +442,7 @@ private void traceback(int limit) { 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 c8da4d1..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 @@ -102,7 +102,7 @@ public MapMatchedTrajectory streamMapMatch(Trajectory trajectory) throws Algorit for (GPSPoint gpsPoint : trajectory.getGPSPointList()) { Tuple3, TimeStep, TiViterbi> result = - this.computeOnlineViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi); + this.computeViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi); sequence = result._1(); previousTimeStep = result._2(); @@ -129,14 +129,13 @@ 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 limit = 10; int trajectorySize = trajectory.getGPSPointList().size(); for (GPSPoint gpsPoint : trajectory.getGPSPointList()) { @@ -149,7 +148,7 @@ public MapMatchedTrajectory onlineStreamMapMatch(Trajectory trajectory) throws A currentTime = (viterbi.message == null) ? 0 : 1; } - result = this.computeOnlineViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi, currentTime, limit); + result = this.computeOnlineViterbiSequence(gpsPoint, sequence, previousTimeStep, viterbi, currentTime); sequence = result._1(); previousTimeStep = result._2(); @@ -252,13 +251,11 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb List seq, TimeStep preTimeStep, OnlineViterbi viterbi, - int time, - int limit + int time ) { 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) { @@ -274,7 +271,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb // 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) { @@ -296,8 +293,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb currentTimeStep.getCandidates(), currentTimeStep.getEmissionLogProbabilities(), currentTimeStep.getTransitionLogProbabilities(), - time, - limit + time ); } else { @@ -328,7 +324,7 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb 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(), @@ -342,12 +338,13 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb 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]开始复制(初始化的元素需要添加) @@ -356,28 +353,40 @@ private Tuple3, TimeStep, OnlineViterbi> computeOnlineViterb 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); - } +// 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/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 6a4a015..89141f8 100644 --- a/src/test/java/OnlineMapMatcherTest.java +++ b/src/test/java/OnlineMapMatcherTest.java @@ -58,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()); @@ -80,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); @@ -87,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(); @@ -104,38 +106,42 @@ public void matchMultiTrajectory() throws AlgorithmExecuteException { */ @Test public void onlineMatchAccuracy() throws AlgorithmExecuteException, IOException { - int testNum = 100; + int testNum = 3; int sampleRate = 0; + int windowSize = 50; + for (int i = 1; i <= testNum; i++) { - System.out.println("==========================="); + 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(); } } @@ -202,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"))) { @@ -222,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,6 +260,7 @@ public void testOnlineStreamMatch() throws AlgorithmExecuteException { public void testConvergedSequenceAccuracy() throws Exception { 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 = 1; i <= testNum; i++) { @@ -267,7 +275,7 @@ 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;