From bd657b69578da9980b5c7c3d58fe5baef406ef96 Mon Sep 17 00:00:00 2001 From: Alexey Kulish Date: Thu, 23 Feb 2017 20:13:30 +0300 Subject: [PATCH] GPX info fixes --- OsmAnd/src/net/osmand/plus/GPXUtilities.java | 365 ---------------- .../net/osmand/plus/GpxSelectionHelper.java | 2 + .../ShowRouteInfoDialogFragment.java | 6 +- .../net/osmand/plus/helpers/GpxUiHelper.java | 406 ++++++++++++++++++ .../plus/myplaces/TrackSegmentFragment.java | 99 ++++- 5 files changed, 494 insertions(+), 384 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/GPXUtilities.java b/OsmAnd/src/net/osmand/plus/GPXUtilities.java index eb03beb37e..25b5e993a2 100644 --- a/OsmAnd/src/net/osmand/plus/GPXUtilities.java +++ b/OsmAnd/src/net/osmand/plus/GPXUtilities.java @@ -5,29 +5,13 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.drawable.Drawable; import android.support.annotation.ColorInt; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.Utils; import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.data.LocationPoint; import net.osmand.data.PointDescription; import net.osmand.data.RotatedTileBox; -import net.osmand.plus.myplaces.SelectedGPXFragment; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.Renderable; import net.osmand.util.Algorithms; @@ -51,7 +35,6 @@ import java.io.StringWriter; import java.io.Writer; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; -import java.text.MessageFormat; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -65,13 +48,6 @@ import java.util.Map; import java.util.Stack; import java.util.TimeZone; -import static com.github.mikephil.charting.components.XAxis.XAxisPosition.BOTTOM; -import static net.osmand.plus.OsmAndFormatter.FEET_IN_ONE_METER; -import static net.osmand.plus.OsmAndFormatter.METERS_IN_KILOMETER; -import static net.osmand.plus.OsmAndFormatter.METERS_IN_ONE_MILE; -import static net.osmand.plus.OsmAndFormatter.METERS_IN_ONE_NAUTICALMILE; -import static net.osmand.plus.OsmAndFormatter.YARDS_IN_ONE_METER; - public class GPXUtilities { public final static Log log = PlatformUtil.getLog(GPXUtilities.class); @@ -1306,345 +1282,4 @@ public class GPXUtilities { to.warning = from.warning; } } - - public static void setupGPXChart(OsmandApplication ctx, LineChart mChart, int yLabelsCount) { - OsmandSettings settings = ctx.getSettings(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); - boolean light = settings.isLightContent(); - - //mChart.setHardwareAccelerationEnabled(true); - mChart.setTouchEnabled(true); - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setPinchZoom(true); - mChart.setScaleYEnabled(false); - mChart.setAutoScaleMinMaxEnabled(true); - mChart.setDrawBorders(false); - mChart.getDescription().setEnabled(false); - mChart.setMaxVisibleValueCount(10); - mChart.setMinOffset(0f); - - mChart.setExtraTopOffset(24f); - mChart.setExtraBottomOffset(16f); - - // create a custom MarkerView (extend MarkerView) and specify the layout - // to use for it - SelectedGPXFragment.MyMarkerView mv = - new SelectedGPXFragment.MyMarkerView(mChart.getContext(), R.layout.chart_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart - mChart.setDrawMarkers(true); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setDrawAxisLine(false); - xAxis.setDrawGridLines(false); - xAxis.setPosition(BOTTOM); - xAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); - - YAxis yAxis = mChart.getAxisLeft(); - yAxis.enableGridDashedLine(10f, 5f, 0f); - yAxis.setGridColor(ActivityCompat.getColor(mChart.getContext(), R.color.divider_color)); - yAxis.setDrawAxisLine(false); - yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); - yAxis.setXOffset(16f); - yAxis.setYOffset(-6f); - yAxis.setLabelCount(yLabelsCount); - yAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); - - yAxis = mChart.getAxisRight(); - yAxis.enableGridDashedLine(10f, 5f, 0f); - yAxis.setGridColor(ActivityCompat.getColor(mChart.getContext(), R.color.divider_color)); - yAxis.setDrawAxisLine(false); - yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); - yAxis.setXOffset(16f); - yAxis.setYOffset(-6f); - yAxis.setLabelCount(yLabelsCount); - yAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); - yAxis.setEnabled(false); - - Legend legend = mChart.getLegend(); - legend.setEnabled(false); - } - - private static void getXAxisParams(OsmandApplication ctx, float meters, float[] koef, StringBuilder format, StringBuilder unit) { - OsmandSettings settings = ctx.getSettings(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - float divX; - - String format1 = "{0,number,0.#} "; - String format2 = "{0,number,0.##} "; - String fmt = null; - int mainUnitStr; - float mainUnitInMeters; - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { - mainUnitStr = R.string.km; - mainUnitInMeters = METERS_IN_KILOMETER; - } else if (mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES) { - mainUnitStr = R.string.nm; - mainUnitInMeters = METERS_IN_ONE_NAUTICALMILE; - } else { - mainUnitStr = R.string.mile; - mainUnitInMeters = METERS_IN_ONE_MILE; - } - if (meters > 9.99f * mainUnitInMeters) { - fmt = format1; - } - if (meters >= 100 * mainUnitInMeters || - meters > 9.99f * mainUnitInMeters || - meters > 0.999f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_FEET && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_METERS && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS && meters > 0.249f * mainUnitInMeters || - mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES && meters > 0.99f * mainUnitInMeters) { - - divX = mainUnitInMeters; - if (fmt == null) { - fmt = format2; - } - } else { - fmt = null; - if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS || mc == OsmandSettings.MetricsConstants.MILES_AND_METERS) { - divX = 1f; - mainUnitStr = R.string.m; - } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) { - divX = 1f / FEET_IN_ONE_METER; - mainUnitStr = R.string.foot; - } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS) { - divX = 1f / YARDS_IN_ONE_METER; - mainUnitStr = R.string.yard; - } else { - divX = 1f; - mainUnitStr = R.string.m; - } - } - - koef[0] = divX; - format.append(fmt); - unit.append(ctx.getString(mainUnitStr)); - } - - public static LineDataSet createGPXElevationDataSet(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis) { - OsmandSettings settings = ctx.getSettings(); - OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); - boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); - boolean light = settings.isLightContent(); - final float convEle = useFeet ? 3.28084f : 1.0f; - final float meters = analysis.totalDistance; - - float[] koef = new float[] { 1f }; - StringBuilder fmt = new StringBuilder(); - StringBuilder unitX = new StringBuilder(); - - getXAxisParams(ctx, meters, koef, fmt, unitX); - float divX = koef[0]; - final String formatX = fmt.toString(); - final String mainUnitX = unitX.toString(); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setValueFormatter(new IAxisValueFormatter() { - - @Override - public String getFormattedValue(float value, AxisBase axis) { - if (!Algorithms.isEmpty(formatX)) { - return MessageFormat.format(formatX + mainUnitX, value); - } else { - return (int)value + " " + mainUnitX; - } - } - }); - - final String mainUnitY = useFeet ? ctx.getString(R.string.foot) : ctx.getString(R.string.m); - - YAxis yAxis; - if (useRightAxis) { - yAxis = mChart.getAxisRight(); - yAxis.setEnabled(true); - ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsRight(mainUnitY); - } else { - yAxis = mChart.getAxisLeft(); - ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsLeft(mainUnitY); - } - if (analysis.minElevation >=0) { - yAxis.setAxisMinimum(0f); - } - yAxis.setValueFormatter(new IAxisValueFormatter() { - - @Override - public String getFormattedValue(float value, AxisBase axis) { - return (int)value + " " + mainUnitY; - } - }); - - ArrayList values = new ArrayList<>(); - List elevationData = analysis.elevationData; - float nextX = 0; - float nextY; - for (Elevation e : elevationData) { - if (e.distance > 0) { - nextX += (float) e.distance / divX; - nextY = (float) (e.elevation * convEle); - values.add(new Entry(nextX, nextY)); - } - } - - LineDataSet dataSet = new LineDataSet(values, ""); - - dataSet.setColor(Color.BLACK); - dataSet.setDrawValues(false); - dataSet.setLineWidth(0f); - dataSet.setValueTextSize(9f); - dataSet.setDrawFilled(true); - dataSet.setFormLineWidth(1f); - dataSet.setFormSize(15.f); - - dataSet.setDrawCircles(false); - dataSet.setDrawCircleHole(false); - - dataSet.setHighlightEnabled(true); - dataSet.setDrawVerticalHighlightIndicator(true); - dataSet.setDrawHorizontalHighlightIndicator(false); - dataSet.setHighLightColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); - - if (Utils.getSDKInt() >= 18) { - // fill drawable only supported on api level 18 and above - Drawable drawable = ContextCompat.getDrawable(mChart.getContext(), R.drawable.line_chart_fade_blue); - dataSet.setFillDrawable(drawable); - } else { - dataSet.setFillColor(ContextCompat.getColor(mChart.getContext(), R.color.transport_route_line)); - } - if (useRightAxis) { - dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT); - } - return dataSet; - } - - public static void setGPXElevationChartData(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis) { - LineDataSet dataSet = createGPXElevationDataSet(ctx, mChart, analysis, useRightAxis); - ArrayList dataSets = new ArrayList<>(); - dataSets.add(dataSet); - LineData data = new LineData(dataSets); - mChart.setData(data); - } - - public static LineDataSet createGPXSpeedDataSet(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis) { - OsmandSettings settings = ctx.getSettings(); - boolean light = settings.isLightContent(); - final float meters = analysis.totalDistance; - - float[] koef = new float[] { 1f }; - StringBuilder fmt = new StringBuilder(); - StringBuilder unitX = new StringBuilder(); - - getXAxisParams(ctx, meters, koef, fmt, unitX); - float divX = koef[0]; - final String formatX = fmt.toString(); - final String mainUnitX = unitX.toString(); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setValueFormatter(new IAxisValueFormatter() { - - @Override - public String getFormattedValue(float value, AxisBase axis) { - if (!Algorithms.isEmpty(formatX)) { - return MessageFormat.format(formatX + mainUnitX, value); - } else { - return (int)value + " " + mainUnitX; - } - } - }); - - OsmandSettings.SpeedConstants sps = settings.SPEED_SYSTEM.get(); - float mulSpeed = Float.NaN; - float divSpeed = Float.NaN; - final String mainUnitY = sps.toShortString(ctx); - if (sps == OsmandSettings.SpeedConstants.KILOMETERS_PER_HOUR) { - mulSpeed = 3.6f; - } else if (sps == OsmandSettings.SpeedConstants.MILES_PER_HOUR) { - mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_MILE; - } else if (sps == OsmandSettings.SpeedConstants.NAUTICALMILES_PER_HOUR) { - mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_NAUTICALMILE; - } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_KILOMETER) { - divSpeed = METERS_IN_KILOMETER / 60; - } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_MILE) { - divSpeed = METERS_IN_ONE_MILE / 60; - } else { - mulSpeed = 1f; - } - - YAxis yAxis; - if (useRightAxis) { - yAxis = mChart.getAxisRight(); - yAxis.setEnabled(true); - ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsRight(mainUnitY); - } else { - yAxis = mChart.getAxisLeft(); - ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsLeft(mainUnitY); - } - yAxis.setAxisMinimum(0f); - yAxis.setValueFormatter(new IAxisValueFormatter() { - - @Override - public String getFormattedValue(float value, AxisBase axis) { - return (int)value + " " + mainUnitY; - } - }); - - ArrayList values = new ArrayList<>(); - List speedData = analysis.speedData; - float nextX = 0; - float nextY; - for (Speed s : speedData) { - if (s.distance > 0) { - nextX += (float) s.distance / divX; - if (Float.isNaN(divSpeed)) { - nextY = (float) (s.speed * mulSpeed); - } else { - nextY = (float) (divSpeed / s.speed); - } - if (nextY < 0) { - nextY = 0; - } - values.add(new Entry(nextX, nextY)); - } - } - - LineDataSet dataSet = new LineDataSet(values, ""); - - dataSet.setColor(Color.BLACK); - dataSet.setDrawValues(false); - dataSet.setLineWidth(0f); - dataSet.setValueTextSize(9f); - dataSet.setDrawFilled(true); - dataSet.setFormLineWidth(1f); - dataSet.setFormSize(15.f); - - dataSet.setDrawCircles(false); - dataSet.setDrawCircleHole(false); - - dataSet.setHighlightEnabled(true); - dataSet.setDrawVerticalHighlightIndicator(true); - dataSet.setDrawHorizontalHighlightIndicator(false); - dataSet.setHighLightColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); - - if (Utils.getSDKInt() >= 18) { - // fill drawable only supported on api level 18 and above - Drawable drawable = ContextCompat.getDrawable(mChart.getContext(), R.drawable.line_chart_fade_red); - dataSet.setFillDrawable(drawable); - } else { - dataSet.setFillColor(ContextCompat.getColor(mChart.getContext(), R.color.transport_end)); - } - if (useRightAxis) { - dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT); - } - return dataSet; - } - - public static void setGPXSpeedChartData(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis) { - LineDataSet dataSet = createGPXSpeedDataSet(ctx, mChart, analysis, useRightAxis); - ArrayList dataSets = new ArrayList<>(); - dataSets.add(dataSet); - LineData data = new LineData(dataSets); - mChart.setData(data); - } } diff --git a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java index 24faf1991a..8914d89e3d 100644 --- a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java @@ -1,6 +1,7 @@ package net.osmand.plus; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; @@ -660,5 +661,6 @@ public class GpxSelectionHelper { public String url; public Bitmap image; public boolean expanded; + public Matrix chartMatrix; } } diff --git a/OsmAnd/src/net/osmand/plus/activities/ShowRouteInfoDialogFragment.java b/OsmAnd/src/net/osmand/plus/activities/ShowRouteInfoDialogFragment.java index 3463bd7135..da4b99bb01 100644 --- a/OsmAnd/src/net/osmand/plus/activities/ShowRouteInfoDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/ShowRouteInfoDialogFragment.java @@ -26,7 +26,6 @@ import android.widget.ListView; import android.widget.TextView; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.utils.Utils; import net.osmand.Location; import net.osmand.data.PointDescription; @@ -41,6 +40,7 @@ import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; +import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.mapcontextmenu.other.MapRouteInfoMenu; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; @@ -212,7 +212,7 @@ public class ShowRouteInfoDialogFragment extends DialogFragment { private void buildHeader(View headerView) { OsmandApplication app = getMyApplication(); LineChart mChart = (LineChart) headerView.findViewById(R.id.chart); - GPXUtilities.setupGPXChart(app, mChart, 4); + GpxUiHelper.setupGPXChart(app, mChart, 4); mChart.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -222,7 +222,7 @@ public class ShowRouteInfoDialogFragment extends DialogFragment { }); GPXTrackAnalysis analysis = gpx.getAnalysis(0); - GPXUtilities.setGPXElevationChartData(app, mChart, analysis, false); + GpxUiHelper.setGPXElevationChartData(app, mChart, analysis, false, true); ((TextView) headerView.findViewById(R.id.average_text)) .setText(OsmAndFormatter.getFormattedAlt(analysis.avgElevation, app)); diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index e6ca28516d..fb72865328 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -7,9 +7,11 @@ import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; +import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.ListPopupWindow; @@ -31,6 +33,21 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.DefaultFillFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.Utils; + import net.osmand.AndroidUtils; import net.osmand.CallbackWithObject; import net.osmand.IndexConstants; @@ -54,6 +71,7 @@ import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.dialogs.ConfigureMapMenu.AppearanceListItem; import net.osmand.plus.dialogs.ConfigureMapMenu.GpxAppearanceAdapter; +import net.osmand.plus.myplaces.SelectedGPXFragment; import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRulesStorage; import net.osmand.util.Algorithms; @@ -63,6 +81,7 @@ import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.text.DateFormat; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -70,6 +89,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.github.mikephil.charting.components.XAxis.XAxisPosition.BOTTOM; +import static net.osmand.plus.OsmAndFormatter.FEET_IN_ONE_METER; +import static net.osmand.plus.OsmAndFormatter.METERS_IN_KILOMETER; +import static net.osmand.plus.OsmAndFormatter.METERS_IN_ONE_MILE; +import static net.osmand.plus.OsmAndFormatter.METERS_IN_ONE_NAUTICALMILE; +import static net.osmand.plus.OsmAndFormatter.YARDS_IN_ONE_METER; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.refreshMapComplete; @@ -819,6 +844,387 @@ public class GpxUiHelper { }, "Loading gpx").start(); //$NON-NLS-1$ } + public static void setupGPXChart(OsmandApplication ctx, LineChart mChart, int yLabelsCount) { + OsmandSettings settings = ctx.getSettings(); + OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); + boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); + boolean light = settings.isLightContent(); + + //mChart.setHardwareAccelerationEnabled(true); + mChart.setTouchEnabled(true); + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + mChart.setPinchZoom(true); + mChart.setScaleYEnabled(false); + mChart.setAutoScaleMinMaxEnabled(true); + mChart.setDrawBorders(false); + mChart.getDescription().setEnabled(false); + mChart.setMaxVisibleValueCount(10); + mChart.setMinOffset(0f); + + mChart.setExtraTopOffset(24f); + mChart.setExtraBottomOffset(16f); + + // create a custom MarkerView (extend MarkerView) and specify the layout + // to use for it + SelectedGPXFragment.MyMarkerView mv = + new SelectedGPXFragment.MyMarkerView(mChart.getContext(), R.layout.chart_marker_view); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart + mChart.setDrawMarkers(true); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setDrawAxisLine(false); + xAxis.setDrawGridLines(false); + xAxis.setPosition(BOTTOM); + xAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); + + YAxis yAxis = mChart.getAxisLeft(); + yAxis.enableGridDashedLine(10f, 5f, 0f); + yAxis.setGridColor(ActivityCompat.getColor(mChart.getContext(), R.color.divider_color)); + yAxis.setDrawAxisLine(false); + yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + yAxis.setXOffset(16f); + yAxis.setYOffset(-6f); + yAxis.setLabelCount(yLabelsCount); + yAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); + + yAxis = mChart.getAxisRight(); + yAxis.enableGridDashedLine(10f, 5f, 0f); + yAxis.setGridColor(ActivityCompat.getColor(mChart.getContext(), R.color.divider_color)); + yAxis.setDrawAxisLine(false); + yAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + yAxis.setXOffset(16f); + yAxis.setYOffset(-6f); + yAxis.setLabelCount(yLabelsCount); + yAxis.setTextColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); + yAxis.setEnabled(false); + + Legend legend = mChart.getLegend(); + legend.setEnabled(false); + } + + private static float getXAxisParams(OsmandApplication ctx, float meters, float[] koef, StringBuilder format, StringBuilder unit) { + OsmandSettings settings = ctx.getSettings(); + OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); + float divX; + + String format1 = "{0,number,0.#} "; + String format2 = "{0,number,0.##} "; + String fmt = null; + float granularity = 1f; + int mainUnitStr; + float mainUnitInMeters; + if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS) { + mainUnitStr = R.string.km; + mainUnitInMeters = METERS_IN_KILOMETER; + } else if (mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES) { + mainUnitStr = R.string.nm; + mainUnitInMeters = METERS_IN_ONE_NAUTICALMILE; + } else { + mainUnitStr = R.string.mile; + mainUnitInMeters = METERS_IN_ONE_MILE; + } + if (meters > 9.99f * mainUnitInMeters) { + fmt = format1; + granularity = .1f; + } + if (meters >= 100 * mainUnitInMeters || + meters > 9.99f * mainUnitInMeters || + meters > 0.999f * mainUnitInMeters || + mc == OsmandSettings.MetricsConstants.MILES_AND_FEET && meters > 0.249f * mainUnitInMeters || + mc == OsmandSettings.MetricsConstants.MILES_AND_METERS && meters > 0.249f * mainUnitInMeters || + mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS && meters > 0.249f * mainUnitInMeters || + mc == OsmandSettings.MetricsConstants.NAUTICAL_MILES && meters > 0.99f * mainUnitInMeters) { + + divX = mainUnitInMeters; + if (fmt == null) { + fmt = format2; + granularity = .01f; + } + } else { + fmt = null; + granularity = 1f; + if (mc == OsmandSettings.MetricsConstants.KILOMETERS_AND_METERS || mc == OsmandSettings.MetricsConstants.MILES_AND_METERS) { + divX = 1f; + mainUnitStr = R.string.m; + } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) { + divX = 1f / FEET_IN_ONE_METER; + mainUnitStr = R.string.foot; + } else if (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS) { + divX = 1f / YARDS_IN_ONE_METER; + mainUnitStr = R.string.yard; + } else { + divX = 1f; + mainUnitStr = R.string.m; + } + } + + koef[0] = divX; + if (fmt != null) { + format.append(fmt); + } + unit.append(ctx.getString(mainUnitStr)); + return granularity; + } + + public static OrderedLineDataSet createGPXElevationDataSet(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis, boolean drawFilled) { + OsmandSettings settings = ctx.getSettings(); + OsmandSettings.MetricsConstants mc = settings.METRIC_SYSTEM.get(); + boolean useFeet = (mc == OsmandSettings.MetricsConstants.MILES_AND_FEET) || (mc == OsmandSettings.MetricsConstants.MILES_AND_YARDS); + boolean light = settings.isLightContent(); + final float convEle = useFeet ? 3.28084f : 1.0f; + final float meters = analysis.totalDistance; + + float[] koef = new float[] { 1f }; + StringBuilder fmt = new StringBuilder(); + StringBuilder unitX = new StringBuilder(); + + float granularity = getXAxisParams(ctx, meters, koef, fmt, unitX); + float divX = koef[0]; + final String formatX = fmt.toString(); + final String mainUnitX = unitX.toString(); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setGranularity(granularity); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + @Override + public String getFormattedValue(float value, AxisBase axis) { + if (!Algorithms.isEmpty(formatX)) { + return MessageFormat.format(formatX + mainUnitX, value); + } else { + return (int)value + " " + mainUnitX; + } + } + }); + + final String mainUnitY = useFeet ? ctx.getString(R.string.foot) : ctx.getString(R.string.m); + + YAxis yAxis; + if (useRightAxis) { + yAxis = mChart.getAxisRight(); + yAxis.setEnabled(true); + ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsRight(mainUnitY); + } else { + yAxis = mChart.getAxisLeft(); + ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsLeft(mainUnitY); + } + yAxis.setGranularity(1f); + yAxis.setValueFormatter(new IAxisValueFormatter() { + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return (int)value + " " + mainUnitY; + } + }); + + ArrayList values = new ArrayList<>(); + List elevationData = analysis.elevationData; + float nextX = 0; + float nextY; + for (GPXUtilities.Elevation e : elevationData) { + if (e.distance > 0) { + nextX += (float) e.distance / divX; + nextY = (float) (e.elevation * convEle); + values.add(new Entry(nextX, nextY)); + } + } + + OrderedLineDataSet dataSet = new OrderedLineDataSet(values, ""); + dataSet.priority = (float) (analysis.avgElevation - analysis.minElevation) * convEle; + if (drawFilled) { + dataSet.setColor(Color.BLACK); + dataSet.setLineWidth(0f); + dataSet.setDrawFilled(true); + } else { + dataSet.setColor(ContextCompat.getColor(mChart.getContext(), R.color.map_widget_blue)); + dataSet.setLineWidth(1f); + dataSet.setDrawFilled(false); + } + dataSet.setDrawValues(false); + dataSet.setValueTextSize(9f); + dataSet.setFormLineWidth(1f); + dataSet.setFormSize(15.f); + + dataSet.setDrawCircles(false); + dataSet.setDrawCircleHole(false); + + dataSet.setHighlightEnabled(true); + dataSet.setDrawVerticalHighlightIndicator(true); + dataSet.setDrawHorizontalHighlightIndicator(false); + dataSet.setHighLightColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); + + if (Utils.getSDKInt() >= 18) { + // fill drawable only supported on api level 18 and above + Drawable drawable = ContextCompat.getDrawable(mChart.getContext(), R.drawable.line_chart_fade_blue); + dataSet.setFillDrawable(drawable); + } else { + dataSet.setFillColor(ContextCompat.getColor(mChart.getContext(), R.color.transport_route_line)); + } + dataSet.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return dataProvider.getYChartMin(); + } + }); + if (useRightAxis) { + dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT); + } + return dataSet; + } + + public static void setGPXElevationChartData(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis, boolean drawFilled) { + LineDataSet dataSet = createGPXElevationDataSet(ctx, mChart, analysis, useRightAxis, drawFilled); + ArrayList dataSets = new ArrayList<>(); + dataSets.add(dataSet); + LineData data = new LineData(dataSets); + mChart.setData(data); + } + + public static OrderedLineDataSet createGPXSpeedDataSet(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis, boolean drawFilled) { + OsmandSettings settings = ctx.getSettings(); + boolean light = settings.isLightContent(); + final float meters = analysis.totalDistance; + + float[] koef = new float[] { 1f }; + StringBuilder fmt = new StringBuilder(); + StringBuilder unitX = new StringBuilder(); + + float granularity = getXAxisParams(ctx, meters, koef, fmt, unitX); + float divX = koef[0]; + final String formatX = fmt.toString(); + final String mainUnitX = unitX.toString(); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setGranularity(granularity); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + @Override + public String getFormattedValue(float value, AxisBase axis) { + if (!Algorithms.isEmpty(formatX)) { + return MessageFormat.format(formatX + mainUnitX, value); + } else { + return (int)value + " " + mainUnitX; + } + } + }); + + OsmandSettings.SpeedConstants sps = settings.SPEED_SYSTEM.get(); + float mulSpeed = Float.NaN; + float divSpeed = Float.NaN; + final String mainUnitY = sps.toShortString(ctx); + if (sps == OsmandSettings.SpeedConstants.KILOMETERS_PER_HOUR) { + mulSpeed = 3.6f; + } else if (sps == OsmandSettings.SpeedConstants.MILES_PER_HOUR) { + mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_MILE; + } else if (sps == OsmandSettings.SpeedConstants.NAUTICALMILES_PER_HOUR) { + mulSpeed = 3.6f * METERS_IN_KILOMETER / METERS_IN_ONE_NAUTICALMILE; + } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_KILOMETER) { + divSpeed = METERS_IN_KILOMETER / 60; + } else if (sps == OsmandSettings.SpeedConstants.MINUTES_PER_MILE) { + divSpeed = METERS_IN_ONE_MILE / 60; + } else { + mulSpeed = 1f; + } + + YAxis yAxis; + if (useRightAxis) { + yAxis = mChart.getAxisRight(); + yAxis.setEnabled(true); + ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsRight(mainUnitY); + } else { + yAxis = mChart.getAxisLeft(); + ((SelectedGPXFragment.MyMarkerView)mChart.getMarker()).setUnitsLeft(mainUnitY); + } + yAxis.setAxisMinimum(0f); + yAxis.setValueFormatter(new IAxisValueFormatter() { + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return (int)value + " " + mainUnitY; + } + }); + + ArrayList values = new ArrayList<>(); + List speedData = analysis.speedData; + float nextX = 0; + float nextY; + for (GPXUtilities.Speed s : speedData) { + if (s.distance > 0) { + nextX += (float) s.distance / divX; + if (Float.isNaN(divSpeed)) { + nextY = (float) (s.speed * mulSpeed); + } else { + nextY = (float) (divSpeed / s.speed); + } + if (nextY < 0) { + nextY = 0; + } + values.add(new Entry(nextX, nextY)); + } + } + + OrderedLineDataSet dataSet = new OrderedLineDataSet(values, ""); + + if (Float.isNaN(divSpeed)) { + dataSet.priority = analysis.avgSpeed * mulSpeed; + } else { + dataSet.priority = divSpeed / analysis.avgSpeed; + } + + if (drawFilled) { + dataSet.setColor(Color.BLACK); + dataSet.setLineWidth(0f); + dataSet.setDrawFilled(true); + } else { + dataSet.setColor(ContextCompat.getColor(mChart.getContext(), R.color.transport_end)); + dataSet.setLineWidth(1f); + dataSet.setDrawFilled(false); + } + dataSet.setDrawValues(false); + dataSet.setValueTextSize(9f); + dataSet.setFormLineWidth(1f); + dataSet.setFormSize(15.f); + + dataSet.setDrawCircles(false); + dataSet.setDrawCircleHole(false); + + dataSet.setHighlightEnabled(true); + dataSet.setDrawVerticalHighlightIndicator(true); + dataSet.setDrawHorizontalHighlightIndicator(false); + dataSet.setHighLightColor(light ? mChart.getResources().getColor(R.color.secondary_text_light) : mChart.getResources().getColor(R.color.secondary_text_dark)); + + if (Utils.getSDKInt() >= 18) { + // fill drawable only supported on api level 18 and above + Drawable drawable = ContextCompat.getDrawable(mChart.getContext(), R.drawable.line_chart_fade_red); + dataSet.setFillDrawable(drawable); + } else { + dataSet.setFillColor(ContextCompat.getColor(mChart.getContext(), R.color.transport_end)); + } + if (useRightAxis) { + dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT); + } + return dataSet; + } + + public static void setGPXSpeedChartData(OsmandApplication ctx, LineChart mChart, GPXTrackAnalysis analysis, boolean useRightAxis, boolean drawFilled) { + LineDataSet dataSet = createGPXSpeedDataSet(ctx, mChart, analysis, useRightAxis, drawFilled); + ArrayList dataSets = new ArrayList<>(); + dataSets.add(dataSet); + LineData data = new LineData(dataSets); + mChart.setData(data); + } + + public static class OrderedLineDataSet extends LineDataSet { + + public float priority; + + public OrderedLineDataSet(List yVals, String label) { + super(yVals, label); + } + } + public static class GPXInfo { private String fileName; private long lastModified; diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java index dad76f72f3..87854dade9 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java @@ -1,6 +1,7 @@ package net.osmand.plus.myplaces; import android.content.Context; +import android.graphics.Matrix; import android.support.annotation.NonNull; import android.support.v4.view.MenuItemCompat; import android.support.v4.view.PagerAdapter; @@ -19,11 +20,11 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture; +import com.github.mikephil.charting.listener.OnChartGestureListener; import net.osmand.AndroidUtils; -import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXTrackAnalysis; import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; @@ -32,6 +33,8 @@ import net.osmand.plus.IconsCache; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.helpers.GpxUiHelper; +import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; import net.osmand.plus.views.controls.PagerSlidingTabStrip; import net.osmand.plus.views.controls.PagerSlidingTabStrip.CustomTabProvider; import net.osmand.plus.views.controls.WrapContentHeightViewPager; @@ -99,6 +102,7 @@ public class TrackSegmentFragment extends SelectedGPXFragment { public View getView(int position, View convertView, @NonNull ViewGroup parent) { View row = convertView; PagerSlidingTabStrip tabLayout = null; + WrapContentHeightViewPager pager; if (row == null) { LayoutInflater inflater = getMyActivity().getLayoutInflater(); row = inflater.inflate(R.layout.gpx_list_item_tab_content, parent, false); @@ -113,12 +117,15 @@ public class TrackSegmentFragment extends SelectedGPXFragment { tabLayout.setTextSize(AndroidUtils.spToPx(app, 12f)); tabLayout.setShouldExpand(true); tabLayout.setTabSelectionType(PagerSlidingTabStrip.TabSelectionType.SOLID_COLOR); + pager = (WrapContentHeightViewPager) row.findViewById(R.id.pager); + pager.setSwipeable(false); + } else { + pager = (WrapContentHeightViewPager) row.findViewById(R.id.pager); } if (tabLayout != null) { - final WrapContentHeightViewPager pager = (WrapContentHeightViewPager) row.findViewById(R.id.pager); pager.setAdapter(getPagerAdapter(tabLayout, getItem(position))); - pager.setSwipeable(false); + pager.setOffscreenPageLimit(2); tabLayout.setViewPager(pager); } @@ -209,7 +216,7 @@ public class TrackSegmentFragment extends SelectedGPXFragment { OsmandApplication app = (OsmandApplication) getActivity().getApplicationContext(); if (gpxItem != null) { GPXTrackAnalysis analysis = gpxItem.analysis; - LineChart chart = (LineChart) view.findViewById(R.id.chart); + final LineChart chart = (LineChart) view.findViewById(R.id.chart); chart.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -217,22 +224,72 @@ public class TrackSegmentFragment extends SelectedGPXFragment { return false; } }); + final View finalView = view; + chart.setOnChartGestureListener(new OnChartGestureListener() { + @Override + public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) { + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) { + gpxItem.chartMatrix = new Matrix(chart.getViewPortHandler().getMatrixTouch()); + for (int i = 0; i < getCount(); i++) { + View v = getViewAtPosition(i); + if (v != finalView) { + updateChart(i); + } + } + } + + @Override + public void onChartLongPressed(MotionEvent me) { + } + + @Override + public void onChartDoubleTapped(MotionEvent me) { + } + + @Override + public void onChartSingleTapped(MotionEvent me) { + } + + @Override + public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + } + + @Override + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + } + + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + } + }); IconsCache ic = app.getIconsCache(); switch (tabType) { case GPX_TAB_ITEM_GENERAL: if (analysis != null) { - ArrayList dataSets = new ArrayList<>(); + List dataSets = new ArrayList<>(); if (analysis.elevationData != null || analysis.isSpeedSpecified()) { - GPXUtilities.setupGPXChart(app, chart, 4); + GpxUiHelper.setupGPXChart(app, chart, 4); + OrderedLineDataSet speedDataSet = null; + OrderedLineDataSet elevationDataSet = null; if (analysis.isSpeedSpecified()) { - LineDataSet dataSet = GPXUtilities.createGPXSpeedDataSet(app, chart, analysis, true); - dataSets.add(dataSet); + speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, analysis, true, true); } if (analysis.elevationData != null) { - LineDataSet dataSet = GPXUtilities.createGPXElevationDataSet(app, chart, analysis, false); - dataSets.add(dataSet); + elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, analysis, false, true); } + if (speedDataSet != null) { + dataSets.add(speedDataSet); + if (elevationDataSet != null) { + dataSets.add(elevationDataSet.priority < speedDataSet.priority ? 1 : 0, elevationDataSet); + } + } else if (elevationDataSet != null) { + dataSets.add(elevationDataSet); + } + LineData data = new LineData(dataSets); chart.setData(data); chart.setVisibility(View.VISIBLE); @@ -285,8 +342,8 @@ public class TrackSegmentFragment extends SelectedGPXFragment { case GPX_TAB_ITEM_ALTITUDE: if (analysis != null) { if (analysis.elevationData != null) { - GPXUtilities.setupGPXChart(app, chart, 4); - GPXUtilities.setGPXElevationChartData(app, chart, analysis, false); + GpxUiHelper.setupGPXChart(app, chart, 4); + GpxUiHelper.setGPXElevationChartData(app, chart, analysis, false, true); chart.setVisibility(View.VISIBLE); } else { chart.setVisibility(View.GONE); @@ -327,9 +384,8 @@ public class TrackSegmentFragment extends SelectedGPXFragment { break; case GPX_TAB_ITEM_SPEED: if (analysis != null && analysis.isSpeedSpecified()) { - GPXUtilities.setupGPXChart(app, chart, 4); - GPXUtilities.setGPXSpeedChartData(app, chart, analysis, false); - + GpxUiHelper.setupGPXChart(app, chart, 4); + GpxUiHelper.setGPXSpeedChartData(app, chart, analysis, false, true); ((ImageView) view.findViewById(R.id.average_icon)) .setImageDrawable(ic.getThemedIcon(R.drawable.ic_action_speed)); ((ImageView) view.findViewById(R.id.max_icon)) @@ -442,5 +498,16 @@ public class TrackSegmentFragment extends SelectedGPXFragment { public View getViewAtPosition(int position) { return views.get(position); } + + void updateChart(int position) { + View view = getViewAtPosition(position); + updateChart((LineChart) view.findViewById(R.id.chart)); + } + + void updateChart(LineChart chart) { + if (gpxItem.chartMatrix != null) { + chart.getViewPortHandler().refresh(new Matrix(gpxItem.chartMatrix), chart, true); + } + } } }