diff --git a/app/src/main/java/org/y20k/trackbook/MainActivityTrackFragment.java b/app/src/main/java/org/y20k/trackbook/MainActivityTrackFragment.java index 4b6ea56..c87ffc3 100755 --- a/app/src/main/java/org/y20k/trackbook/MainActivityTrackFragment.java +++ b/app/src/main/java/org/y20k/trackbook/MainActivityTrackFragment.java @@ -24,6 +24,8 @@ import android.content.IntentFilter; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Vibrator; +import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -48,10 +50,12 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import org.osmdroid.api.IMapController; +import org.osmdroid.events.MapEventsReceiver; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; import org.osmdroid.views.overlay.ItemizedIconOverlay; +import org.osmdroid.views.overlay.MapEventsOverlay; import org.osmdroid.views.overlay.TilesOverlay; import org.osmdroid.views.overlay.compass.CompassOverlay; import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider; @@ -75,7 +79,7 @@ import java.util.Locale; /** * MainActivityTrackFragment class */ -public class MainActivityTrackFragment extends Fragment implements AdapterView.OnItemSelectedListener, TrackbookKeys { +public class MainActivityTrackFragment extends Fragment implements AdapterView.OnItemSelectedListener, MapEventsReceiver, TrackbookKeys { /* Define log tag */ private static final String LOG_TAG = MainActivityTrackFragment.class.getSimpleName(); @@ -268,6 +272,10 @@ public class MainActivityTrackFragment extends Fragment implements AdapterView.O attachTapListenerToStatisticsSheet(); } + // enable additional gestures + MapEventsOverlay OverlayEventos = new MapEventsOverlay(this); + mMapView.getOverlays().add(OverlayEventos); + return mRootView; } @@ -365,6 +373,25 @@ public class MainActivityTrackFragment extends Fragment implements AdapterView.O } + @Override + public boolean singleTapConfirmedHelper(GeoPoint p) { + return false; + } + + + @Override + public boolean longPressHelper(GeoPoint p) { + if (mTrack != null) { + // vibrate 50 milliseconds + Vibrator vibrator = (Vibrator) mActivity.getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(50); + // zoom to bounding box (= edge coordinates of map) + mMapView.zoomToBoundingBox(mTrack.getBoundingBox(), true); + } + return true; + } + + /* Displays map and statistics for track */ private void displayTrack() { GeoPoint position; @@ -405,7 +432,6 @@ public class MainActivityTrackFragment extends Fragment implements AdapterView.O // draw track on map drawTrackOverlay(mTrack); - } else { position = new GeoPoint(DEFAULT_LATITUDE, DEFAULT_LONGITUDE); } diff --git a/app/src/main/java/org/y20k/trackbook/core/Track.java b/app/src/main/java/org/y20k/trackbook/core/Track.java index 62be542..973d0b4 100755 --- a/app/src/main/java/org/y20k/trackbook/core/Track.java +++ b/app/src/main/java/org/y20k/trackbook/core/Track.java @@ -54,36 +54,11 @@ public class Track implements TrackbookKeys, Parcelable { private double mMinAltitude; private double mPositiveElevation; private double mNegativeElevation; + private BoundingBox mBoundingBox; - private BoundingBox boundingBox; - - /** - * Create a {@code BoundingBox} for the collection of - * {@code WayPoint}s, so that it would be possible to fit the map in - * such box and see the whole {@code Track} in the map without - * manual zooming. - * - * It computes the {@code BoundingBox} only once since it's possibly - * useless to compute it everytime. - * - * @return {@code BoundingBox} containing all {@code Waypoint}s - */ - public BoundingBox getBoundingBox() { - if (null == boundingBox) { - final ArrayList geoPoints = new ArrayList<>(mWayPoints.size()); - - for (final WayPoint aWayPoint : mWayPoints) { - final GeoPoint aGeoPoint = new GeoPoint(aWayPoint.getLocation()); - geoPoints.add(aGeoPoint); - } - - boundingBox = BoundingBox.fromGeoPoints(geoPoints); - } - return boundingBox; - } /* Generic Constructor */ - public Track(int trackFormatVersion, List wayPoints, float trackLength, long duration, float stepCount, Date recordingStart, Date recordingStop, double maxAltitude, double minAltitude, double positiveElevation, double negativeElevation) { + public Track(int trackFormatVersion, List wayPoints, float trackLength, long duration, float stepCount, Date recordingStart, Date recordingStop, double maxAltitude, double minAltitude, double positiveElevation, double negativeElevation, BoundingBox boundingBox) { mTrackFormatVersion = trackFormatVersion; mWayPoints = wayPoints; mTrackLength = trackLength; @@ -95,12 +70,13 @@ public class Track implements TrackbookKeys, Parcelable { mMinAltitude = minAltitude; mPositiveElevation = positiveElevation; mNegativeElevation = negativeElevation; + mBoundingBox = boundingBox; } /* Copy Constructor */ public Track(Track track) { - this(track.getTrackFormatVersion(), track.getWayPoints(), track.getTrackLength(), track.getTrackDuration(), track.getStepCount(), track.getRecordingStart(), track.getRecordingStop(), track.getMaxAltitude(), track.getMinAltitude(), track.getPositiveElevation(), track.getNegativeElevation()); + this(track.getTrackFormatVersion(), track.getWayPoints(), track.getTrackLength(), track.getTrackDuration(), track.getStepCount(), track.getRecordingStart(), track.getRecordingStop(), track.getMaxAltitude(), track.getMinAltitude(), track.getPositiveElevation(), track.getNegativeElevation(), track.getBoundingBox()); } @@ -117,6 +93,7 @@ public class Track implements TrackbookKeys, Parcelable { mMinAltitude = 0f; mPositiveElevation = 0f; mNegativeElevation = 0f; + mBoundingBox = new BoundingBox(); } @@ -231,6 +208,12 @@ public class Track implements TrackbookKeys, Parcelable { } + /* Setter for this track's BoundingBox - a data structure describing the edge coordinates of a track */ + public void setBoundingBox(BoundingBox boundingBox) { + mBoundingBox = boundingBox; + } + + /* Getter for file/track format version */ public int getTrackFormatVersion() { return mTrackFormatVersion; @@ -302,6 +285,10 @@ public class Track implements TrackbookKeys, Parcelable { } + /* Getter for this track's BoundingBox - a data structure describing the edge coordinates of a track */ + public BoundingBox getBoundingBox() { return mBoundingBox; } + + /* Getter recorded distance */ public Double getTrackDistance() { int size = mWayPoints.size(); diff --git a/app/src/main/java/org/y20k/trackbook/core/TrackBuilder.java b/app/src/main/java/org/y20k/trackbook/core/TrackBuilder.java index ead4938..73d2306 100755 --- a/app/src/main/java/org/y20k/trackbook/core/TrackBuilder.java +++ b/app/src/main/java/org/y20k/trackbook/core/TrackBuilder.java @@ -17,7 +17,9 @@ package org.y20k.trackbook.core; +import org.osmdroid.util.BoundingBox; import org.y20k.trackbook.helpers.LogHelper; +import org.y20k.trackbook.helpers.MapHelper; import java.util.Date; import java.util.List; @@ -44,10 +46,11 @@ public class TrackBuilder { private final double mMinAltitude; private final double mPositiveElevation; private final double mNegativeElevation; + private BoundingBox mBoundingBox; /* Generic Constructor */ - public TrackBuilder(int trackFormatVersion, List wayPoints, float trackLength, long duration, float stepCount, Date recordingStart, Date recordingStop, double maxAltitude, double minAltitude, double positiveElevation, double negativeElevation) { + public TrackBuilder(int trackFormatVersion, List wayPoints, float trackLength, long duration, float stepCount, Date recordingStart, Date recordingStop, double maxAltitude, double minAltitude, double positiveElevation, double negativeElevation, BoundingBox boundingBox) { mTrackFormatVersion = trackFormatVersion; mWayPoints = wayPoints; mTrackLength = trackLength; @@ -59,6 +62,7 @@ public class TrackBuilder { mMinAltitude = minAltitude; mPositiveElevation = positiveElevation; mNegativeElevation = negativeElevation; + mBoundingBox = boundingBox; } @@ -67,10 +71,13 @@ public class TrackBuilder { switch (mTrackFormatVersion) { case 1: // file format version 1 - does not have elevation data stored - return new Track(mTrackFormatVersion, mWayPoints, mTrackLength, mDuration, mStepCount, mRecordingStart, mRecordingStop, 0f, 0f, 0f, 0f); + return new Track(mTrackFormatVersion, mWayPoints, mTrackLength, mDuration, mStepCount, mRecordingStart, mRecordingStop, 0f, 0f, 0f, 0f, new BoundingBox()); case 2: - // file format version 2 (current version) - return new Track(mTrackFormatVersion, mWayPoints, mTrackLength, mDuration, mStepCount, mRecordingStart, mRecordingStop, mMaxAltitude, mMinAltitude, mPositiveElevation, mNegativeElevation); + // file format version 2 - does not have edge coordinates stored + return new Track(mTrackFormatVersion, mWayPoints, mTrackLength, mDuration, mStepCount, mRecordingStart, mRecordingStop, mMaxAltitude, mMinAltitude, mPositiveElevation, mNegativeElevation, MapHelper.calculateBoundingBox(mWayPoints)); + case 3: + // file format version 3 (current version) + return new Track(mTrackFormatVersion, mWayPoints, mTrackLength, mDuration, mStepCount, mRecordingStart, mRecordingStop, mMaxAltitude, mMinAltitude, mPositiveElevation, mNegativeElevation, new BoundingBox()); default: LogHelper.e(LOG_TAG, "Unknown file format version: " + mTrackFormatVersion); return null; diff --git a/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java b/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java index 8a9b2c9..213bf5e 100755 --- a/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java @@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable; import android.location.Location; import android.widget.Toast; +import org.osmdroid.util.BoundingBox; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.overlay.ItemizedIconOverlay; import org.osmdroid.views.overlay.OverlayItem; @@ -33,12 +34,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; + /** * MapHelper class */ -public final class MapHelper { +public final class MapHelper implements TrackbookKeys { /* Define log tag */ private static final String LOG_TAG = MapHelper.class.getSimpleName(); @@ -181,4 +184,92 @@ public final class MapHelper { return new OverlayItem(title, description, position); } + + + /** + * Create a {@code BoundingBox} for the collection of + * {@code WayPoint}s, so that it would be possible to fit the map in + * such box and see the whole {@code Track} in the map without + * manual zooming. + * + * @return {@code BoundingBox} containing all {@code Waypoint}s + */ + public static BoundingBox calculateBoundingBox(List wayPoints) { + final ArrayList geoPoints = new ArrayList<>(wayPoints.size()); + + for (final WayPoint aWayPoint : wayPoints) { + final GeoPoint aGeoPoint = new GeoPoint(aWayPoint.getLocation()); + geoPoints.add(aGeoPoint); + } + return BoundingBox.fromGeoPoints(geoPoints); + } + + + + /* Calculates positive and negative elevation of track */ + public static Track calculateElevation(@Nullable Track track) { + double maxAltitude = 0; + double minAltitude = 0; + double positiveElevation = 0; + double negativeElevation = 0; + + if (track != null && track.getWayPoints().size() > 0) { + double previousLocationAltitude; + double currentLocationAltitude; + long previousTimeStamp; + long currentTimeStamp; + + // initial values for max height and min height - first waypoint + maxAltitude = track.getWayPointLocation(0).getAltitude(); + minAltitude = maxAltitude; + + // apply filter & smooth data +// track = smoothTrack(track, 15f, 35f); + + // iterate over track + for (int i = 1; i < track.getWayPoints().size(); i++ ) { + + // get time difference + previousTimeStamp = track.getWayPointLocation(i -1).getTime(); + currentTimeStamp = track.getWayPointLocation(i).getTime(); + double timeDiff = (currentTimeStamp - previousTimeStamp); + + // factor is bigger than 1 if the time stamp difference is larger than the movement recording interval (usually 15 seconds) + double timeDiffFactor = timeDiff / FIFTEEN_SECONDS_IN_MILLISECONDS; + + // height of previous and current waypoints + previousLocationAltitude = track.getWayPointLocation(i -1).getAltitude(); + currentLocationAltitude = track.getWayPointLocation(i).getAltitude(); + + // check for new min and max heights + if (currentLocationAltitude > maxAltitude) { + maxAltitude = currentLocationAltitude; + } + if (minAltitude == 0 || currentLocationAltitude < minAltitude) { + minAltitude = currentLocationAltitude; + } + + // get elevation difference and sum it up + double altitudeDiff = currentLocationAltitude - previousLocationAltitude; + if (altitudeDiff > 0 && altitudeDiff < MEASUREMENT_ERROR_THRESHOLD * timeDiffFactor && currentLocationAltitude != 0) { + positiveElevation = positiveElevation + altitudeDiff; + } + if (altitudeDiff < 0 && altitudeDiff > -MEASUREMENT_ERROR_THRESHOLD * timeDiffFactor && currentLocationAltitude != 0) { + negativeElevation = negativeElevation + altitudeDiff; + } + + } + + // store elevation data in track + track.setMaxAltitude(maxAltitude); + track.setMinAltitude(minAltitude); + track.setPositiveElevation(positiveElevation); + track.setNegativeElevation(negativeElevation); + } + return track; + } + + + + } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/StorageHelper.java b/app/src/main/java/org/y20k/trackbook/helpers/StorageHelper.java index d519829..400b340 100755 --- a/app/src/main/java/org/y20k/trackbook/helpers/StorageHelper.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/StorageHelper.java @@ -103,18 +103,20 @@ public class StorageHelper implements TrackbookKeys { } if (mFolder != null && mFolder.exists() && mFolder.isDirectory() && mFolder.canWrite() && recordingStart != null && track != null) { - // calculate elevation and store it in track - track = calculateElevation(track); - // create file object + // create file object and calculate bounding box and elevation, if necessary String fileName; if (fileType == FILE_TEMP_TRACK) { - // case: temp file + // get the temp file name fileName = FILE_NAME_TEMP + FILE_TYPE_TRACKBOOK_EXTENSION; } else { - // case: regular file + // build a regular file name DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); fileName = dateFormat.format(recordingStart) + FILE_TYPE_TRACKBOOK_EXTENSION; + // calculate elevation and store it in track + track = MapHelper.calculateElevation(track); + // calculate bounding box and store it in track + track.setBoundingBox(MapHelper.calculateBoundingBox(track.getWayPoints())); } File file = new File(mFolder.toString() + "/" + fileName); @@ -360,70 +362,6 @@ public class StorageHelper implements TrackbookKeys { } - /* Calculates positive and negative elevation of track */ - private Track calculateElevation(@Nullable Track track) { - double maxAltitude = 0; - double minAltitude = 0; - double positiveElevation = 0; - double negativeElevation = 0; - - if (track != null && track.getWayPoints().size() > 0) { - double previousLocationAltitude; - double currentLocationAltitude; - long previousTimeStamp; - long currentTimeStamp; - - // initial values for max height and min height - first waypoint - maxAltitude = track.getWayPointLocation(0).getAltitude(); - minAltitude = maxAltitude; - - // apply filter & smooth data -// track = smoothTrack(track, 15f, 35f); - - // iterate over track - for (int i = 1; i < track.getWayPoints().size(); i++ ) { - - // get time difference - previousTimeStamp = track.getWayPointLocation(i -1).getTime(); - currentTimeStamp = track.getWayPointLocation(i).getTime(); - double timeDiff = (currentTimeStamp - previousTimeStamp); - - // factor is bigger than 1 if the time stamp difference is larger than the movement recording interval (usually 15 seconds) - double timeDiffFactor = timeDiff / FIFTEEN_SECONDS_IN_MILLISECONDS; - - // height of previous and current waypoints - previousLocationAltitude = track.getWayPointLocation(i -1).getAltitude(); - currentLocationAltitude = track.getWayPointLocation(i).getAltitude(); - - // check for new min and max heights - if (currentLocationAltitude > maxAltitude) { - maxAltitude = currentLocationAltitude; - } - if (minAltitude == 0 || currentLocationAltitude < minAltitude) { - minAltitude = currentLocationAltitude; - } - - // get elevation difference and sum it up - double altitudeDiff = currentLocationAltitude - previousLocationAltitude; - if (altitudeDiff > 0 && altitudeDiff < MEASUREMENT_ERROR_THRESHOLD * timeDiffFactor && currentLocationAltitude != 0) { - positiveElevation = positiveElevation + altitudeDiff; - } - if (altitudeDiff < 0 && altitudeDiff > -MEASUREMENT_ERROR_THRESHOLD * timeDiffFactor && currentLocationAltitude != 0) { - negativeElevation = negativeElevation + altitudeDiff; - } - - } - - // store elevation data in track - track.setMaxAltitude(maxAltitude); - track.setMinAltitude(minAltitude); - track.setPositiveElevation(positiveElevation); - track.setNegativeElevation(negativeElevation); - } - return track; - } - - /* Tries to smooth the elevation data using a low pass filter */ private Track smoothTrack(Track input, float dt, float rc) { diff --git a/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java b/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java index 97c7551..9a7d902 100755 --- a/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java @@ -113,7 +113,7 @@ public interface TrackbookKeys { String NOTIFICATION_CHANEL_ID_RECORDING_CHANNEL ="notificationChannelIdRecordingChannel"; /* MISC */ - int CURRENT_TRACK_FORMAT_VERSION = 2; // incremental version number to prevent issues in case the Track format evolves + int CURRENT_TRACK_FORMAT_VERSION = 3; // incremental version number to prevent issues in case the Track format evolves double DEFAULT_LATITUDE = 71.172500; // latitude Nordkapp, Norway double DEFAULT_LONGITUDE = 25.784444; // longitude Nordkapp, Norway int MEASUREMENT_ERROR_THRESHOLD = 10; // altitude changes of 10 meter or more (per 15 seconds) are being discarded diff --git a/build.gradle b/build.gradle index a763091..36428b1 100644 --- a/build.gradle +++ b/build.gradle @@ -24,8 +24,8 @@ allprojects { project.ext { applicationId = 'org.y20k.trackbook' - versionCode = 36 - versionName = '1.2.2' + versionCode = 37 + versionName = '1.2.3' compileSdkVersion = 28 targetSdkVersion = 27