Move .merge() from SegmentStatisticUpdater to Statistics.

This is a functional change as maxSpeed is now determined by Segment (rather than the joined Segment).
This commit is contained in:
Dennis Guse
2025-11-20 22:55:46 +01:00
parent 94a0b57362
commit 1ef7a6ecc4
6 changed files with 146 additions and 206 deletions
@@ -1,9 +1,11 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
//TODO Add @NonNull to attributes
public record Statistics(
@@ -54,4 +56,73 @@ public record Statistics(
if (altitudeExtremities == null) return 0;
return altitudeExtremities.max_m();
}
/**
* Combines these statistics with those from another object.
* This assumes that the time periods covered by each do not intersect.
*/
//TODO Should be refactored to append only
@NonNull
public Statistics merge(@NonNull Statistics other) {
HeartRate newAvgHeartRate = avgHeartRate;
if (avgHeartRate == null) {
newAvgHeartRate = other.avgHeartRate;
} else {
if (other.avgHeartRate != null) {
// Using total time as weights for the averaging.
// Important to do this before total time is updated
newAvgHeartRate = HeartRate.of(
(totalDuration.getSeconds() * avgHeartRate.getBPM() + other.totalDuration.getSeconds() * other.avgHeartRate.getBPM())
/ (totalDuration.getSeconds() + other.totalDuration.getSeconds())
);
}
}
Power newAvgPower = avgPower;
if (avgPower == null) {
newAvgPower = other.avgPower;
} else {
if (other.avgPower != null) {
// Using total time as weights for the averaging.
// Important to do this before total time is updated
newAvgPower = Power.of(
(totalDuration.getSeconds() * avgPower.getW() + other.totalDuration.getSeconds() * other.avgPower.getW())
/ (totalDuration.getSeconds() + other.totalDuration.getSeconds())
);
}
}
AltitudeExtremities newAltitudeExtremities;
if (altitudeExtremities != null && other.altitudeExtremities != null) {
newAltitudeExtremities = new AltitudeExtremities(
Math.min(altitudeExtremities.min_m(), other.altitudeExtremities.min_m()),
Math.max(altitudeExtremities.max_m(), other.altitudeExtremities.max_m())
);
} else {
newAltitudeExtremities = altitudeExtremities != null ? altitudeExtremities : other.altitudeExtremities;
}
AltitudeGainLoss newAltitudeGainLoss;
if (altitudeGainLoss != null && other.altitudeGainLoss != null) {
newAltitudeGainLoss = new AltitudeGainLoss(
altitudeGainLoss.gain_m() + other.altitudeGainLoss.gain_m(),
altitudeGainLoss.loss_m() + other.altitudeGainLoss.loss_m()
);
} else {
newAltitudeGainLoss = altitudeGainLoss != null ? altitudeGainLoss : other.altitudeGainLoss;
}
return new Statistics(
startTime == null ? other.startTime : startTime.isBefore(other.startTime) ? startTime : other.startTime,
stopTime == null ? other.stopTime : stopTime.isAfter(other.stopTime) ? stopTime : other.stopTime,
totalDuration.plus(other.totalDuration),
movingDuration.plus(other.movingDuration),
totalDistance.plus(other.totalDistance),
Speed.max(maxSpeed, other.maxSpeed),
newAltitudeExtremities,
newAltitudeGainLoss,
newAvgHeartRate,
newAvgPower
);
}
}