Add my location projection to analyze gpx screen

This commit is contained in:
Vitaliy 2020-06-24 18:07:05 +03:00
parent f4bcbc46a6
commit 1cce2705ee
5 changed files with 110 additions and 40 deletions

View file

@ -100,7 +100,7 @@ public class SelectedGpxMenuBuilder extends MenuBuilder {
String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app);
String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app);
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_range_16), null, app.getString(R.string.altitude_range),
buildRow(view, getThemedIcon(R.drawable.ic_action_altitude_range), null, app.getString(R.string.altitude_range),
min + " - " + max, 0, null,
false, null, false, 0, false, false, false, null, false);

View file

@ -57,7 +57,7 @@ public class SelectedGpxMenuController extends MenuController {
}
};
rightTitleButtonController.caption = mapActivity.getString(R.string.analyze_on_map);
rightTitleButtonController.startIconId = R.drawable.ic_action_track_16;
rightTitleButtonController.startIconId = R.drawable.ic_action_analyze_intervals;
}
private static class OpenGpxDetailsTask extends AsyncTask<Void, Void, GpxSelectionHelper.GpxDisplayItem> {

View file

@ -26,6 +26,7 @@ import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox;
@ -39,6 +40,7 @@ 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.views.GPXLayer;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
@ -51,6 +53,8 @@ import static net.osmand.plus.SimplePopUpMenuItemAdapter.SimplePopUpMenuItem;
public class TrackDetailsMenu {
private static final int MAX_DISTANCE_LOCATION_PROJECTION = 20; // in meters
@Nullable
private MapActivity mapActivity;
@Nullable
@ -66,9 +70,7 @@ public class TrackDetailsMenu {
private int topMarginPx;
private boolean visible;
private boolean hidding;
public TrackDetailsMenu() {
}
private Location myLocation;
@Nullable
public MapActivity getMapActivity() {
@ -123,6 +125,35 @@ public class TrackDetailsMenu {
}
}
public void updateMyLocation(View mainView, Location location) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
LineChart chart = mainView.findViewById(R.id.chart);
GpxDisplayItem gpxItem = getGpxItem();
TrkSegment segment = getTrackSegment(chart);
LineData lineData = chart.getLineData();
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
if (ds != null && ds.size() > 0 && gpxItem != null && segment != null) {
RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox();
int mx = (int) tb.getPixXFromLatLon(location.getLatitude(), location.getLongitude());
int my = (int) tb.getPixYFromLatLon(location.getLatitude(), location.getLongitude());
int r = (int) (MAX_DISTANCE_LOCATION_PROJECTION * tb.getPixDensity());
WptPt point = GPXLayer.findPointNearSegment(tb, segment.points, r, mx, my);
if (point != null) {
int index = segment.points.indexOf(point);
gpxItem.locationOnMap = GPXLayer.createProjectionPoint(segment.points.get(index - 1), point, tb.getLatLonFromPixel(mx, my));
gpxItem.chartHighlightPos = (float) (gpxItem.locationOnMap.distance / ((OrderedLineDataSet) ds.get(0)).getDivX());
chart.highlightValue(gpxItem.chartHighlightPos, 0);
}
myLocation = location;
}
}
}
public Location getMyLocation() {
return myLocation;
}
public void update() {
TrackDetailsMenuFragment fragment = getMenuFragment();
if (fragment != null) {

View file

@ -14,25 +14,31 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils;
import net.osmand.Location;
import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.MapContextMenu;
import net.osmand.util.MapUtils;
public class TrackDetailsMenuFragment extends BaseOsmAndFragment implements OsmAndLocationListener {
public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
public static final String TAG = "TrackDetailsMenuFragment";
private TrackDetailsMenu menu;
private View mainView;
private boolean paused = true;
private boolean nightMode;
private boolean locationUpdateStarted;
@Nullable
private MapActivity getMapActivity() {
return (MapActivity) getActivity();
@ -49,9 +55,7 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
MapActivity mapActivity = requireMapActivity();
menu = mapActivity.getTrackDetailsMenu();
nightMode = mapActivity.getMyApplication().getDaynightHelper().isNightModeForMapControls();
ContextThemeWrapper context =
new ContextThemeWrapper(mapActivity, !nightMode ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme);
View view = LayoutInflater.from(context).inflate(R.layout.track_details, container, false);
View view = UiUtilities.getInflater(mapActivity, nightMode).inflate(R.layout.track_details, container, false);
if (!AndroidUiHelper.isOrientationPortrait(mapActivity)) {
AndroidUtils.addStatusBarPadding21v(mapActivity, view);
}
@ -129,13 +133,44 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
} else {
menu.onShow();
}
paused = false;
startLocationUpdate();
}
@Override
public void onPause() {
super.onPause();
paused = true;
stopLocationUpdate();
}
private void startLocationUpdate() {
OsmandApplication app = getMyApplication();
if (app != null && !locationUpdateStarted) {
locationUpdateStarted = true;
app.getLocationProvider().addLocationListener(this);
}
}
private void stopLocationUpdate() {
OsmandApplication app = getMyApplication();
if (app != null && locationUpdateStarted) {
locationUpdateStarted = false;
app.getLocationProvider().removeLocationListener(this);
}
}
@Override
public void updateLocation(final Location location) {
if (location != null && !MapUtils.areLatLonEqual(menu.getMyLocation(), location)) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null && mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation()) {
mapActivity.getMyApplication().runInUIThread(new Runnable() {
@Override
public void run() {
menu.updateMyLocation(mainView, location);
}
});
}
}
}
@Override
@ -151,10 +186,6 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
return R.color.status_bar_transparent_gradient;
}
public boolean isPaused() {
return paused;
}
public int getHeight() {
if (mainView != null) {
return mainView.getHeight();
@ -243,4 +274,4 @@ public class TrackDetailsMenuFragment extends BaseOsmAndFragment {
return false;
}
}
}
}

View file

@ -40,7 +40,6 @@ import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.MapMarkersHelper.MapMarkersGroup;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.base.PointImageDrawable;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.SelectedGpxPoint;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu.TrackChartPoints;
@ -603,8 +602,11 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
for (TrkSegment segment : segments) {
QuadRect trackBounds = GPXUtilities.calculateBounds(segment.points);
if (QuadRect.trivialOverlap(tb.getLatLonBounds(), trackBounds)) {
SelectedGpxPoint selectedGpxPoint = findPointNearSegment(tb, selectedGpxFile, segment.points, r, mx, my);
if (selectedGpxPoint != null) {
WptPt nextPoint = findPointNearSegment(tb, segment.points, r, mx, my);
if (nextPoint != null) {
int index = segment.points.indexOf(nextPoint);
WptPt prevPoint = segment.points.get(index - 1);
SelectedGpxPoint selectedGpxPoint = createSelectedGpxPoint(selectedGpxFile, prevPoint, nextPoint, tb.getLatLonFromPixel(mx, my));
res.add(selectedGpxPoint);
break;
}
@ -613,7 +615,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
}
}
private SelectedGpxPoint findPointNearSegment(RotatedTileBox tb, SelectedGpxFile selectedGpxFile, List<WptPt> points, int r, int mx, int my) {
public static WptPt findPointNearSegment(RotatedTileBox tb, List<WptPt> points, int r, int mx, int my) {
WptPt firstPoint = points.get(0);
int ppx = (int) tb.getPixXFromLatLon(firstPoint.lat, firstPoint.lon);
int ppy = (int) tb.getPixYFromLatLon(firstPoint.lat, firstPoint.lon);
@ -625,7 +627,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
int py = (int) tb.getPixYFromLatLon(point.lat, point.lon);
int cross = placeInBbox(px, py, mx, my, r, r);
if (cross == 0) {
return createProjectionPoint(selectedGpxFile, points.get(i - 1), point, tb.getLatLonFromPixel(mx, my));
return point;
}
if ((pcross & cross) == 0) {
int mpx = px;
@ -636,7 +638,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
int mpynew = mpy / 2 + ppy / 2;
int mcrossnew = placeInBbox(mpxnew, mpynew, mx, my, r, r);
if (mcrossnew == 0) {
return createProjectionPoint(selectedGpxFile, points.get(i - 1), point, tb.getLatLonFromPixel(mx, my));
return point;
}
if ((mcrossnew & mcross) != 0) {
mpx = mpxnew;
@ -659,19 +661,8 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
return null;
}
private SelectedGpxPoint createProjectionPoint(SelectedGpxFile selectedGpxFile, WptPt prevPoint, WptPt nextPoint, LatLon latLon) {
LatLon projection = MapUtils.getProjection(latLon.getLatitude(), latLon.getLongitude(), prevPoint.lat, prevPoint.lon, nextPoint.lat, nextPoint.lon);
WptPt projectionPoint = new WptPt();
projectionPoint.lat = projection.getLatitude();
projectionPoint.lon = projection.getLongitude();
projectionPoint.heading = prevPoint.heading;
projectionPoint.distance = prevPoint.distance + MapUtils.getDistance(projection, prevPoint.lat, prevPoint.lon);
projectionPoint.ele = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.ele, nextPoint.distance, nextPoint.ele);
projectionPoint.speed = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.speed, nextPoint.distance, nextPoint.speed);
if (prevPoint.time != 0 && nextPoint.time != 0) {
projectionPoint.time = (long) getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.time, nextPoint.distance, nextPoint.time);
}
private SelectedGpxPoint createSelectedGpxPoint(SelectedGpxFile selectedGpxFile, WptPt prevPoint, WptPt nextPoint, LatLon latLon) {
WptPt projectionPoint = createProjectionPoint(prevPoint, nextPoint, latLon);
Location prevPointLocation = new Location("");
prevPointLocation.setLatitude(prevPoint.lat);
@ -686,11 +677,28 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
return new SelectedGpxPoint(selectedGpxFile, projectionPoint, prevPointLocation);
}
private double getValueByDistInterpolation(double projectionDist, double prevDist, double prevVal, double nextDist, double nextVal) {
public static WptPt createProjectionPoint(WptPt prevPoint, WptPt nextPoint, LatLon latLon) {
LatLon projection = MapUtils.getProjection(latLon.getLatitude(), latLon.getLongitude(), prevPoint.lat, prevPoint.lon, nextPoint.lat, nextPoint.lon);
WptPt projectionPoint = new WptPt();
projectionPoint.lat = projection.getLatitude();
projectionPoint.lon = projection.getLongitude();
projectionPoint.heading = prevPoint.heading;
projectionPoint.distance = prevPoint.distance + MapUtils.getDistance(projection, prevPoint.lat, prevPoint.lon);
projectionPoint.ele = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.ele, nextPoint.distance, nextPoint.ele);
projectionPoint.speed = getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.speed, nextPoint.distance, nextPoint.speed);
if (prevPoint.time != 0 && nextPoint.time != 0) {
projectionPoint.time = (long) getValueByDistInterpolation(projectionPoint.distance, prevPoint.distance, prevPoint.time, nextPoint.distance, nextPoint.time);
}
return projectionPoint;
}
private static double getValueByDistInterpolation(double projectionDist, double prevDist, double prevVal, double nextDist, double nextVal) {
return prevVal + (projectionDist - prevDist) * ((nextVal - prevVal) / (nextDist - prevDist));
}
int placeInBbox(int x, int y, int mx, int my, int halfw, int halfh) {
private static int placeInBbox(int x, int y, int mx, int my, int halfw, int halfh) {
int cross = 0;
cross |= (x < mx - halfw ? 1 : 0);
cross |= (x > mx + halfw ? 2 : 0);
@ -841,7 +849,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
private void syncGpx(GPXFile gpxFile) {
MapMarkersGroup group = view.getApplication().getMapMarkersHelper().getMarkersGroup(gpxFile);
if (group != null) {
mapMarkersHelper.runSynchronization(group);
mapMarkersHelper.runSynchronization(group);
}
}