From 7f733e413384a29d96eea81f7ed80d0204b3871b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 3 Feb 2025 18:46:01 +0100 Subject: [PATCH] bug6076: fixing OutlierFilter, recognize Vakaros timestamp headers --- .../VakarosExtendedDataImporterImpl.java | 22 +++++++++++++++++++ .../vakarosadapter/VakarosGPSFixImporter.java | 5 ++++- .../domain/tracking/impl/OutlierFilter.java | 11 +++++----- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosExtendedDataImporterImpl.java b/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosExtendedDataImporterImpl.java index d1ba62a551f..bdfb1799b18 100644 --- a/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosExtendedDataImporterImpl.java +++ b/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosExtendedDataImporterImpl.java @@ -4,11 +4,16 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.sap.sailing.domain.trackimport.FormatNotSupportedException; import com.sap.sailing.server.trackfiles.impl.ExpeditionExtendedDataImporterImpl; import com.sap.sse.common.TimePoint; public class VakarosExtendedDataImporterImpl extends ExpeditionExtendedDataImporterImpl { + private static final Logger logger = Logger.getLogger(VakarosExtendedDataImporterImpl.class.getName()); + private static final DateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); private static final String VAKAROS_EXTENDED = "VAKAROS_EXTENDED"; @@ -22,4 +27,21 @@ public class VakarosExtendedDataImporterImpl extends ExpeditionExtendedDataImpor return TimePoint.of(timestampFormat .parse(lineContentTokens[columnsInFileFromHeader.get(VakarosGPSFixImporter.TIMESTAMP_COLUMN_HEADING)]+"Z")); } + + /** + * Ensures that all columns in + * {@link #columnNamesInFileAndTheirValueIndexInResultingDoubleVectorFix}'s + * key set are present in {@code colIndicesInFile}'s key set. If not, an + * exception is thrown that reports the columns missing. + */ + @Override + public void validateHeader(Map colIndicesInFile) throws FormatNotSupportedException { + final boolean dateTimeFormatOk; + dateTimeFormatOk = colIndicesInFile.containsKey(VakarosGPSFixImporter.TIMESTAMP_COLUMN_HEADING); + if (!dateTimeFormatOk) { + final String msg = "Missing timestamp header; expected "+VakarosGPSFixImporter.TIMESTAMP_COLUMN_HEADING; + logger.log(Level.SEVERE, msg); + throw new FormatNotSupportedException(msg); + } + } } diff --git a/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosGPSFixImporter.java b/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosGPSFixImporter.java index f1257348144..335cbcc6d02 100644 --- a/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosGPSFixImporter.java +++ b/java/com.sap.sailing.domain.vakarosadapter/src/com/sap/sailing/domain/vakarosadapter/VakarosGPSFixImporter.java @@ -8,6 +8,7 @@ import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -35,6 +36,8 @@ public class VakarosGPSFixImporter implements GPSFixImporter { private static final Logger logger = Logger.getLogger(VakarosGPSFixImporter.class.getName()); + private static final List supportedExpeditionLogFileExtensions = Arrays.asList("csv", "vak", "zip", "gz"); + static final String TIMESTAMP_COLUMN_HEADING = "timestamp"; private static final String LAT_COLUMN_HEADING = "latitude"; private static final String LON_COLUMN_HEADING = "longitude"; @@ -129,7 +132,7 @@ public class VakarosGPSFixImporter implements GPSFixImporter { @Override public Iterable getSupportedFileExtensions() { - return ExpeditionImportFileHandler.supportedExpeditionLogFileExtensions; + return supportedExpeditionLogFileExtensions; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/OutlierFilter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/OutlierFilter.java index 4c64df86136..9bf20158279 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/OutlierFilter.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/OutlierFilter.java @@ -116,7 +116,7 @@ public class OutlierFilter { */ private Pair isLikelyOutlierWithCorrectableTimepoint(DynamicGPSFixTrack track, GPSFixMoving previous, GPSFixMoving fix, GPSFixMoving next) { - final int HOW_MANY_CRITERIA_TO_FULFILL = 3; + final int HOW_MANY_CRITERIA_TO_FULFILL = 2; final double DISTANCE_RATIO_TOLERANCE = 0.5; // ratio between cross-track distance and length of closest segment final Pair adjustedFixAndDistance; int criteriaFulfilled = 0; @@ -129,7 +129,7 @@ public class OutlierFilter { if (hasInconsistentCogSog(previous, fix, next, /* speed ratio tolerance */ 0.1, /* course degree tolerance */ 10)) { criteriaFulfilled++; } - if (criteriaFulfilled >= HOW_MANY_CRITERIA_TO_FULFILL-1) { + if (criteriaFulfilled >= HOW_MANY_CRITERIA_TO_FULFILL) { final Pair adjusted = adjust(previous, fix, track); if (adjusted.getB() > DISTANCE_RATIO_TOLERANCE) { adjustedFixAndDistance = null; @@ -140,7 +140,7 @@ public class OutlierFilter { } else { adjustedFixAndDistance = null; } - assert criteriaFulfilled >= 3 || adjustedFixAndDistance == null; + assert criteriaFulfilled >= HOW_MANY_CRITERIA_TO_FULFILL || adjustedFixAndDistance == null; return adjustedFixAndDistance; } @@ -225,10 +225,11 @@ public class OutlierFilter { final Distance alongTrackDistanceFromLastFix = fixPosition.alongTrackDistance(lastFix.getPosition(), bearingFromLastToCurrent); // interpolate the time between the adjacent fixes to whose connection "fix" is closest, splitting the duration // between the adjacent fixes proportionately based on "fix"'s distances to each of the two adjacent fixes: + final Distance distanceFromLastFixToCurrentFix = lastFix.getPosition().getDistance(currentFix.getPosition()); final TimePoint inferredTimePointForFix = lastFix.getTimePoint().plus(lastFix.getTimePoint().until(currentFix.getTimePoint()).times( - alongTrackDistanceFromLastFix.divide(lastFix.getPosition().getDistance(currentFix.getPosition())))); + distanceFromLastFixToCurrentFix.equals(Distance.NULL) ? 0.5 : alongTrackDistanceFromLastFix.divide(distanceFromLastFixToCurrentFix))); result = new GPSFixMovingImpl(fixPosition, inferredTimePointForFix, fix.getSpeed(), fix.getOptionalTrueHeading()); - distanceRatio = distanceFromSegment.divide(lastFix.getPosition().getDistance(currentFix.getPosition())); + distanceRatio = distanceFromSegment.divide(distanceFromLastFixToCurrentFix); } else { // we found a minimum after fix: foundMinimum = true; }