bug6076: fixing OutlierFilter, recognize Vakaros timestamp headers

This commit is contained in:
Axel Uhl
2025-02-03 18:46:01 +01:00
parent d814eed6e1
commit 7f733e4133
3 changed files with 32 additions and 6 deletions
@@ -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<String, Integer> 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);
}
}
}
@@ -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<String> 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<String> getSupportedFileExtensions() {
return ExpeditionImportFileHandler.supportedExpeditionLogFileExtensions;
return supportedExpeditionLogFileExtensions;
}
@Override
@@ -116,7 +116,7 @@ public class OutlierFilter {
*/
private Pair<GPSFixMoving, Double> isLikelyOutlierWithCorrectableTimepoint(DynamicGPSFixTrack<Competitor, GPSFixMoving> 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<GPSFixMoving, Double> 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<GPSFixMoving, Double> 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;
}