Snap to nearest data point and display value

Need help in implementing some specific function to your LightningChart Ultimate powered application? Post a question and get code snippets from other LightningChart Ultimate community members.

Moderator: Queue Moderators

Post Reply
Skippy
Posts: 1
Joined: Mon Apr 29, 2013 6:19 pm

Snap to nearest data point and display value

Post by Skippy » Mon Apr 29, 2013 6:58 pm

I would like to have a feature of: snap to a data point and display the value at that data point i.e. When you drag the marker, it automatically snaps to nearest data point and shows the X and Y value of it.

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: Snap to nearest data point and display value

Post by ArctionPasi » Tue Apr 30, 2013 9:27 am

PointLineSeries, SampleDataSeries, AreaSeries and FreeformPointLineSeries have marker snapping built-in (they are ITrackable series). FreeformPointLineSeries that you are using, does not.

So you'll have to implement this in the application side as follows. Define MovedByMouse event handler for the marker. In the handler, seek the nearest data point from the series, with the SeekNearestDataPointIndex method below.

This code (WinForms) creates the chart, creates one FreeformPointLineSeries, fills it with points and inserts 2 markers.

Code: Select all

void CreateChart()
        {
            m_chart = new LightningChartUltimate("Y3PT77S2RQRCKU6M6PR2EQ7XKJEHMSG9WHAZUQAHZLWY7RTFYU74CP6UT9DGB7AFJVTJB35PML8RWY4HBGAVHL9NP9WEGRVU2R9FF5JXWVQZMJ9T4TZMLYK65EX9WCSSVSFRTZKVUJCVKHJPJFVYXD75XVDUMJSFK2ZKJHLLRGS9J9B4M6W2");
            m_chart.Parent = this; 
            m_chart.Dock = DockStyle.Fill; 

            m_chart.BeginUpdate();

            m_chart.ViewXY.XAxes[0].ValueType = AxisValueType.Number; 

            //Add FreeformPointLineSeries
            FreeformPointLineSeries fpls = new FreeformPointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
            fpls.MouseInteraction = false; 

            int iPointCount = 101;
            SeriesPoint[] aPoints = new SeriesPoint[iPointCount]; 
            Random rand = new Random(); 

            for (int i = 0; i < iPointCount; i++)
            {
                aPoints[i].X = 50 + rand.NextDouble() * 20.0;
                aPoints[i].Y = i; 
            }

            fpls.Points = aPoints;
            m_chart.ViewXY.FreeformPointLineSeries.Add(fpls);

            AddMarker(aPoints[20].X, aPoints[20].Y, fpls);
            AddMarker(aPoints[35].X, aPoints[35].Y, fpls); 

            m_chart.ViewXY.LegendBox.Visible = false;

            m_chart.ViewXY.XAxes[0].SetRange(0, 100); 
            m_chart.ViewXY.YAxes[0].SetRange(0, 80);
            

            m_chart.EndUpdate(); 

        }

        void AddMarker(double x, double y, FreeformPointLineSeries series)
        {
            SeriesEventMarker marker = new SeriesEventMarker(series);
            marker.VerticalPosition = SeriesEventMarkerVerticalPosition.AtYValue;
            marker.HorizontalPosition = SeriesEventMarkerHorizontalPosition.AtXValue;
            marker.XValue = x;
            marker.YValue = y;
            marker.MovedByMouse += new MouseEventHandler(marker_MovedByMouse);
            marker.MouseUp += new MouseEventHandler(marker_MouseUp);
            series.SeriesEventMarkers.Add(marker); 
        }

        void marker_MouseUp(object sender, MouseEventArgs e)
        {
            UpdateMarkerToTrackedPosition((SeriesEventMarker)sender); 
        }

        void marker_MovedByMouse(object sender, MouseEventArgs e)
        {
            int iNearestDataPointIndex = SeekNearestDataPointIndex(e.GetPosition(m_chart));
            
            if (iNearestDataPointIndex >= 0)
            {
                m_spLatestTrack = m_chart.ViewXY.FreeformPointLineSeries[0].Points[iNearestDataPointIndex];
                UpdateMarkerToTrackedPosition((SeriesEventMarker) sender); 
            }
        }

        void UpdateMarkerToTrackedPosition(SeriesEventMarker marker)
        {
            m_chart.BeginUpdate();

            marker.XValue = m_spLatestTrack.X;
            marker.YValue = m_spLatestTrack.Y;
            marker.Label.Text = "[" + marker.XValue.ToString("0.0") + " | " + marker.YValue.ToString("0.0")+"]"; 

            m_chart.EndUpdate();
        }

        int SeekNearestDataPointIndex(Point mouseLocation)
        {
            FreeformPointLineSeries fpls = m_chart.ViewXY.FreeformPointLineSeries[0];
            int iPointCount = fpls.PointCount;
            SeriesPoint[] aPoints = fpls.Points; //store reference to avoid calling Points property getter each loop round 

            double dValueUnderMouseX = 0;
            bool bXSolved = m_chart.ViewXY.XAxes[0].CoordToValue((int)mouseLocation.X, out dValueUnderMouseX, false);

            double dValueUnderMouseY = 0;
            bool bYSolved = m_chart.ViewXY.YAxes[0].CoordToValue((int)mouseLocation.Y, out dValueUnderMouseY);


            double dDistSq;
            double dMinDistSq = double.MaxValue;
            int iNearestIndex = -1;

            //Seek nearest point, by using squared method. It's faster than calling Sqrt every time. 
            for (int i = 0; i < iPointCount; i++)
            {
                dDistSq = (aPoints[i].Y - dValueUnderMouseY) * (aPoints[i].Y - dValueUnderMouseY)
                    + (aPoints[i].X - dValueUnderMouseX) * (aPoints[i].X - dValueUnderMouseX);

                if (dDistSq < dMinDistSq)
                {
                    dMinDistSq = dDistSq;
                    iNearestIndex = i;
                }
            }

            return iNearestIndex; 

        }
When you move a marker, it'll snap to nearest data point and show the solved values in the marker's label.
Attachments
FreeformPointLineSeries with 2 markers snapped to data nearest data points.
FreeformPointLineSeries with 2 markers snapped to data nearest data points.
fpls_markers.jpg (54.96 KiB) Viewed 10306 times
LightningChart Support Team, PT

Post Reply