diff --git a/res/values/strings.xml b/res/values/strings.xml index 05b8dcf47..4a2bc0451 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -53,6 +53,12 @@ Track Details Start time: End time: + Distance: + Km + Elevation(min/max): + m + Speed(min/max): + Km/h Starts at: Ends at: Exported: diff --git a/src/me/guillaumin/android/osmtracker/activity/TrackDetail.java b/src/me/guillaumin/android/osmtracker/activity/TrackDetail.java index 8d593294e..78070acbc 100644 --- a/src/me/guillaumin/android/osmtracker/activity/TrackDetail.java +++ b/src/me/guillaumin/android/osmtracker/activity/TrackDetail.java @@ -2,6 +2,7 @@ import java.sql.Date; import java.text.DateFormat; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -155,6 +156,25 @@ protected void onResume() { map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_enddate)); map.put(ITEM_VALUE, t.getEndDateAsString()); data.add(map); + + DecimalFormat df = new DecimalFormat("#,##0.00"); + // Distance + map = new HashMap(); + map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_distance)); + map.put(ITEM_VALUE, df.format(t.getDistance()/1000) + " " +getResources().getString(R.string.trackdetail_distance_unit)); + data.add(map); + + // Elevation + map = new HashMap(); + map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_elevation)); + map.put(ITEM_VALUE, df.format(t.getElevationMin()) + " / " + df.format(t.getElevationMax()) + " " +getResources().getString(R.string.trackdetail_elevation_unit)); + data.add(map); + + // Speed + map = new HashMap(); + map.put(ITEM_KEY, getResources().getString(R.string.trackdetail_speed)); + map.put(ITEM_VALUE, df.format(t.getSpeedMin() * 3.6) + " / " + df.format(t.getSpeedMax() * 3.6) + " " + getResources().getString(R.string.trackdetail_speed_unit)); + data.add(map); // Start point map = new HashMap(); diff --git a/src/me/guillaumin/android/osmtracker/db/model/Track.java b/src/me/guillaumin/android/osmtracker/db/model/Track.java index aeb992e9f..b4d2b1027 100644 --- a/src/me/guillaumin/android/osmtracker/db/model/Track.java +++ b/src/me/guillaumin/android/osmtracker/db/model/Track.java @@ -9,6 +9,7 @@ import me.guillaumin.android.osmtracker.R; import me.guillaumin.android.osmtracker.db.TrackContentProvider; import me.guillaumin.android.osmtracker.db.TrackContentProvider.Schema; +import me.guillaumin.android.osmtracker.util.DistanceUtil; import android.content.ContentResolver; import android.database.Cursor; @@ -57,8 +58,12 @@ public static OSMVisibility fromPosition(int position) { private Long startDate=null, endDate=null; private Float startLat=null, startLong=null, endLat=null, endLong=null; + private Float distance=0.0f; + private Float elevationMax=0.0f,elevationMin=0.0f; + private Float speedMax=0.0f, speedMin=0.0f; private boolean extraInformationRead = false; + private boolean extraInformationRead_stats = false; private ContentResolver cr; @@ -121,6 +126,56 @@ private void readExtraInformation(){ } } + /** + * + * @todo: allow to exclude zero speed values + */ + private void readExtraInformation_stats() { + if(!extraInformationRead_stats){ + Cursor cursor = cr.query(TrackContentProvider.trackPointsUri(trackId), null, null, null, null); + Float latitudeCurrent, longitudeCurrent, latitudePrev, longitudePrev; + + Float elevationCurr, speedCurr; + + boolean avoidZeroSpeed = true; + + if(cursor != null && cursor.moveToFirst()) { + // Initialize the min/max values + elevationMin = elevationMax = cursor.getFloat(cursor.getColumnIndex(Schema.COL_ELEVATION)); + speedMax = cursor.getFloat(cursor.getColumnIndex(Schema.COL_SPEED)); + speedMin = (avoidZeroSpeed && speedMax == 0) ? Float.MAX_VALUE : speedMax; + + latitudePrev = cursor.getFloat(cursor.getColumnIndex(Schema.COL_LATITUDE)); + longitudePrev = cursor.getFloat(cursor.getColumnIndex(Schema.COL_LONGITUDE)); + + // Iterate over all points + while (cursor.moveToNext()) { + latitudeCurrent = cursor.getFloat(cursor.getColumnIndex(Schema.COL_LATITUDE)); + longitudeCurrent = cursor.getFloat(cursor.getColumnIndex(Schema.COL_LONGITUDE)); + distance += DistanceUtil.getDistance(latitudePrev, longitudePrev, latitudeCurrent, longitudeCurrent); + + latitudePrev = latitudeCurrent; + longitudePrev = longitudeCurrent; + + // Compute the Elevation + elevationCurr = cursor.getFloat(cursor.getColumnIndex(Schema.COL_ELEVATION)); + elevationMin = Math.min(elevationMin, elevationCurr); + elevationMax = Math.max(elevationMax, elevationCurr); + + // Compute the Speed + speedCurr = cursor.getFloat(cursor.getColumnIndex(Schema.COL_SPEED)); + if (!avoidZeroSpeed || speedCurr > 0) { + speedMin = Math.min(speedMin, speedCurr); + speedMax = Math.max(speedMax, speedCurr); + } + } + cursor.close(); + extraInformationRead_stats = true; + } + } + } + + public void setName(String name) { this.name = name; } @@ -177,6 +232,32 @@ public Integer getTpCount() { return tpCount; } + public Float getDistance() { + readExtraInformation_stats(); + return distance; + } + + public Float getElevationMin() { + readExtraInformation_stats(); + return elevationMin; + } + + public Float getElevationMax() { + readExtraInformation_stats(); + return elevationMax; + } + + public Float getSpeedMax() { + readExtraInformation_stats(); + return speedMax; + } + + public Float getSpeedMin() { + readExtraInformation_stats(); + // Avoid returning inconsistent min-speed if "avoidZeroSpeed" is set to true + return (speedMin > speedMax) ? 0.0f : speedMin; + } + public String getName() { if (name != null && name.length() > 0) { return name; diff --git a/src/me/guillaumin/android/osmtracker/util/DistanceUtil.java b/src/me/guillaumin/android/osmtracker/util/DistanceUtil.java new file mode 100644 index 000000000..d4a456df5 --- /dev/null +++ b/src/me/guillaumin/android/osmtracker/util/DistanceUtil.java @@ -0,0 +1,46 @@ +package me.guillaumin.android.osmtracker.util; + +import org.osmdroid.api.IGeoPoint; + +public class DistanceUtil { + + private static final double DEG2RAD = Math.PI / 180.0; + // private static final double RAD2DEG = 180.0 / Math.PI; + private static final int RADIUS_EARTH_METERS = 6378137; + + /** + * Computes the distance with Spherical Law of Cosines + * http://www.movable-type.co.uk/scripts/latlong.html + * @param lat1 + * @param lon1 + * @param lat2 + * @param lon2 + * @return The distance (in meters) between point1 and point2 + */ + public static float getDistance(final float lat1, final float lon1, final float lat2, final float lon2) { + final double theta = lon1 - lon2; + System.out.println("theta = " + theta); + final double v = (Math.sin(DEG2RAD * lat1) * Math.sin(DEG2RAD * lat2)) + + (Math.cos(DEG2RAD * lat1) * Math.cos(DEG2RAD * lat2) * Math.cos(DEG2RAD * theta)); + + // Due to Float/Double approximations sometimes the value v is greater than 1, thus out of Math.acos() domain + // @see: http://www.mathworks.it/it/help/matlab/ref/acos.html + return (v > 1) ? 0.0f : (float) (Math.acos(v) * RADIUS_EARTH_METERS); + } + + /** + * Computes the distance with Spherical Law of Cosines + * http://www.movable-type.co.uk/scripts/latlong.html + * @param point1 + * @param point2 + * @return The distance (in meters) between point1 and point2 + */ + public static float getDistance(final IGeoPoint point1, final IGeoPoint point2) { + final float lat1 = (float) (point1.getLatitudeE6() / 1E6); + final float lon1 = (float) (point1.getLongitudeE6() / 1E6); + final float lat2 = (float) (point2.getLatitudeE6() / 1E6); + final float lon2 = (float) (point2.getLongitudeE6() / 1E6); + + return getDistance(lat1, lon1, lat2, lon2); + } +}