Package structure: moved models and database.

Part of #963.
This commit is contained in:
Dennis Guse
2022-01-15 21:46:23 +01:00
parent cf993b73d7
commit 0645628d7e
134 changed files with 491 additions and 511 deletions
@@ -0,0 +1,70 @@
package de.dennisguse.opentracks.data.models;
import de.dennisguse.opentracks.R;
public abstract class Altitude {
private final double altitude_m;
private Altitude(double altitude_m) {
this.altitude_m = altitude_m;
}
public double toM() {
return altitude_m;
}
public abstract Altitude replace(double altitude_m);
public abstract int getLabelId();
public static class WGS84 extends Altitude {
private WGS84(double altitude_m) {
super(altitude_m);
}
@Override
public int getLabelId() {
return R.string.wgs84;
}
public static Altitude of(double altitude_m) {
return new WGS84(altitude_m);
}
@Override
public Altitude replace(double altitude_m) {
return new WGS84(altitude_m);
}
}
public static class EGM2008 extends Altitude {
private EGM2008(double altitude_m) {
super(altitude_m);
}
@Override
public int getLabelId() {
return R.string.egm2008;
}
public static Altitude of(double altitude_m) {
return new EGM2008(altitude_m);
}
@Override
public Altitude replace(double altitude_m) {
return new EGM2008(altitude_m);
}
}
@Override
public String toString() {
return "Altitude{" +
"altitude_m=" + altitude_m + this.getClass().getSimpleName() +
'}';
}
}
@@ -0,0 +1,131 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import java.util.Objects;
public class Distance {
public static Distance of(double distance_m) {
return new Distance(distance_m);
}
public static Distance of(String distance_m) {
return of(Float.parseFloat(distance_m));
}
public static Distance ofMile(double distance_mile) {
return of(distance_mile * UnitConversions.MI_TO_M);
}
public static Distance ofKilometer(double distance_km) {
return of(distance_km * UnitConversions.KM_TO_M);
}
public static Distance ofMM(double distance_mm) {
return of(0.001 * distance_mm);
}
public static Distance ofCM(double distance_cm) {
return of(0.01 * distance_cm);
}
public static Distance ofDM(double distance_dm) {
return of(0.1 * distance_dm);
}
public static Distance one(boolean metricUnit) {
if (metricUnit) {
return Distance.ofKilometer(1);
} else {
return Distance.ofMile(1);
}
}
private final double distance_m;
private Distance(double distance_m) {
this.distance_m = distance_m;
}
public Distance plus(@NonNull Distance distance) {
return new Distance(distance_m + distance.distance_m);
}
public Distance minus(@NonNull Distance distance) {
return new Distance(distance_m - distance.distance_m);
}
public Distance multipliedBy(double factor) {
return new Distance(factor * distance_m);
}
public double dividedBy(@NonNull Distance divisor) {
return distance_m / divisor.distance_m;
}
public boolean isZero() {
return distance_m == 0;
}
public boolean isInvalid() {
return Double.isNaN(distance_m) || Double.isInfinite(distance_m);
}
public boolean lessThan(@NonNull Distance distance) {
return !greaterThan(distance);
}
public boolean greaterThan(@NonNull Distance distance) {
return distance_m > distance.distance_m;
}
public boolean greaterOrEqualThan(@NonNull Distance distance) {
return distance_m >= distance.distance_m;
}
public double toM() {
return distance_m;
}
public double toKM() {
return distance_m * UnitConversions.M_TO_KM;
}
public double toFT() {
return distance_m * UnitConversions.M_TO_FT;
}
public double toMI() {
return toKM() * UnitConversions.KM_TO_MI;
}
public double toKM_Miles(boolean metricUnit) {
return metricUnit ? toKM() : toMI();
}
public double toM_FT(boolean metricUnit) {
return metricUnit ? toM() : toFT();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Distance distance = (Distance) o;
return Double.compare(distance.distance_m, distance_m) == 0;
}
@Override
public int hashCode() {
return Objects.hash(distance_m);
}
@NonNull
@Override
public String toString() {
return "Distance{" +
"distance_m=" + distance_m +
'}';
}
}
@@ -0,0 +1,322 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.data.models;
import android.location.Location;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//TODO All data should be final; no default values.
public final class Marker {
private Id id;
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private Track.Id trackId;
private final Instant time;
private Double latitude;
private Double longitude;
@Deprecated //Not needed
private Distance accuracy;
private Altitude altitude;
private Float bearing;
//TODO It is the distance from the track starting point; rename to something more meaningful
private Distance length;
private Duration duration;
@Deprecated //TODO Make an URI instead of String
private String photoUrl = "";
public Marker(@Nullable Track.Id trackId, Instant time) {
this.trackId = trackId;
this.time = time;
}
@Deprecated //TODO Marker cannot be created without length AND duration!
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
this.trackId = trackId;
this.time = trackPoint.getTime();
if (!trackPoint.hasLocation())
throw new RuntimeException("Marker requires a trackpoint with a location.");
setTrackPoint(trackPoint);
this.length = Distance.of(0); //TODO Not cool!
this.duration = Duration.ofMillis(0); //TODO Not cool!
}
@Deprecated
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackStatistics statistics, @NonNull TrackPoint trackPoint, String photoUrl) {
this(trackId, trackPoint);
this.name = name;
this.description = description;
this.category = category;
this.icon = icon;
this.length = statistics.getTotalDistance();
this.duration = statistics.getTotalTime();
this.photoUrl = photoUrl;
}
//TODO Is somehow part of the initialization process. Can we at least limit visibility?
public void setTrackPoint(TrackPoint trackPoint) {
this.latitude = trackPoint.getLatitude();
this.longitude = trackPoint.getLongitude();
if (trackPoint.hasHorizontalAccuracy()) this.accuracy = trackPoint.getHorizontalAccuracy();
if (trackPoint.hasAltitude()) this.altitude = trackPoint.getAltitude();
if (trackPoint.hasBearing()) this.bearing = trackPoint.getBearing();
}
/**
* May be null if the it was not loaded from the database.
*/
@Nullable
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public Instant getTime() {
return time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Track.Id getTrackId() {
return trackId;
}
@Deprecated
public void setTrackId(@NonNull Track.Id trackId) {
this.trackId = trackId;
}
public boolean hasLocation() {
return latitude != null || longitude != null;
}
public Location getLocation() {
Location location = new Location("");
location.setTime(time.toEpochMilli());
if (hasLocation()) {
location.setLatitude(latitude);
location.setLongitude(longitude);
}
if (hasBearing()) {
location.setBearing(bearing);
}
if (hasAccuracy()) {
location.setAccuracy((float) accuracy.toM());
}
if (hasAltitude()) {
location.setAltitude(altitude.toM());
}
return location;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public boolean hasAccuracy() {
return accuracy != null;
}
public Distance getAccuracy() {
return accuracy;
}
public void setAccuracy(Distance accuracy) {
this.accuracy = accuracy;
}
public boolean hasAltitude() {
return altitude != null;
}
public Altitude getAltitude() {
return altitude;
}
public void setAltitude(Altitude altitude) {
this.altitude = altitude;
}
public boolean hasBearing() {
return bearing != null;
}
public Float getBearing() {
return bearing;
}
public void setBearing(float bearing) {
this.bearing = bearing;
}
public Distance getLength() {
return length;
}
public void setLength(Distance length) {
this.length = length;
}
public Duration getDuration() {
return duration;
}
public void setDuration(@NonNull Duration duration) {
this.duration = duration;
}
public String getPhotoUrl() {
return photoUrl;
}
public void setPhotoUrl(String photoUrl) {
this.photoUrl = photoUrl;
}
public Uri getPhotoURI() {
return Uri.parse(photoUrl);
}
public boolean hasPhoto() {
return photoUrl != null && !"".equals(photoUrl);
}
public static class Id implements Parcelable {
private final long id;
public Id(long id) {
this.id = id;
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id1 = (Id) o;
return id == id1.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@NonNull
@Override
public String toString() {
throw new RuntimeException("Not supported");
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(id);
}
public static final Parcelable.Creator<Id> CREATOR = new Parcelable.Creator<>() {
public Id createFromParcel(Parcel in) {
return new Id(in.readLong());
}
public Id[] newArray(int size) {
return new Id[size];
}
};
}
}
@@ -0,0 +1,144 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import java.time.Duration;
import java.util.Objects;
import de.dennisguse.opentracks.settings.PreferencesUtils;
public class Speed {
public static Speed of(Distance distance, Duration duration) {
if (duration.isZero()) {
return zero();
}
return new Speed(distance.toM() / (duration.toMillis() * UnitConversions.MS_TO_S));
}
public static Speed of(double speed_mps) {
return new Speed(speed_mps);
}
public static Speed of(String speed_mps) {
return of(Float.parseFloat(speed_mps));
}
public static Speed ofKMH(double speed_kmh) {
return of(Distance.ofKilometer(speed_kmh), Duration.ofHours(1));
}
public static Speed zero() {
return of(0.0);
}
public static Speed max(Speed speed1, Speed speed2) {
if (speed1.greaterThan(speed2)) {
return speed1;
}
return speed2;
}
public static Speed absDiff(Speed speed1, Speed speed2) {
//TODO Why Math.abs? Seems to be a leftover.
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
}
private final double speed_mps;
private Speed(double speed_mps) {
this.speed_mps = speed_mps;
}
public Speed mul(double factor) {
return new Speed(factor * speed_mps);
}
public boolean isZero() {
return speed_mps == 0;
}
public boolean isInvalid() {
return Double.isNaN(speed_mps) || Double.isInfinite(speed_mps);
}
public boolean isMoving() {
return !isInvalid() && greaterThan(PreferencesUtils.getIdleSpeed());
}
public boolean lessThan(Speed speed) {
return !greaterThan(speed);
}
public boolean greaterThan(Speed speed) {
return speed_mps > speed.speed_mps;
}
public boolean greaterOrEqualThan(Speed speed) {
return speed_mps >= speed.speed_mps;
}
public double toMPS() {
return speed_mps;
}
public double toKMH() {
return speed_mps * UnitConversions.MPS_TO_KMH;
}
public double toMPH() {
return toKMH() * UnitConversions.KM_TO_MI;
}
public Duration toPace(boolean metricUnit) {
if (isZero()) {
return Duration.ofSeconds(0);
}
double distance = speed_mps * (metricUnit ? UnitConversions.M_TO_KM : UnitConversions.M_TO_MI);
return Duration.ofSeconds(Math.round(1 / distance));
}
public double to(boolean metricUnit) {
return to(metricUnit ? Unit.KMH : Unit.MPH);
}
public double to(Unit unit) {
switch (unit) {
case KMH:
return toKMH();
case MPH:
return toMPH();
default:
throw new RuntimeException("Not implemented");
}
}
public enum Unit {
KMH,
MPH,
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Speed speed = (Speed) o;
return Double.compare(speed.speed_mps, speed_mps) == 0;
}
@Override
public int hashCode() {
return Objects.hash(speed_mps);
}
@NonNull
@Override
public String toString() {
return "Speed{" +
"speed_mps=" + speed_mps +
'}';
}
}
@@ -0,0 +1,202 @@
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.data.models;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
import java.util.UUID;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* A track.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//TODO Do not default initialize attributes; might be confusing for debugging
public class Track {
private Track.Id id;
private UUID uuid = UUID.randomUUID();
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private final ZoneOffset zoneOffset;
private TrackStatistics trackStatistics = new TrackStatistics();
@VisibleForTesting
public Track() {
this.zoneOffset = ZoneOffset.UTC;
}
public Track(@NonNull ZoneOffset zoneOffset) {
this.zoneOffset = zoneOffset;
}
/**
* May be null if the track was not loaded from the database.
*/
@Nullable
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public ZoneOffset getZoneOffset() {
return zoneOffset;
}
public OffsetDateTime getStartTime() {
return trackStatistics.getStartTime().atOffset(zoneOffset);
}
@NonNull
public TrackStatistics getTrackStatistics() {
return trackStatistics;
}
public void setTrackStatistics(@NonNull TrackStatistics trackStatistics) {
this.trackStatistics = trackStatistics;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Track track = (Track) o;
return id.equals(track.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public static class Id implements Parcelable {
private final long id;
public Id(long id) {
this.id = id;
}
protected Id(Parcel in) {
id = in.readLong();
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id1 = (Id) o;
return id == id1.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public int describeContents() {
return 0;
}
@NonNull
@Override
public String toString() {
return String.valueOf(id);
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(id);
}
public static final Parcelable.Creator<Track.Id> CREATOR = new Parcelable.Creator<>() {
public Track.Id createFromParcel(Parcel in) {
return new Track.Id(in.readLong());
}
public Track.Id[] newArray(int size) {
return new Track.Id[size];
}
};
}
}
@@ -0,0 +1,480 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.data.models;
import android.location.Location;
import android.os.Parcel;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
/**
* This class extends the standard Android location with extra information.
* <p>
* NOTE: default location will be latitude=0.0 and longitude=0.0.
* For {@link TrackPoint}s with `type == null` this is not meaningful.
* <p>
* NOTE: For Locations provided by the GPS.
* We are replacing the GPS-provided time using the system time.
* Then we have the same timestamps for the user-driven events (aka start, pause, resume) and restore {@link de.dennisguse.opentracks.stats.TrackStatistics}.
* Drawbacks:
* * GPS-provided timestamp might be more precise (but also have GPS week rollover)
* * System clock might be changed (and thus non-monotonic)
* TODO: if these might be a problem, we need to store both timestamps.
*
* @author Sandor Dornbush
*/
public class TrackPoint {
private static final Duration MAX_LOCATION_AGE = Duration.ofMinutes(1);
private TrackPoint.Id id;
@NonNull
private final Instant time;
private Double latitude;
private Double longitude;
private Distance horizontalAccuracy;
private Altitude altitude;
private Speed speed;
private Float bearing;
private Distance sensorDistance;
public enum Type {
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
SEGMENT_START_AUTOMATIC(-1), //Start of a segment due to too much distance from previous TrackPoint
TRACKPOINT(0), //Just GPS data and may contain BLE sensor data
SENSORPOINT(2), //Just BLE sensor data; required to have speed and sensorDistance
SEGMENT_END_MANUAL(1); //End of a segment
public final int type_db;
Type(int type_db) {
this.type_db = type_db;
}
@Override
public String toString() {
return name() + "(" + type_db + ")";
}
public static Type getById(int id) {
for (Type e : values()) {
if (e.type_db == id) return e;
}
throw new RuntimeException("unknown id: " + id);
}
}
@NonNull
private Type type;
private Float heartRate_bpm = null;
private Float cadence_rpm = null;
private Float power = null;
private Float altitudeGain_m = null;
private Float altitudeLoss_m = null;
public TrackPoint(@NonNull Type type, @NonNull Instant time) {
this.type = type;
this.time = time;
}
public TrackPoint(@NonNull Location location, @NonNull Instant time) {
this(Type.TRACKPOINT, location, time);
}
public TrackPoint(@NonNull Type type, @NonNull Location location, @NonNull Instant time) {
this(type, time);
setLocation(location);
}
@VisibleForTesting
public TrackPoint(double latitude, double longitude, Altitude altitude, Instant time) {
this(Type.TRACKPOINT, time);
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
public static TrackPoint createSegmentStartManualWithTime(Instant time) {
return new TrackPoint(Type.SEGMENT_START_MANUAL, time);
}
public static TrackPoint createSegmentEndWithTime(Instant time) {
return new TrackPoint(Type.SEGMENT_END_MANUAL, time);
}
@NonNull
public Type getType() {
return type;
}
public TrackPoint setType(@NonNull Type type) {
this.type = type;
return this;
}
public boolean isSegmentStart() {
return type == Type.SEGMENT_START_AUTOMATIC || type == Type.SEGMENT_START_MANUAL;
}
public boolean isSegmentEnd() {
return type == Type.SEGMENT_END_MANUAL;
}
public boolean wasCreatedManually() {
return hasLocation() || hasSpeed();
}
/**
* May be null if the track was not loaded from the database.
*/
@Nullable
public TrackPoint.Id getId() {
return id;
}
public void setId(TrackPoint.Id id) {
this.id = id;
}
public boolean hasLocation() {
return latitude != null && longitude != null;
}
public double getLatitude() {
return latitude;
}
public TrackPoint setLatitude(double latitude) {
this.latitude = latitude;
return this;
}
public double getLongitude() {
return longitude;
}
public TrackPoint setLongitude(double longitude) {
this.longitude = longitude;
return this;
}
//TODO Better return null, if no location is present aka latitude == null etc.
@NonNull
public Location getLocation() {
Location location = new Location("");
location.setTime(time.toEpochMilli());
if (hasLocation()) {
location.setLatitude(latitude);
location.setLongitude(longitude);
}
if (hasBearing()) {
location.setBearing(bearing);
}
if (hasHorizontalAccuracy()) {
location.setAccuracy((float) horizontalAccuracy.toM());
}
if (hasAltitude()) {
location.setAltitude(altitude.toM());
}
if (hasSpeed()) {
location.setSpeed((float) speed.toMPS());
}
return location;
}
public TrackPoint setLocation(@NonNull Location location) {
this.latitude = location.getLatitude();
this.longitude = location.getLongitude();
this.altitude = location.hasAltitude() ? Altitude.WGS84.of(location.getAltitude()) : null;
this.speed = location.hasSpeed() ? Speed.of(location.getSpeed()) : null;
this.horizontalAccuracy = location.hasAccuracy() ? Distance.of(location.getAccuracy()) : null;
//TODO Should we copy the bearing?
return this;
}
public boolean hasAltitudeGain() {
return altitudeGain_m != null;
}
public float getAltitudeGain() {
return altitudeGain_m;
}
public TrackPoint setAltitudeGain(Float altitudeGain_m) {
this.altitudeGain_m = altitudeGain_m;
return this;
}
public boolean hasAltitudeLoss() {
return altitudeLoss_m != null;
}
public float getAltitudeLoss() {
return altitudeLoss_m;
}
public TrackPoint setAltitudeLoss(Float altitudeLoss_m) {
this.altitudeLoss_m = altitudeLoss_m;
return this;
}
@NonNull
public Instant getTime() {
return time;
}
public boolean isRecent() {
return Instant.now()
.isBefore(time.plus(MAX_LOCATION_AGE));
}
public boolean hasAltitude() {
return altitude != null;
}
public Altitude getAltitude() {
return altitude;
}
@VisibleForTesting
public TrackPoint setAltitude(double altitude_m) {
this.altitude = Altitude.WGS84.of(altitude_m);
return this;
}
public TrackPoint setAltitude(Altitude altitude) {
this.altitude = altitude;
return this;
}
public boolean hasSpeed() {
return speed != null;
}
public Speed getSpeed() {
return speed;
}
public TrackPoint setSpeed(Speed speed) {
this.speed = speed;
return this;
}
public boolean isMoving() {
return hasSpeed() && getSpeed().isMoving();
}
public boolean hasBearing() {
return bearing != null;
}
public float getBearing() {
return bearing;
}
public TrackPoint setBearing(Float bearing) {
this.bearing = bearing;
return this;
}
public boolean hasHorizontalAccuracy() {
return horizontalAccuracy != null;
}
public Distance getHorizontalAccuracy() {
return horizontalAccuracy;
}
public TrackPoint setHorizontalAccuracy(Distance horizontalAccuracy) {
this.horizontalAccuracy = horizontalAccuracy;
return this;
}
@NonNull
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
if (hasSensorDistance()) {
return getSensorDistance();
}
return distanceToPreviousFromLocation(previous);
}
@NonNull
public Distance distanceToPreviousFromLocation(@NonNull TrackPoint previous) {
if (!hasLocation() || hasLocation() != previous.hasLocation()) {
throw new RuntimeException("Cannot compute distance.");
}
return Distance.of(getLocation().distanceTo(previous.getLocation()));
}
public boolean fulfillsAccuracy(Distance thresholdHorizontalAccuracy) {
return hasHorizontalAccuracy() && horizontalAccuracy.lessThan(thresholdHorizontalAccuracy);
}
//TODO Bearing requires a location; what do we do if we don't have any?
public float bearingTo(@NonNull TrackPoint dest) {
return getLocation().bearingTo(dest.getLocation());
}
//TODO Bearing requires a location; what do we do if we don't have any?
public float bearingTo(@NonNull Location dest) {
return getLocation().bearingTo(dest);
}
// Sensor data
public boolean hasSensorDistance() {
return sensorDistance != null;
}
public Distance getSensorDistance() {
return sensorDistance;
}
public TrackPoint setSensorDistance(Distance distance_m) {
this.sensorDistance = distance_m;
return this;
}
public TrackPoint minusCumulativeSensorData(@NonNull TrackPoint lastTrackPoint) {
if (hasSensorDistance() && lastTrackPoint.hasSensorDistance()) {
sensorDistance = sensorDistance.minus(lastTrackPoint.getSensorDistance());
}
if (hasAltitudeGain() && lastTrackPoint.hasAltitudeGain()) {
altitudeGain_m -= lastTrackPoint.altitudeGain_m;
}
if (hasAltitudeLoss() && lastTrackPoint.hasAltitudeLoss()) {
altitudeLoss_m -= lastTrackPoint.altitudeLoss_m;
}
return this;
}
public boolean hasSensorData() {
return hasHeartRate() || hasCadence() || hasPower();
}
public boolean hasHeartRate() {
return heartRate_bpm != null && heartRate_bpm > 0;
}
public float getHeartRate_bpm() {
return heartRate_bpm;
}
public TrackPoint setHeartRate_bpm(Float heartRate_bpm) {
this.heartRate_bpm = heartRate_bpm;
return this;
}
public boolean hasCadence() {
return cadence_rpm != null;
}
public float getCadence_rpm() {
return cadence_rpm;
}
public TrackPoint setCadence_rpm(Float cadence_rpm) {
this.cadence_rpm = cadence_rpm;
return this;
}
public boolean hasPower() {
return power != null;
}
public float getPower() {
return power;
}
public TrackPoint setPower(Float power) {
this.power = power;
return this;
}
@Override
public String toString() {
return "TrackPoint{" +
"id=" + id +
", time=" + time +
", latitude=" + latitude +
", longitude=" + longitude +
", horizontalAccuracy=" + horizontalAccuracy +
", altitude=" + altitude +
", speed=" + speed +
", bearing=" + bearing +
", sensorDistance=" + sensorDistance +
", type=" + type +
", heartRate_bpm=" + heartRate_bpm +
", cadence_rpm=" + cadence_rpm +
", power=" + power +
", altitudeGain_m=" + altitudeGain_m +
", altitudeLoss_m=" + altitudeLoss_m +
'}';
}
public static class Id {
private final long id;
public Id(long id) {
this.id = id;
}
protected Id(Parcel in) {
id = in.readLong();
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrackPoint.Id id1 = (TrackPoint.Id) o;
return id == id1.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@NonNull
@Override
public String toString() {
return String.valueOf(id);
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.data.models;
/**
* Unit conversion constants.
*
* @author Sandor Dornbush
*/
public class UnitConversions {
// Time //TODO Use Duration
// multiplication factor to convert seconds to milliseconds
public static final long S_TO_MS = 1000;
// multiplication factor to convert milliseconds to seconds
@Deprecated
public static final double MS_TO_S = 1d / S_TO_MS;
// multiplication factor to convert minutes to seconds
@Deprecated
public static final double MIN_TO_S = 60.0;
// multiplication factor to convert seconds to minutes
@Deprecated
public static final double S_TO_MIN = 1 / MIN_TO_S;
// multiplication factor to convert minutes to hours
@Deprecated
public static final double MIN_TO_HR = 1 / 60.0;
// multiplication factor to convert kilometers to miles
public static final double KM_TO_MI = 0.621371192;
// Distance //TODO Make private to Distance class!
// multiplication factor to convert miles to feet
private static final double MI_TO_FT = 5280.0;
// multiplication factor to covert kilometers to meters
public static final double KM_TO_M = 1000.0;
// multiplication factor to convert meters to kilometers
public static final double M_TO_KM = 1 / KM_TO_M;
// multiplication factor to convert meters to miles
public static final double M_TO_MI = M_TO_KM * KM_TO_MI;
// multiplication factor to convert meters to feet
public static final double M_TO_FT = M_TO_MI * MI_TO_FT;
// multiplication factor to convert meters per second to kilometers per hour
public static final double MPS_TO_KMH = M_TO_KM / (S_TO_MIN * MIN_TO_HR);
// multiplication factor to convert miles to km
public static final double MI_TO_KM = 1 / KM_TO_MI;
// multiplication factor to convert miles to m
public static final double MI_TO_M = MI_TO_KM * KM_TO_M;
private UnitConversions() {
}
}