/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2016 HydroloGIS (www.hydrologis.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geopaparazzi.core.ui.activities; import android.app.Activity; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.PointF; import android.location.Location; import android.os.Build; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; import com.androidplot.ui.AnchorPosition; import com.androidplot.ui.DynamicTableModel; import com.androidplot.ui.SizeLayoutType; import com.androidplot.ui.SizeMetrics; import com.androidplot.ui.XLayoutStyle; import com.androidplot.ui.YLayoutStyle; import com.androidplot.util.PixelUtils; import com.androidplot.xy.BoundaryMode; import com.androidplot.xy.LineAndPointFormatter; import com.androidplot.xy.SimpleXYSeries; import com.androidplot.xy.XYGraphWidget; import com.androidplot.xy.XYLegendWidget; import com.androidplot.xy.XYPlot; import com.androidplot.xy.XYSeries; import com.vividsolutions.jts.geom.Coordinate; import java.io.IOException; import java.util.ArrayList; import java.util.List; import eu.geopaparazzi.library.database.GPLog; import eu.geopaparazzi.library.util.Compat; import eu.geopaparazzi.library.util.DynamicDoubleArray; import eu.geopaparazzi.library.util.GPDialogs; import eu.geopaparazzi.library.util.StringAsyncTask; import eu.geopaparazzi.core.R; import eu.geopaparazzi.core.database.DaoGpsLog; import eu.geopaparazzi.core.database.objects.Line; import eu.geopaparazzi.core.utilities.Constants; import eu.geopaparazzi.core.utilities.FeatureSlidingAverage; /** * The profile chart activity. * * @author Andrea Antonello */ public class ProfileChartActivity extends Activity implements View.OnTouchListener, View.OnClickListener { private XYPlot xyPlotSpeed, xyPlotElev; private LineAndPointFormatter seriesSpeedFormat, seriesElevFormat; XYSeries seriesSpeed, seriesElev; private Line line; private PointF minXYSpeed; private PointF maxXYSpeed; private PointF minXYElevation; private PointF maxXYElevation; private TextView infoTextView; private double elevDifference; private FloatingActionButton resetButton; private View textLayout; private StringAsyncTask resumeTask; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_profilechart); int backgroundColor = Compat.getColor(this, R.color.main_background); // colors int rgbSpeedLine = Color.rgb(0, 200, 0); int rgbSpeedPoints = Color.rgb(0, 100, 0); int rgbElevLine = Color.rgb(0, 0, 200); int rgbElevPoints = Color.rgb(0, 0, 100); Bundle extras = getIntent().getExtras(); if (extras != null) { try { long logid = extras.getLong(Constants.ID); line = DaoGpsLog.getGpslogAsLine(logid, -1); } catch (IOException e) { GPLog.error(this, e.getLocalizedMessage(), e); e.printStackTrace(); } } else { GPDialogs.warningDialog(this, getString(R.string.an_error_occurred_while_creating_the_chart_), new Runnable() { @Override public void run() { finish(); } }); return; } final float f26 = PixelUtils.dpToPix(26); final float f10 = PixelUtils.dpToPix(10); final SizeMetrics sm = new SizeMetrics(0, SizeLayoutType.FILL, 0, SizeLayoutType.FILL); xyPlotSpeed = (XYPlot) findViewById(R.id.speed_plot); xyPlotElev = (XYPlot) findViewById(R.id.elevation_plot); xyPlotSpeed.setOnTouchListener(this); textLayout = findViewById(R.id.info_text_layout); textLayout.setVisibility(View.GONE); infoTextView = (TextView) findViewById(R.id.info_text); resetButton = (FloatingActionButton) findViewById(R.id.reset_chart_button); resetButton.setOnClickListener(this); resetButton.hide(); // Disable Hardware Acceleration on the xyPlot view object. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { xyPlotSpeed.setLayerType(View.LAYER_TYPE_SOFTWARE, null); xyPlotElev.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } /* * Setup the Plots */ xyPlotSpeed.setPlotMargins(0, 0, 0, 0); xyPlotElev.setPlotMargins(0, 0, 0, 0); xyPlotSpeed.setPlotPadding(0, 0, 0, 0); xyPlotElev.setPlotPadding(0, 0, 0, 0); xyPlotElev.getDomainLabelWidget().setVisible(false); xyPlotElev.getRangeLabelWidget().setVisible(false); xyPlotElev.getTitleWidget().setVisible(false); xyPlotElev.setBorderPaint(null); xyPlotElev.setBackgroundPaint(null); /* * Setup the Graph Widgets */ XYGraphWidget graphWidgetSpeed = xyPlotSpeed.getGraphWidget(); XYGraphWidget graphWidgetElev = xyPlotElev.getGraphWidget(); graphWidgetSpeed.setSize(sm); graphWidgetElev.setSize(sm); graphWidgetSpeed.setMargins(0, 0, 0, 0); graphWidgetElev.setMargins(0, 0, 0, 0); graphWidgetSpeed.setPadding(f26, f10, f26, f26); graphWidgetElev.setPadding(f26, f10, f26, f26); graphWidgetSpeed.setRangeAxisPosition(true, false, 4, "10"); graphWidgetElev.setRangeAxisPosition(false, false, 4, "10"); graphWidgetSpeed.setRangeLabelVerticalOffset(-3); graphWidgetElev.setRangeLabelVerticalOffset(-3); graphWidgetSpeed.setRangeOriginLabelPaint(null); graphWidgetElev.setRangeOriginLabelPaint(null); graphWidgetSpeed.setRangeLabelWidth(0); graphWidgetElev.setRangeLabelWidth(0); graphWidgetSpeed.setDomainLabelWidth(0); graphWidgetElev.setDomainLabelWidth(0); graphWidgetElev.setBackgroundPaint(null); graphWidgetElev.setDomainLabelPaint(null); graphWidgetElev.setGridBackgroundPaint(null); graphWidgetElev.setDomainOriginLabelPaint(null); graphWidgetElev.setRangeOriginLinePaint(null); graphWidgetElev.setDomainGridLinePaint(null); graphWidgetElev.setRangeGridLinePaint(null); graphWidgetSpeed.getBackgroundPaint().setColor(backgroundColor); graphWidgetSpeed.getGridBackgroundPaint().setColor(backgroundColor); graphWidgetSpeed.getRangeOriginLinePaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getRangeOriginLinePaint().setStrokeWidth(3f); graphWidgetSpeed.getDomainOriginLinePaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getDomainOriginLinePaint().setStrokeWidth(3f); graphWidgetSpeed.getRangeGridLinePaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getRangeGridLinePaint().setStrokeWidth(1f); graphWidgetSpeed.getDomainGridLinePaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getDomainGridLinePaint().setStrokeWidth(1f); graphWidgetSpeed.getRangeLabelPaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getDomainLabelPaint().setColor(rgbSpeedPoints); graphWidgetSpeed.getDomainOriginLabelPaint().setColor(rgbSpeedPoints); Paint rangeOriginLabelPaint = graphWidgetSpeed.getRangeOriginLabelPaint(); if (rangeOriginLabelPaint == null) { rangeOriginLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); rangeOriginLabelPaint.setStyle(Paint.Style.STROKE); graphWidgetSpeed.setRangeOriginLabelPaint(rangeOriginLabelPaint); } rangeOriginLabelPaint.setColor(rgbSpeedPoints); graphWidgetElev.getRangeLabelPaint().setColor(rgbElevPoints); Paint rangeOriginLabelPaint2 = graphWidgetElev.getRangeOriginLabelPaint(); if (rangeOriginLabelPaint2 == null) { rangeOriginLabelPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); rangeOriginLabelPaint2.setStyle(Paint.Style.STROKE); graphWidgetElev.setRangeOriginLabelPaint(rangeOriginLabelPaint2); } rangeOriginLabelPaint2.setColor(rgbElevPoints); graphWidgetSpeed.getRangeLabelPaint().setTextSize(PixelUtils.dpToPix(8)); graphWidgetElev.getRangeLabelPaint().setTextSize(PixelUtils.dpToPix(8)); graphWidgetSpeed.getDomainOriginLabelPaint().setTextSize(PixelUtils.dpToPix(8)); graphWidgetSpeed.getDomainLabelPaint().setTextSize(PixelUtils.dpToPix(8)); float textSize = graphWidgetSpeed.getRangeLabelPaint().getTextSize(); graphWidgetSpeed.setRangeLabelVerticalOffset((textSize / 2) * -1); graphWidgetElev.setRangeLabelVerticalOffset(graphWidgetSpeed.getRangeLabelVerticalOffset()); /* * Position the Graph Widgets in the Centre */ graphWidgetSpeed.position(0, XLayoutStyle.ABSOLUTE_FROM_CENTER, 0, YLayoutStyle.ABSOLUTE_FROM_CENTER, AnchorPosition.CENTER); graphWidgetElev.position(0, XLayoutStyle.ABSOLUTE_FROM_CENTER, 0, YLayoutStyle.ABSOLUTE_FROM_CENTER, AnchorPosition.CENTER); /* * Position the Label Widgets */ xyPlotSpeed.getDomainLabelWidget().setWidth(100); xyPlotSpeed.getRangeLabelWidget().setWidth(100); xyPlotSpeed.getDomainLabelWidget().position(0, XLayoutStyle.RELATIVE_TO_CENTER, 1, YLayoutStyle.ABSOLUTE_FROM_BOTTOM, AnchorPosition.BOTTOM_MIDDLE); xyPlotSpeed.getRangeLabelWidget().position(1, XLayoutStyle.ABSOLUTE_FROM_LEFT, -20, YLayoutStyle.ABSOLUTE_FROM_CENTER, AnchorPosition.LEFT_BOTTOM); /* * Setup and Position the speed Legend */ XYLegendWidget legendWidgetSpeed = xyPlotSpeed.getLegendWidget(); legendWidgetSpeed.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 200, SizeLayoutType.ABSOLUTE)); legendWidgetSpeed.setPadding(1, 1, 1, 1); legendWidgetSpeed.setTableModel(new DynamicTableModel(1, 3)); legendWidgetSpeed.setIconSizeMetrics(new SizeMetrics(PixelUtils.dpToPix(10), SizeLayoutType.ABSOLUTE, PixelUtils.dpToPix(10), SizeLayoutType.ABSOLUTE)); legendWidgetSpeed.getTextPaint().setColor(rgbSpeedPoints); legendWidgetSpeed.getTextPaint().setTextSize(PixelUtils.dpToPix(9)); legendWidgetSpeed.position(PixelUtils.dpToPix(30), XLayoutStyle.ABSOLUTE_FROM_LEFT, f10 + 2, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.LEFT_TOP); /* * Setup and Position the elev Legend */ XYLegendWidget legendWidgetElev = xyPlotElev.getLegendWidget(); legendWidgetElev.setSize(new SizeMetrics(100, SizeLayoutType.ABSOLUTE, 200, SizeLayoutType.ABSOLUTE)); legendWidgetElev.setPadding(1, 1, 1, 1); legendWidgetElev.setTableModel(new DynamicTableModel(1, 3)); legendWidgetElev.setIconSizeMetrics(new SizeMetrics(PixelUtils.dpToPix(10), SizeLayoutType.ABSOLUTE, PixelUtils.dpToPix(10), SizeLayoutType.ABSOLUTE)); legendWidgetElev.getTextPaint().setTextSize(PixelUtils.dpToPix(9)); legendWidgetElev.getTextPaint().setColor(rgbElevPoints); legendWidgetElev.getTextPaint().setTextAlign(Align.RIGHT); legendWidgetElev.setMarginLeft(185); legendWidgetElev.position(PixelUtils.dpToPix(30), XLayoutStyle.ABSOLUTE_FROM_RIGHT, f10 + 2, YLayoutStyle.ABSOLUTE_FROM_TOP, AnchorPosition.RIGHT_TOP); // Setup the formatters seriesSpeedFormat = new LineAndPointFormatter(rgbSpeedLine, rgbSpeedPoints, null, null); seriesElevFormat = new LineAndPointFormatter(rgbElevLine, rgbElevPoints, null, null); } @Override protected void onResume() { super.onResume(); //$NON-NLS-1$ resumeTask = new StringAsyncTask(this) { protected String doBackgroundWork() { try { createDatasetFromProfile(); } catch (Exception e) { GPLog.error(this, null, e); //$NON-NLS-1$ return "ERROR"; } return ""; } protected void doUiPostWork(String response) { dispose(); if (response.length() != 0) { GPDialogs.warningDialog(ProfileChartActivity.this, response, null); } else { updateView(); } } }; resumeTask.setProgressDialog("", "Loading data...", false, null); resumeTask.execute(); } @Override protected void onDestroy() { if (resumeTask!= null) resumeTask.dispose(); super.onDestroy(); } private void updateView() { // Remove all current series from each plot for (XYSeries setElement : xyPlotSpeed.getSeriesSet()) { xyPlotSpeed.removeSeries(setElement); } for (XYSeries setElement : xyPlotElev.getSeriesSet()) { xyPlotElev.removeSeries(setElement); } // Add series to each plot as needed. xyPlotSpeed.addSeries(seriesSpeed, seriesSpeedFormat); xyPlotElev.addSeries(seriesElev, seriesElevFormat); // Finalise each Plot based on whether they have any series or not. if (!xyPlotElev.getSeriesSet().isEmpty()) { xyPlotElev.setVisibility(XYPlot.VISIBLE); xyPlotElev.redraw(); } else { xyPlotElev.setVisibility(XYPlot.INVISIBLE); } if (!xyPlotSpeed.getSeriesSet().isEmpty()) { xyPlotSpeed.setVisibility(XYPlot.VISIBLE); xyPlotSpeed.redraw(); } else { xyPlotSpeed.setVisibility(XYPlot.INVISIBLE); } xyPlotSpeed.calculateMinMaxVals(); minXYSpeed = new PointF(xyPlotSpeed.getCalculatedMinX().floatValue(), xyPlotSpeed.getCalculatedMinY().floatValue()); maxXYSpeed = new PointF(xyPlotSpeed.getCalculatedMaxX().floatValue(), xyPlotSpeed.getCalculatedMaxY().floatValue()); xyPlotElev.calculateMinMaxVals(); minXYElevation = new PointF(xyPlotElev.getCalculatedMinX().floatValue(), xyPlotElev.getCalculatedMinY().floatValue()); maxXYElevation = new PointF(xyPlotElev.getCalculatedMaxX().floatValue(), xyPlotElev.getCalculatedMaxY().floatValue()); infoTextView.setText(getString(R.string.active_elevation_diff) + (int) elevDifference + "m"); } /** * Create a dataset based on supplied data that are supposed to be coordinates and elevations for a profile view. */ public void createDatasetFromProfile() throws Exception { DynamicDoubleArray lonArray = line.getLonList(); DynamicDoubleArray latArray = line.getLatList(); DynamicDoubleArray elevArray = line.getAltimList(); List<String> dates = line.getDateList(); double previousLat = 0; double previousLon = 0; double summedDistance = 0.0; long previousTime = 0; List<Coordinate> elevList = new ArrayList<>(lonArray.size()); List<Coordinate> speedList = new ArrayList<>(lonArray.size()); for (int i = 0; i < lonArray.size(); i++) { double elev = elevArray.get(i); double lat = latArray.get(i); double lon = lonArray.get(i); String dateStr = dates.get(i); long time = Long.parseLong(dateStr); double distance = 0.0; double speedKmH = 0.0; if (i > 0) { Location thisLoc = new Location("dummy1"); //$NON-NLS-1$ thisLoc.setLongitude(lon); thisLoc.setLatitude(lat); Location thatLoc = new Location("dummy2"); //$NON-NLS-1$ thatLoc.setLongitude(previousLon); thatLoc.setLatitude(previousLat); distance = thisLoc.distanceTo(thatLoc); double timeSeconds = (time - previousTime) / 1000.0; speedKmH = 3.6 * distance / timeSeconds; } previousLat = lat; previousLon = lon; previousTime = time; summedDistance = summedDistance + distance; elevList.add(new Coordinate(summedDistance, elev)); speedList.add(new Coordinate(summedDistance, speedKmH)); } int lookAhead = 20; double slide = 1; FeatureSlidingAverage fsaElev = new FeatureSlidingAverage(elevList); List<Coordinate> smoothedElev = fsaElev.smooth(lookAhead, false, slide); FeatureSlidingAverage fsaSpeed = new FeatureSlidingAverage(speedList); List<Coordinate> smoothedSpeed = fsaSpeed.smooth(lookAhead, false, slide); int size = lonArray.size(); List<Double> finalYList1 = new ArrayList<>(size); List<Double> finalYList2 = new ArrayList<>(lonArray.size()); List<Double> finalXList1 = new ArrayList<>(lonArray.size()); elevDifference = 0; double previousElev = 0; for (int i = 0; i < size; i++) { Coordinate elev = smoothedElev.get(i); Coordinate speed = smoothedSpeed.get(i); finalXList1.add(elev.x); finalYList1.add(elev.y); finalYList2.add(speed.y); if (i > 0) { double diff = elev.y - previousElev; if (diff > 0) elevDifference = elevDifference + diff; } previousElev = elev.y; } // Setup the Series seriesElev = new SimpleXYSeries(finalXList1, finalYList1, "Elev [m]"); seriesSpeed = new SimpleXYSeries(finalXList1, finalYList2, "Speed [km/h]"); } // Definition of the touch states static final int NONE = 0; static final int ONE_FINGER_DRAG = 1; static final int TWO_FINGERS_DRAG = 2; int mode = NONE; PointF firstFinger; float distBetweenFingers; boolean stopThread = false; @Override public boolean onTouch(View arg0, MotionEvent event) { try { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: // Start gesture textLayout.setVisibility(View.VISIBLE); firstFinger = new PointF(event.getX(), event.getY()); mode = ONE_FINGER_DRAG; stopThread = true; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = NONE; textLayout.setVisibility(View.GONE); PointF upPoint = new PointF(event.getX(), event.getY()); if (!firstFinger.equals(upPoint.x, upPoint.y)) resetButton.show(); break; case MotionEvent.ACTION_POINTER_DOWN: // second finger distBetweenFingers = spacing(event); // the distance check is done to avoid false alarms if (distBetweenFingers > 5f) { mode = TWO_FINGERS_DRAG; } break; case MotionEvent.ACTION_MOVE: textLayout.setVisibility(View.GONE); if (mode == ONE_FINGER_DRAG) { PointF oldFirstFinger = firstFinger; firstFinger = new PointF(event.getX(), event.getY()); scrollElev(oldFirstFinger.x - firstFinger.x); scrollSpeed(oldFirstFinger.x - firstFinger.x); xyPlotSpeed.setDomainBoundaries(minXYSpeed.x, maxXYSpeed.x, BoundaryMode.FIXED); xyPlotElev.setDomainBoundaries(minXYElevation.x, maxXYElevation.x, BoundaryMode.FIXED); xyPlotSpeed.redraw(); xyPlotElev.redraw(); } else if (mode == TWO_FINGERS_DRAG) { float oldDist = distBetweenFingers; distBetweenFingers = spacing(event); zoomElev(oldDist / distBetweenFingers); zoomSpeed(oldDist / distBetweenFingers); xyPlotSpeed.setDomainBoundaries(minXYSpeed.x, maxXYSpeed.x, BoundaryMode.FIXED); xyPlotElev.setDomainBoundaries(minXYElevation.x, maxXYElevation.x, BoundaryMode.FIXED); xyPlotSpeed.redraw(); xyPlotElev.redraw(); } break; } } catch (Exception e) { GPLog.error(this, null, e); } return true; } private void zoomElev(float scale) { float domainSpan = maxXYSpeed.x - minXYSpeed.x; float domainMidPoint = maxXYSpeed.x - domainSpan / 2.0f; float offset = domainSpan * scale / 2.0f; minXYSpeed.x = domainMidPoint - offset; maxXYSpeed.x = domainMidPoint + offset; minXYSpeed.x = Math.min(minXYSpeed.x, seriesSpeed.getX(seriesSpeed.size() - 3) .floatValue()); maxXYSpeed.x = Math.max(maxXYSpeed.x, seriesSpeed.getX(1).floatValue()); clampToDomainBoundsElev(domainSpan); } private void zoomSpeed(float scale) { float domainSpan = maxXYElevation.x - minXYElevation.x; float domainMidPoint = maxXYElevation.x - domainSpan / 2.0f; float offset = domainSpan * scale / 2.0f; minXYElevation.x = domainMidPoint - offset; maxXYElevation.x = domainMidPoint + offset; minXYElevation.x = Math.min(minXYElevation.x, seriesElev.getX(seriesElev.size() - 3) .floatValue()); maxXYElevation.x = Math.max(maxXYElevation.x, seriesElev.getX(1).floatValue()); clampToDomainBoundsSpeed(domainSpan); } private void scrollElev(float pan) { float domainSpan = maxXYSpeed.x - minXYSpeed.x; float step = domainSpan / xyPlotSpeed.getWidth(); float offset = pan * step; minXYSpeed.x = minXYSpeed.x + offset; maxXYSpeed.x = maxXYSpeed.x + offset; clampToDomainBoundsElev(domainSpan); } private void scrollSpeed(float pan) { float domainSpan = maxXYElevation.x - minXYElevation.x; float step = domainSpan / xyPlotElev.getWidth(); float offset = pan * step; minXYElevation.x = minXYElevation.x + offset; maxXYElevation.x = maxXYElevation.x + offset; clampToDomainBoundsSpeed(domainSpan); } private void clampToDomainBoundsElev(float domainSpan) { float leftBoundary = seriesSpeed.getX(0).floatValue(); float rightBoundary = seriesSpeed.getX(seriesSpeed.size() - 1).floatValue(); // enforce left scroll boundary: if (minXYSpeed.x < leftBoundary) { minXYSpeed.x = leftBoundary; maxXYSpeed.x = leftBoundary + domainSpan; } else if (maxXYSpeed.x > seriesSpeed.getX(seriesSpeed.size() - 1).floatValue()) { maxXYSpeed.x = rightBoundary; minXYSpeed.x = rightBoundary - domainSpan; } } private void clampToDomainBoundsSpeed(float domainSpan) { float leftBoundary = seriesElev.getX(0).floatValue(); float rightBoundary = seriesElev.getX(seriesElev.size() - 1).floatValue(); // enforce left scroll boundary: if (minXYElevation.x < leftBoundary) { minXYElevation.x = leftBoundary; maxXYElevation.x = leftBoundary + domainSpan; } else if (maxXYElevation.x > seriesElev.getX(seriesElev.size() - 1).floatValue()) { maxXYElevation.x = rightBoundary; minXYElevation.x = rightBoundary - domainSpan; } } private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } @Override public void onClick(View v) { minXYSpeed.x = seriesSpeed.getX(0).floatValue(); maxXYSpeed.x = seriesSpeed.getX(seriesSpeed.size() - 1).floatValue(); xyPlotSpeed.setDomainBoundaries(minXYSpeed.x, maxXYSpeed.x, BoundaryMode.FIXED); minXYElevation.x = seriesElev.getX(0).floatValue(); maxXYElevation.x = seriesElev.getX(seriesElev.size() - 1).floatValue(); xyPlotElev.setDomainBoundaries(minXYElevation.x, maxXYSpeed.x, BoundaryMode.FIXED); xyPlotElev.redraw(); xyPlotSpeed.redraw(); resetButton.hide(); } }