diff --git a/OsmAnd/res/layout-land/menu.xml b/OsmAnd/res/layout-land/menu.xml
index 6cff01c1db..752d96613e 100644
--- a/OsmAnd/res/layout-land/menu.xml
+++ b/OsmAnd/res/layout-land/menu.xml
@@ -62,7 +62,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
+ android:text="@string/my_data_Button" android:typeface="serif" android:textColor="#000000"/>
diff --git a/OsmAnd/res/layout/menu.xml b/OsmAnd/res/layout/menu.xml
index 24da0231d0..2ff8192857 100644
--- a/OsmAnd/res/layout/menu.xml
+++ b/OsmAnd/res/layout/menu.xml
@@ -60,7 +60,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b
+ android:text="@string/my_data_Button" android:typeface="serif" android:textColor="#000000"/>
diff --git a/OsmAnd/res/layout/tab_content.xml b/OsmAnd/res/layout/tab_content.xml
new file mode 100644
index 0000000000..99e54ecbc8
--- /dev/null
+++ b/OsmAnd/res/layout/tab_content.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index 48a91d46d8..6b56ed4dce 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -9,6 +9,8 @@
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
-->
+ My Data
+ My Data
User %1$s joined group %2$s
User %1$s left group %2$s
Show group notifications
diff --git a/OsmAnd/src/net/osmand/plus/GPXUtilities.java b/OsmAnd/src/net/osmand/plus/GPXUtilities.java
index 81bbc3822a..b47737716b 100644
--- a/OsmAnd/src/net/osmand/plus/GPXUtilities.java
+++ b/OsmAnd/src/net/osmand/plus/GPXUtilities.java
@@ -108,7 +108,236 @@ public class GPXUtilities {
public List points = new ArrayList();
}
+
+ public static class GPXTrackAnalysis {
+ public float totalDistance = 0;
+ public int totalTracks = 0;
+ public long startTime = Long.MAX_VALUE;
+ public long endTime = Long.MIN_VALUE;
+ public long timeSpan = 0;
+ public long timeMoving = 0;
+ public float totalDistanceMoving = 0;
+ public double diffElevationUp = 0;
+ public double diffElevationDown = 0;
+ public double avgElevation = 0;
+ public double minElevation = 99999;
+ public double maxElevation = -100;
+
+ public float maxSpeed = 0;
+ public float avgSpeed;
+
+ public int points;
+ public int wptPoints = 0;
+
+ public int metricEnd;
+
+ public boolean isTimeSpecified() {
+ return startTime != Long.MAX_VALUE;
+ }
+
+ public boolean isTimeMoving() {
+ return timeMoving != 0;
+ }
+
+ public boolean isElevationSpecified() {
+ return maxElevation != -100;
+ }
+
+ public int getTimeHours(long time) {
+ return (int) ((time / 1000) / 3600);
+ }
+
+ public int getTimeSeconds(long time) {
+ return (int) ((time / 1000) % 60);
+ }
+
+ public int getTimeMinutes(long time) {
+ return (int) (((time / 1000) / 60) % 60);
+ }
+
+ public boolean isSpeedSpecified() {
+ return avgSpeed > 0;
+ }
+
+
+
+ public void prepareInformation(long filestamp, SplitSegment... splitSegments) {
+ float[] calculations = new float[1];
+
+ float totalElevation = 0;
+ int elevationPoints = 0;
+ int speedCount = 0;
+ double totalSpeedSum = 0;
+ points = 0;
+ for (SplitSegment s : splitSegments) {
+ final int numberOfPoints = s.getNumberOfPoints();
+ metricEnd += s.metricEnd;
+ points += numberOfPoints;
+ for (int j = 0; j < numberOfPoints; j++) {
+ WptPt point = s.get(j);
+ long time = point.time;
+ if (time != 0) {
+ startTime = Math.min(startTime, time);
+ endTime = Math.max(startTime, time);
+ }
+
+ double elevation = point.ele;
+ if (!Double.isNaN(elevation)) {
+ totalElevation += elevation;
+ elevationPoints++;
+ minElevation = Math.min(elevation, minElevation);
+ maxElevation = Math.max(elevation, maxElevation);
+ }
+
+ float speed = (float) point.speed;
+ if (speed > 0) {
+ totalSpeedSum += speed;
+ maxSpeed = Math.max(speed, maxSpeed);
+ speedCount++;
+ }
+
+ if (j > 0) {
+ WptPt prev = s.get(j - 1);
+
+ if (!Double.isNaN(point.ele) && !Double.isNaN(prev.ele)) {
+ double diff = point.ele - prev.ele;
+ if (diff > 0) {
+ diffElevationUp += diff;
+ } else {
+ diffElevationDown -= diff;
+ }
+ }
+
+ // totalDistance += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon);
+ // using ellipsoidal 'distanceBetween' instead of spherical haversine (MapUtils.getDistance) is
+ // a little more exact, also seems slightly faster:
+ net.osmand.Location.distanceBetween(prev.lat, prev.lon, point.lat, point.lon, calculations);
+ totalDistance += calculations[0];
+
+ // Averaging speed values is less exact than totalDistance/timeMoving
+ if (speed > 0 && point.time != 0 && prev.time != 0) {
+ timeMoving = timeMoving + (point.time - prev.time);
+ totalDistanceMoving += calculations[0];
+ }
+ }
+ }
+ }
+ if(!isTimeSpecified()){
+ startTime = filestamp;
+ endTime = filestamp;
+ }
+
+ // OUTPUT:
+ // 1. Total distance, Start time, End time
+ // 2. Time span
+ timeSpan = endTime - startTime;
+
+ // 3. Time moving, if any
+ if (elevationPoints > 0) {
+ avgElevation = elevationPoints / totalElevation;
+ }
+
+ // 4. Elevation, eleUp, eleDown, if recorded
+
+ // 5. Max speed and Average speed, if any. Average speed is NOT overall (effective) speed, but only calculated for "moving" periods.
+ if(speedCount > 0) {
+ if(timeMoving > 0){
+ avgSpeed = (float) (totalDistanceMoving / timeMoving * 1000);
+ } else {
+ avgSpeed = (float) (totalSpeedSum / speedCount);
+ }
+ } else {
+ avgSpeed = -1;
+ }
+ }
+ }
+
+ private static class SplitSegment {
+ TrkSegment segment;
+ float startCoeff = 0;
+ int startPointInd;
+ float endCoeff = 0;
+ int endPointInd;
+ int metricEnd;
+
+ public int getNumberOfPoints() {
+ return endPointInd - startPointInd + 2;
+ }
+
+ public WptPt get(int j) {
+ final int ind = j + startPointInd;
+ if(j == 0) {
+ if(startCoeff == 0) {
+ return segment.points.get(ind);
+ }
+ return approx(segment.points.get(ind), segment.points.get(ind + 1), startCoeff);
+ }
+ if(j == getNumberOfPoints()) {
+ if(startCoeff == 1) {
+ return segment.points.get(ind);
+ }
+ return approx(segment.points.get(ind - 1), segment.points.get(ind), endCoeff);
+ }
+ return segment.points.get(ind);
+ }
+
+
+ private WptPt approx(WptPt w1, WptPt w2, float cf) {
+ long time = value(w1.time, w2.time, 0, cf);
+ double speed = value(w1.speed, w2.speed, 0, cf);
+ double ele = value(w1.ele, w2.ele, 0, cf);
+ double hdop = value(w1.hdop, w2.hdop, 0, cf);
+ double lat = value(w1.lat, w2.lat, -360, cf);
+ double lon = value(w1.lon, w2.lon, -360, cf);
+ return new WptPt(lat, lon, time, ele, speed, hdop);
+ }
+
+ private double value(double vl, double vl2, double none, float cf) {
+ if(vl == none || Double.isNaN(vl)) {
+ return vl2;
+ } else if (vl2 == none || Double.isNaN(vl2)) {
+ return vl;
+ }
+ return vl + cf * (vl2 - vl);
+ }
+
+ private long value(long vl, long vl2, long none, float cf) {
+ if(vl == none) {
+ return vl2;
+ } else if(vl2 == none) {
+ return vl;
+ }
+ return vl + ((long) cf * (vl2 - vl));
+ }
+
+ public SplitSegment(TrkSegment s) {
+ startPointInd = 0;
+ startCoeff = 0;
+ endPointInd = s.points.size() - 1;
+ startCoeff = 1;
+ }
+
+ public SplitSegment(TrkSegment s, int pointInd, float cf) {
+ this.segment = s;
+ this.startPointInd = pointInd;
+ this.startCoeff = cf;
+ }
+
+ public float setLastPoint(int pointInd, float endCf) {
+ endCoeff = endCf;
+ endPointInd = pointInd;
+ return startCoeff;
+ }
+
+ }
+
+ private abstract static class SplitMetric {
+
+ public abstract int metric(WptPt p1, WptPt p2);
+
+ }
+
public static class GPXFile extends GPXExtensions {
public String author;
public List