Gpx chart fixes. Added multi chart to details view

This commit is contained in:
Alexey Kulish 2017-03-09 20:08:17 +03:00
parent 260480aa10
commit 90af3e2445
8 changed files with 176 additions and 77 deletions

View file

@ -301,6 +301,9 @@ public class GPXUtilities {
public List<Elevation> elevationData;
public List<Speed> speedData;
public boolean hasElevationData;
public boolean hasSpeedData;
public boolean isSpeedSpecified() {
return avgSpeed > 0;
}
@ -472,9 +475,15 @@ public class GPXUtilities {
elevation1.time = timeDiff;
elevation1.distance = (j > 0) ? calculations[0] : 0;
elevationData.add(elevation1);
if (!hasElevationData && !Float.isNaN(elevation1.elevation) && totalDistance > 0) {
hasElevationData = true;
}
speed1.time = timeDiff;
speed1.distance = elevation1.distance;
speedData.add(speed1);
if (!hasSpeedData && speed1.speed > 0 && totalDistance > 0) {
hasSpeedData = true;
}
}
}
if (!isTimeSpecified()) {

View file

@ -676,7 +676,7 @@ public class GpxSelectionHelper {
public boolean route;
public WptPt locationOnMap;
public GPXDataSetType chartType;
public GPXDataSetType[] chartTypes;
public GPXDataSetAxisType chartAxisType = GPXDataSetAxisType.DISTANCE;
public Matrix chartMatrix;

View file

@ -49,6 +49,7 @@ import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
import net.osmand.plus.mapcontextmenu.other.MapRouteInfoMenu;
import net.osmand.plus.routing.RouteDirectionInfo;
@ -75,6 +76,7 @@ public class ShowRouteInfoDialogFragment extends DialogFragment {
private RouteInfoAdapter adapter;
private GPXFile gpx;
private OrderedLineDataSet elevationDataSet;
private OrderedLineDataSet slopeDataSet;
private GpxDisplayItem gpxItem;
private boolean hasHeights;
@ -241,20 +243,21 @@ public class ShowRouteInfoDialogFragment extends DialogFragment {
});
GPXTrackAnalysis analysis = gpx.getAnalysis(0);
if (analysis.totalDistance > 0) {
if (analysis.hasElevationData) {
List<ILineDataSet> dataSets = new ArrayList<>();
elevationDataSet =
GpxUiHelper.createGPXElevationDataSet(app, mChart, analysis, GPXDataSetAxisType.DISTANCE, false, true);
elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, mChart, analysis,
GPXDataSetAxisType.DISTANCE, false, true);
dataSets.add(elevationDataSet);
if (analysis.elevationData.size() > 1) {
OrderedLineDataSet slopeDataSet =
GpxUiHelper.createGPXSlopeDataSet(app, mChart, analysis, GPXDataSetAxisType.DISTANCE, elevationDataSet.getValues(), true, true);
slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, mChart, analysis,
GPXDataSetAxisType.DISTANCE, elevationDataSet.getValues(), true, true);
dataSets.add(slopeDataSet);
}
LineData data = new LineData(dataSets);
mChart.setData(data);
mChart.setVisibility(View.VISIBLE);
} else {
elevationDataSet = null;
slopeDataSet = null;
mChart.setVisibility(View.GONE);
}
((TextView) headerView.findViewById(R.id.average_text))
@ -291,7 +294,7 @@ public class ShowRouteInfoDialogFragment extends DialogFragment {
if (gpxItem != null) {
LatLon location = null;
WptPt wpt = null;
gpxItem.chartType = GpxUiHelper.GPXDataSetType.ALTITUDE;
gpxItem.chartTypes = new GPXDataSetType[] { GPXDataSetType.ALTITUDE, GPXDataSetType.SLOPE };
if (gpxItem.chartHighlightPos != -1) {
TrkSegment segment = gpx.tracks.get(0).segments.get(0);
if (segment != null) {

View file

@ -12,6 +12,7 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
@ -865,6 +866,7 @@ public class GpxUiHelper {
mChart.getDescription().setEnabled(false);
mChart.setMaxVisibleValueCount(10);
mChart.setMinOffset(0f);
mChart.setDragDecelerationEnabled(false);
mChart.setExtraTopOffset(24f);
mChart.setExtraBottomOffset(16f);
@ -1443,7 +1445,7 @@ public class GpxUiHelper {
this.imageId = imageId;
}
public String getName(Context ctx) {
public String getName(@NonNull Context ctx) {
return ctx.getString(stringId);
}
@ -1455,9 +1457,33 @@ public class GpxUiHelper {
return imageId;
}
public Drawable getImageDrawable(OsmandApplication app) {
public Drawable getImageDrawable(@NonNull OsmandApplication app) {
return app.getIconsCache().getThemedIcon(imageId);
}
public static String getName(@NonNull Context ctx, @NonNull GPXDataSetType[] types) {
List<String> list = new ArrayList<>();
for (GPXDataSetType type : types) {
list.add(type.getName(ctx));
}
Collections.sort(list);
StringBuilder sb = new StringBuilder();
for (String s : list) {
if (sb.length() > 0) {
sb.append("/");
}
sb.append(s);
}
return sb.toString();
}
public static Drawable getImageDrawable(@NonNull OsmandApplication app, @NonNull GPXDataSetType[] types) {
if (types.length > 0) {
return types[0].getImageDrawable(app);
} else {
return null;
}
}
}
public enum GPXDataSetAxisType {

View file

@ -639,7 +639,7 @@ public class MapRouteInfoMenu implements IRouteInformationListener {
}
}
mapActivity.getMapView().fitLocationToMap(latitude, longitude, mapActivity.getMapView().getZoom(),
tileBoxWidthPx, tileBoxHeightPx, AndroidUtils.dpToPx(mapActivity, 40f));
tileBoxWidthPx, tileBoxHeightPx, AndroidUtils.dpToPx(mapActivity, 40f), true);
}
@Override

View file

@ -15,6 +15,7 @@ import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.listener.ChartTouchListener;
import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture;
import com.github.mikephil.charting.listener.OnChartGestureListener;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
@ -41,6 +42,8 @@ import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControll
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TrackDetailsMenu {
@ -239,7 +242,7 @@ public class TrackDetailsMenu {
return new QuadRect(left, top, right, bottom);
}
private void fitTrackOnMap(LineChart chart) {
private void fitTrackOnMap(LineChart chart, LatLon location, boolean forceFit) {
QuadRect rect = getRect(chart, chart.getLowestVisibleX(), chart.getHighestVisibleX());
if (rect != null) {
RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox().copy();
@ -256,30 +259,41 @@ public class TrackDetailsMenu {
tileBoxHeightPx = tb.getPixHeight() - f.getHeight();
}
}
if (forceFit) {
mapActivity.getMapView().fitRectToMap(rect.left, rect.right, rect.top, rect.bottom,
tileBoxWidthPx, tileBoxHeightPx, 0);
} else if (location != null &&
!mapActivity.getMapView().getTileBox(tileBoxWidthPx, tileBoxHeightPx, 0).containsLatLon(location)) {
boolean animating = mapActivity.getMapView().getAnimatedDraggingThread().isAnimating();
mapActivity.getMapView().fitLocationToMap(location.getLatitude(), location.getLongitude(),
mapActivity.getMapView().getZoom(), tileBoxWidthPx, tileBoxHeightPx, 0, !animating);
} else {
mapActivity.refreshMap();
}
}
}
private void refreshChart(LineChart chart) {
private void refreshChart(LineChart chart, boolean forceFit) {
gpxItem.chartMatrix = new Matrix(chart.getViewPortHandler().getMatrixTouch());
Highlight[] highlights = chart.getHighlighted();
LatLon location = null;
if (highlights != null && highlights.length > 0) {
gpxItem.chartHighlightPos = highlights[0].getX();
WptPt wpt = getPoint(chart, gpxItem.chartHighlightPos);
if (wpt != null) {
location = new LatLon(wpt.lat, wpt.lon);
if (gpxItem.route) {
mapActivity.getMapLayers().getMapInfoLayer().setSelectedPointLatLon(new LatLon(wpt.lat, wpt.lon));
mapActivity.getMapLayers().getMapInfoLayer().setSelectedPointLatLon(location);
} else {
mapActivity.getMapLayers().getGpxLayer().setSelectedPointLatLon(new LatLon(wpt.lat, wpt.lon));
mapActivity.getMapLayers().getGpxLayer().setSelectedPointLatLon(location);
}
//mapActivity.setMapLocation(wpt.lat, wpt.lon);
}
} else {
gpxItem.chartHighlightPos = -1;
}
fitTrackOnMap(chart);
fitTrackOnMap(chart, location, forceFit);
}
private void updateView(final View parentView) {
@ -287,7 +301,7 @@ public class TrackDetailsMenu {
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
refreshChart(chart);
refreshChart(chart, false);
}
@Override
@ -296,13 +310,24 @@ public class TrackDetailsMenu {
}
});
chart.setOnChartGestureListener(new OnChartGestureListener() {
boolean hasTranslated = false;
@Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) {
hasTranslated = false;
}
@Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
refreshChart(chart);
public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) {
if ((lastPerformedGesture == ChartGesture.DRAG && hasTranslated) ||
lastPerformedGesture == ChartGesture.X_ZOOM ||
lastPerformedGesture == ChartGesture.Y_ZOOM ||
lastPerformedGesture == ChartGesture.PINCH_ZOOM ||
lastPerformedGesture == ChartGesture.DOUBLE_TAP ||
lastPerformedGesture == ChartGesture.ROTATE) {
refreshChart(chart, true);
}
}
@Override
@ -327,6 +352,7 @@ public class TrackDetailsMenu {
@Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {
hasTranslated = true;
}
});
@ -336,27 +362,37 @@ public class TrackDetailsMenu {
if (analysis != null) {
GpxUiHelper.setupGPXChart(app, chart, 4);
if (gpxItem.chartType != null) {
if (gpxItem.chartTypes != null) {
List<ILineDataSet> dataSets = new ArrayList<>();
if (gpxItem.chartTypes != null && gpxItem.chartTypes.length > 0) {
for (int i = 0; i < gpxItem.chartTypes.length; i++) {
OrderedLineDataSet dataSet = null;
if (gpxItem.chartType != null) {
switch (gpxItem.chartType) {
switch (gpxItem.chartTypes[i]) {
case ALTITUDE:
dataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, analysis,
gpxItem.chartAxisType, false, true);
break;
case SPEED:
dataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, analysis,
gpxItem.chartAxisType, false, true);
gpxItem.chartAxisType, gpxItem.chartTypes.length > 1, true);
break;
case SLOPE:
dataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, analysis,
gpxItem.chartAxisType, null, false, true);
gpxItem.chartAxisType, null, gpxItem.chartTypes.length > 1, true);
break;
}
}
dataSets.add(dataSet);
}
}
Collections.sort(dataSets, new Comparator<ILineDataSet>() {
@Override
public int compare(ILineDataSet ds1, ILineDataSet ds2) {
OrderedLineDataSet dataSet1 = (OrderedLineDataSet) ds1;
OrderedLineDataSet dataSet2 = (OrderedLineDataSet) ds2;
return dataSet1.getPriority() > dataSet2.getPriority() ? -1 : (dataSet1.getPriority() == dataSet2.getPriority() ? 0 : 1);
}
});
chart.setData(new LineData(dataSets));
updateChart(chart);
}
@ -366,31 +402,38 @@ public class TrackDetailsMenu {
ImageView yAxisIcon = (ImageView) parentView.findViewById(R.id.y_axis_icon);
TextView yAxisTitle = (TextView) parentView.findViewById(R.id.y_axis_title);
View yAxisArrow = parentView.findViewById(R.id.y_axis_arrow);
final List<GPXDataSetType> availableTypes = new ArrayList<>();
final List<GPXDataSetType[]> availableTypes = new ArrayList<>();
if (analysis != null) {
if (analysis.elevationData != null) {
availableTypes.add(GPXDataSetType.ALTITUDE);
availableTypes.add(GPXDataSetType.SLOPE);
if (analysis.hasElevationData) {
availableTypes.add(new GPXDataSetType[] { GPXDataSetType.ALTITUDE });
availableTypes.add(new GPXDataSetType[] { GPXDataSetType.SLOPE });
}
if (analysis.isSpeedSpecified()) {
availableTypes.add(GPXDataSetType.SPEED);
if (analysis.hasSpeedData) {
availableTypes.add(new GPXDataSetType[] { GPXDataSetType.SPEED });
}
if (analysis.hasElevationData) {
availableTypes.add(new GPXDataSetType[] { GPXDataSetType.ALTITUDE, GPXDataSetType.SLOPE });
}
if (analysis.hasElevationData && analysis.hasSpeedData) {
availableTypes.add(new GPXDataSetType[] { GPXDataSetType.ALTITUDE, GPXDataSetType.SPEED });
}
}
availableTypes.remove(gpxItem.chartType);
yAxisIcon.setImageDrawable(gpxItem.chartType.getImageDrawable(app));
yAxisTitle.setText(gpxItem.chartType.getName(app));
yAxisIcon.setImageDrawable(GPXDataSetType.getImageDrawable(app, gpxItem.chartTypes));
yAxisTitle.setText(GPXDataSetType.getName(app, gpxItem.chartTypes));
if (availableTypes.size() > 0) {
yAxis.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final PopupMenu optionsMenu = new PopupMenu(mapActivity, v);
DirectionsDialogs.setupPopUpMenuIcon(optionsMenu);
for (final GPXDataSetType type : availableTypes) {
MenuItem menuItem = optionsMenu.getMenu().add(type.getStringId()).setIcon(type.getImageDrawable(app));
for (final GPXDataSetType[] types : availableTypes) {
MenuItem menuItem = optionsMenu.getMenu()
.add(GPXDataSetType.getName(app, types))
.setIcon(GPXDataSetType.getImageDrawable(app, types));
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem mItem) {
gpxItem.chartType = type;
gpxItem.chartTypes = types;
update();
return true;
}
@ -451,7 +494,7 @@ public class TrackDetailsMenu {
xAxisArrow.setVisibility(View.GONE);
}
refreshChart(chart);
refreshChart(chart, true);
}
private void updateChart(LineChart chart) {
@ -480,7 +523,6 @@ public class TrackDetailsMenu {
@Override
public void updateToolbar(MapInfoWidgetsFactory.TopToolbarView view) {
super.updateToolbar(view);
//view.getCloseButton().setVisibility(View.GONE);
view.getShadowView().setVisibility(View.GONE);
}
}

View file

@ -640,11 +640,11 @@ public class TrackSegmentFragment extends OsmAndListFragment {
case GPX_TAB_ITEM_GENERAL: {
OrderedLineDataSet speedDataSet = null;
OrderedLineDataSet elevationDataSet = null;
if (analysis.isSpeedSpecified()) {
if (analysis.hasSpeedData) {
speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
analysis, GPXDataSetAxisType.DISTANCE, true, true);
}
if (analysis.elevationData != null) {
if (analysis.hasElevationData) {
elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
analysis, GPXDataSetAxisType.DISTANCE, false, true);
}
@ -664,7 +664,7 @@ public class TrackSegmentFragment extends OsmAndListFragment {
OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
analysis, GPXDataSetAxisType.DISTANCE, false, true);
dataSets.add(elevationDataSet);
if (analysis.elevationData.size() > 1) {
if (analysis.hasElevationData) {
OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart,
analysis, GPXDataSetAxisType.DISTANCE, elevationDataSet.getValues(), true, true);
dataSets.add(slopeDataSet);
@ -775,8 +775,7 @@ public class TrackSegmentFragment extends OsmAndListFragment {
switch (tabType) {
case GPX_TAB_ITEM_GENERAL:
if (analysis != null) {
if ((analysis.elevationData != null && analysis.totalDistance > 0)
|| analysis.isSpeedSpecified()) {
if (analysis.hasElevationData || analysis.hasSpeedData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_GENERAL, chart)));
updateChart(chart);
@ -829,7 +828,7 @@ public class TrackSegmentFragment extends OsmAndListFragment {
break;
case GPX_TAB_ITEM_ALTITUDE:
if (analysis != null) {
if (analysis.elevationData != null && analysis.totalDistance > 0) {
if (analysis.hasElevationData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, chart)));
updateChart(chart);
@ -873,7 +872,7 @@ public class TrackSegmentFragment extends OsmAndListFragment {
break;
case GPX_TAB_ITEM_SPEED:
if (analysis != null && analysis.isSpeedSpecified()) {
if (analysis.totalDistance > 0) {
if (analysis.hasSpeedData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_SPEED, chart)));
updateChart(chart);
@ -1013,16 +1012,13 @@ public class TrackSegmentFragment extends OsmAndListFragment {
void openDetails(GPXTabItemType tabType) {
LatLon location = null;
WptPt wpt = null;
gpxItem.chartType = null;
gpxItem.chartTypes = null;
List<ILineDataSet> ds = getDataSets(tabType, null);
if (ds != null && ds.size() > 0) {
for (ILineDataSet dataSet : ds) {
OrderedLineDataSet orderedDataSet = (OrderedLineDataSet) dataSet;
if (orderedDataSet.getDataSetType() == GPXDataSetType.ALTITUDE) {
gpxItem.chartType = GPXDataSetType.ALTITUDE;
break;
}
gpxItem.chartType = orderedDataSet.getDataSetType();
gpxItem.chartTypes = new GPXDataSetType[ds.size()];
for (int i = 0; i < ds.size(); i++) {
OrderedLineDataSet orderedDataSet = (OrderedLineDataSet) ds.get(i);
gpxItem.chartTypes[i] = orderedDataSet.getDataSetType();
}
if (gpxItem.chartHighlightPos != -1) {
TrkSegment segment = null;

View file

@ -784,11 +784,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
tb.setPixelDimensions(tbw, tbh);
double clat = bottom / 2 + top / 2;
//double clat = 5 * bottom / 4 - top / 4;
double clon = left / 2 + right / 2;
// landscape mode
// double clat = bottom / 2 + top / 2;
// double clon = 5 * left / 4 - right / 4;
tb.setLatLonCenter(clat, clon);
while (tb.getZoom() < 17 && tb.containsLatLon(top, left) && tb.containsLatLon(bottom, right)) {
tb.setZoom(tb.getZoom() + 1);
@ -803,8 +799,31 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
animatedDraggingThread.startMoving(clat, clon, tb.getZoom(), true);
}
public RotatedTileBox getTileBox(int tileBoxWidthPx, int tileBoxHeightPx, int marginTopPx) {
RotatedTileBox tb = currentViewport.copy();
double border = 0.8;
int dy = 0;
int tbw = (int) (tb.getPixWidth() * border);
int tbh = (int) (tb.getPixHeight() * border);
if (tileBoxWidthPx > 0) {
tbw = (int) (tileBoxWidthPx * border);
} else if (tileBoxHeightPx > 0) {
tbh = (int) (tileBoxHeightPx * border);
dy = (tb.getPixHeight() - tileBoxHeightPx) / 2 - marginTopPx;
}
tb.setPixelDimensions(tbw, tbh);
if (dy != 0) {
double clat = tb.getLatFromPixel(tb.getPixWidth() / 2, tb.getPixHeight() / 2 - dy);
double clon = tb.getLonFromPixel(tb.getPixWidth() / 2, tb.getPixHeight() / 2);
tb.setLatLonCenter(clat, clon);
}
return tb;
}
public void fitLocationToMap(double clat, double clon, int zoom,
int tileBoxWidthPx, int tileBoxHeightPx, int marginTopPx) {
int tileBoxWidthPx, int tileBoxHeightPx, int marginTopPx, boolean animated) {
RotatedTileBox tb = currentViewport.copy();
int dy = 0;
@ -823,7 +842,11 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
clat = tb.getLatFromPixel(tb.getPixWidth() / 2, tb.getPixHeight() / 2 + dy);
clon = tb.getLonFromPixel(tb.getPixWidth() / 2, tb.getPixHeight() / 2);
}
if (animated) {
animatedDraggingThread.startMoving(clat, clon, tb.getZoom(), true);
} else {
setLatLon(clat, clon);
}
}
public boolean onGenericMotionEvent(MotionEvent event) {