Files
comaps/android/app/src/main/java/app/organicmaps/ChartController.java
2025-09-10 22:05:18 +02:00

250 lines
8.9 KiB
Java

package app.organicmaps;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.sdk.bookmarks.data.ElevationInfo;
import app.organicmaps.sdk.bookmarks.data.Track;
import app.organicmaps.sdk.bookmarks.data.TrackStatistics;
import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.widget.placepage.AxisValueFormatter;
import app.organicmaps.widget.placepage.CurrentLocationMarkerView;
import app.organicmaps.widget.placepage.FloatingMarkerView;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.MarkerView;
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.highlight.Highlight;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import com.google.android.material.textview.MaterialTextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ChartController implements OnChartValueSelectedListener
{
private static final int CHART_Y_LABEL_COUNT = 3;
private static final int CHART_X_LABEL_COUNT = 6;
private static final int CHART_ANIMATION_DURATION = 0;
private static final int CHART_FILL_ALPHA = (int) (0.12 * 255);
private static final int CHART_AXIS_GRANULARITY = 100;
private static final float CUBIC_INTENSITY = 0.2f;
private static final int CURRENT_POSITION_OUT_OF_TRACK = -1;
private static final String ELEVATION_PROFILE_POINTS = "ELEVATION_PROFILE_POINTS";
@SuppressWarnings("NullableProblems")
@NonNull
private LineChart mChart;
@SuppressWarnings("NullableProblems")
@NonNull
private FloatingMarkerView mFloatingMarkerView;
@SuppressWarnings("NullableProblems")
@NonNull
private MarkerView mCurrentLocationMarkerView;
@SuppressWarnings("NullableProblems")
@NonNull
private MaterialTextView mMaxAltitude;
@SuppressWarnings("NullableProblems")
@NonNull
private MaterialTextView mMinAltitude;
@NonNull
private final Context mContext;
private long mTrackId = Utils.INVALID_ID;
private boolean mCurrentPositionOutOfTrack = true;
private boolean mInformSelectedActivePointToCore = true;
public ChartController(@NonNull Context context)
{
mContext = context;
}
public void initialize(@NonNull View view)
{
final Resources resources = mContext.getResources();
mChart = view.findViewById(R.id.elevation_profile_chart);
mFloatingMarkerView = view.findViewById(R.id.floating_marker);
mCurrentLocationMarkerView = new CurrentLocationMarkerView(mContext);
mFloatingMarkerView.setChartView(mChart);
mCurrentLocationMarkerView.setChartView(mChart);
mMaxAltitude = view.findViewById(R.id.highest_altitude);
mMinAltitude = view.findViewById(R.id.lowest_altitude);
mChart.setBackgroundColor(ThemeUtils.getColor(mContext, R.attr.cardBackground));
mChart.setTouchEnabled(true);
mChart.setOnChartValueSelectedListener(this);
mChart.setDrawGridBackground(false);
mChart.setScaleXEnabled(true);
mChart.setScaleYEnabled(false);
mChart.setExtraTopOffset(0);
int sideOffset = resources.getDimensionPixelSize(R.dimen.margin_base);
int topOffset = 0;
mChart.setViewPortOffsets(sideOffset, topOffset, sideOffset,
resources.getDimensionPixelSize(R.dimen.margin_base_plus_quarter));
mChart.getDescription().setEnabled(false);
mChart.setDrawBorders(false);
Legend l = mChart.getLegend();
l.setEnabled(false);
initAxises();
}
private void highlightChartCurrentLocation()
{
mChart.highlightValues(Collections.singletonList(getCurrentPosHighlight()),
Collections.singletonList(mCurrentLocationMarkerView));
}
private void initAxises()
{
XAxis x = mChart.getXAxis();
x.setLabelCount(CHART_X_LABEL_COUNT, false);
x.setDrawGridLines(false);
x.setGranularity(CHART_AXIS_GRANULARITY);
x.setGranularityEnabled(true);
x.setTextColor(ThemeUtils.getColor(mContext, R.attr.elevationProfileAxisLabelColor));
x.setPosition(XAxis.XAxisPosition.BOTTOM);
x.setAxisLineColor(ThemeUtils.getColor(mContext, androidx.appcompat.R.attr.dividerHorizontal));
x.setAxisLineWidth(mContext.getResources().getDimensionPixelSize(R.dimen.divider_height));
IAxisValueFormatter xAxisFormatter = new AxisValueFormatter(mChart);
x.setValueFormatter(xAxisFormatter);
YAxis y = mChart.getAxisLeft();
y.setLabelCount(CHART_Y_LABEL_COUNT, false);
y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
y.setDrawGridLines(true);
y.setGridColor(ContextCompat.getColor(mContext, R.color.black_12));
y.setEnabled(true);
y.setTextColor(Color.TRANSPARENT);
y.setAxisLineColor(Color.TRANSPARENT);
int lineLength = mContext.getResources().getDimensionPixelSize(R.dimen.margin_eighth);
y.enableGridDashedLine(lineLength, 2 * lineLength, 0);
mChart.getAxisRight().setEnabled(false);
}
public void setData(Track track)
{
mTrackId = track.getTrackId();
ElevationInfo info = track.getElevationInfo();
TrackStatistics stats = track.getTrackStatistics();
List<Entry> values = new ArrayList<>();
for (ElevationInfo.Point point : info.getPoints())
values.add(new Entry((float) point.getDistance(), point.getAltitude(), point));
LineDataSet set = new LineDataSet(values, ELEVATION_PROFILE_POINTS);
set.setMode(LineDataSet.Mode.CUBIC_BEZIER);
set.setCubicIntensity(CUBIC_INTENSITY);
set.setDrawFilled(true);
set.setDrawCircles(false);
int lineThickness = mContext.getResources().getDimensionPixelSize(R.dimen.divider_width);
set.setLineWidth(lineThickness);
int color = ThemeUtils.getColor(mContext, R.attr.elevationProfileColor);
set.setCircleColor(color);
set.setColor(color);
set.setFillAlpha(CHART_FILL_ALPHA);
set.setFillColor(color);
set.setDrawHorizontalHighlightIndicator(false);
set.setHighlightLineWidth(lineThickness);
set.setHighLightColor(ContextCompat.getColor(mContext, R.color.base_accent_transparent));
LineData data = new LineData(set);
data.setValueTextSize(mContext.getResources().getDimensionPixelSize(R.dimen.text_size_icon_title));
data.setDrawValues(false);
mChart.setData(data);
mChart.animateX(CHART_ANIMATION_DURATION);
mMinAltitude.setText(Framework.nativeFormatAltitude(stats.getMinElevation()));
mMaxAltitude.setText(Framework.nativeFormatAltitude(stats.getMaxElevation()));
highlightActivePointManually();
}
@Override
public void onValueSelected(Entry e, Highlight h)
{
mFloatingMarkerView.updateOffsets(e, h);
Highlight curPos = getCurrentPosHighlight();
if (mCurrentPositionOutOfTrack)
mChart.highlightValues(Collections.singletonList(h), Collections.singletonList(mFloatingMarkerView));
else
mChart.highlightValues(Arrays.asList(curPos, h), Arrays.asList(mCurrentLocationMarkerView, mFloatingMarkerView));
if (mTrackId == Utils.INVALID_ID)
return;
if (mInformSelectedActivePointToCore)
BookmarkManager.INSTANCE.setElevationActivePoint(mTrackId, e.getX(), (ElevationInfo.Point) e.getData());
mInformSelectedActivePointToCore = true;
}
@NonNull
private Highlight getCurrentPosHighlight()
{
double activeX = BookmarkManager.INSTANCE.getElevationCurPositionDistance(mTrackId);
return new Highlight((float) activeX, 0f, 0);
}
@Override
public void onNothingSelected()
{
if (mCurrentPositionOutOfTrack)
return;
highlightChartCurrentLocation();
}
public void onCurrentPositionChanged()
{
if (mTrackId == Utils.INVALID_ID)
return;
double distance = BookmarkManager.INSTANCE.getElevationCurPositionDistance(mTrackId);
mCurrentPositionOutOfTrack = distance == CURRENT_POSITION_OUT_OF_TRACK;
highlightActivePointManually();
}
public void onElevationActivePointChanged()
{
if (mTrackId == Utils.INVALID_ID)
return;
highlightActivePointManually();
}
private void highlightActivePointManually()
{
Highlight highlight = getActivePoint();
mInformSelectedActivePointToCore = false;
mChart.highlightValue(highlight, true);
}
@NonNull
private Highlight getActivePoint()
{
double activeX = BookmarkManager.INSTANCE.getElevationActivePointDistance(mTrackId);
return new Highlight((float) activeX, 0f, 0);
}
public void onHide()
{
mChart.fitScreen();
mTrackId = Utils.INVALID_ID;
}
}