Plotting Real-Time Data on an iPad Using Core Plot

In one of our research projects, we had to plot the real-time Heart Rate (HR) of a user — which was collected from a wearable device during course of their workout — against time elapsed. This real-time plot showed the status of how well a user is progressing in a workout. To plot this graph, we utilized data visualization techniques.

Data visualization is essential to give meaning to raw data. It helps the viewer quickly understand trends and tracking data, as well as aid data analysis. The data can be represented in a multitude of ways, including a flow diagram, pie chart, line graph, scatter plot, and time-series graph.

heart rate vs time

Approach

During the workout, the HR of the user (in bpm) is collected from the wearable device at different times and then saved onto a server. This data is then retrieved from the server and a real-time HR is plotted on the graph in the iPad App. Below is the dataflow diagram of the process, followed by a detailed explanation of how to retrieve the data.

approach visual
Getting data from the server

To get the user HR data, the iPad sends a timestamp of the workout in a request to the server. For the first request, the timestamp is set to null in order to retrieve the HR data from the start of the workout to the current time. In every subsequent call, the timestamp parameter must be updated to the time when the previous call was requested from the server to ensure that only new data is requested from the server. The server then sends a list of the HR of the user with its corresponding timestamp.

Plotting the real-time HR graph – Challenges and tricks

The major challenge is generating a real-time graph; that is, plotting the HR data in such a way that the graph appears to progress with time. To do this, we plotted sections of the HR graph by using Timer, which allowed us to schedule requests to the server and receive data from only a specific duration of time. This then generated data points for only a small section of the HR graph, which can then be added to the existing graph so it looks like it is progressing with time.

We scheduled a Timer request to continuously receive the HR of the user at an interval of 10 seconds, which then supplies us with real-time data.

self.timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: Selector("getActualHRData"), userInfo: nil, repeats: true)

We then have to take this server response and generate data points from the HR plot. Since the graph shows the progress of a user through the workout, the y-axis represents the HR data while the x-axis represents the time elapsed in seconds. The first timestamp, which becomes the first data point, is the first HR number from the initial response generated by Timer; this must then be used to calculate all subsequent x-coordinates. The first timestamp is subtracted from the rest of the timestamps to calculate the time elapsed, which then become the data points to be plotted.

In order to keep track of the progress of a user during a workout, we must generate an expected HR graph in addition to the real-time HR graph. This will provide the user with a reference to see any deviation from the expected HR, so that they can stay on track with their goals. To create this, we had to use Core Plot, which allowed us to draw two graphs simultaneously.

Plotting graphs using Core Plot 1.2

Before we could utilize Core Plot 1.2 for our graph, we had to merge it with Apple’s recently introduced language, Swift. To do this, we created a file named Bridging-Header.h and set in the XCode build settings.

We then used the following steps to create the graph:

Creating the graph

  1. Initialize a CPTXYGraph
    var graph = CPTXYGraph(frame: CGRectZero)
  2. Set text style and graph title
    var tts = CPTMutableTextStyle.textStyle() as CPTMutableTextStyle
    tts.fontSize = 14.0
    tts.color = CPTColor(componentRed: 255.0, green: 255.0, blue: 255.0, alpha: 1.0)
    tts.fontName = "HelveticaNeue-Bold"
    self.graph.titleTextStyle = tts
         
    self.graph.title = "Heart Rate vs Time"
  3. Set up graph padding to accommodate graph on screen
    var tts = CPTMutableTextStyle.textStyle() as CPTMutableTextStyle
    tts.fontSize = 14.0
    tts.color = CPTColor(componentRed: 255.0, green: 255.0, blue: 255.0, alpha: 1.0)
    tts.fontName = "HelveticaNeue-Bold"
    self.graph.titleTextStyle = tts
         
    self.graph.title = "Heart Rate vs Time"
  1. For a better user experience, a theme can also be added to graph using:
    self.graph.applyTheme(CPTTheme(named:kCPTStocksTheme))

Setting up the plot space

  1. Initialize the CPTXYPlotSpace
    var plotSpace = graph.defaultPlotSpace as CPTXYPlotSpace!
  2. Set range of axes
    var xRange = plotSpace.xRange.mutableCopy() as CPTMutablePlotRange
    		    	var yRange = plotSpace.yRange.mutableCopy() as CPTMutablePlotRange
    xRange.setLocationFloat(Float(0))
            xRange.setLengthFloat(Float(self.userWorkout!.lastExpectedWorkoutLog()!.elapsedTime))
    yRange.setLocationFloat(Float(0))
    	     	yRange.setLengthFloat(Float(210))
            
    		    	plotSpace.xRange = xRange
    plotSpace.yRange = yRange
  3. Add plot space to graph
    graph.addPlotSpace(plotSpace)

Configuring the axes

  1. Set text style and formatter for labels
    var xts = CPTMutableTextStyle.textStyle() as CPTMutableTextStyle
    xts.color = CPTColor(componentRed: 255.0, green: 255.0, blue: 255.0, alpha: 1.0)
    			
    			var axisFormatter = NSNumberFormatter()
            	axisFormatter.maximumFractionDigits = 0
    axisFormatter.minimumIntegerDigits = 1
  2. Set labels for axes
    let axisSet = graph.axisSet as CPTXYAxisSet!
    
    let xAxis = axisSet.xAxis as CPTXYAxis!
    xAxis.axisTitle = CPTAxisTitle(text: "Elapsed Time (seconds)", textStyle: xts)
    xAxis.labelFormatter = axisFormatter
    
    let yAxis = axisSet.yAxis as CPTXYAxis!
    yAxis.axisTitle = CPTAxisTitle(text: "Heart Rate (BPM)", textStyle: xts)
    yAxis.labelFormatter = axisFormatter
    yAxis.titleOffset = 35.0
  3. Set interval at which ticks appear on axes
    xAxis.setMajorIntervalLength(Float((self.userWorkout!.lastExpectedWorkoutLog()!.elapsedTime)/self.numberOfTicks))     
    yAxis.setMajorIntervalLength(20)
  4. Add graph to graph view as hosted graph
    self.graphView.hostedGraph = self.graph

Creating two scatter plots: expected HR and real-time HR

  1. Initialize CPTScatterPlot for scatter plot
    var plot = CPTScatterPlot()
    plot.dataSource = self
  2. Add an identifier to each plot. This is crucial for setting data points of that plot
    plot.identifier = "actual";
  3. Set line style. Add interpolation to graph for Bezier curve interpolation
    var actualPlotStyle = plot.dataLineStyle.mutableCopy() as CPTMutableLineStyle
    actualPlotStyle.lineWidth = 2.0
    actualPlotStyle.lineColor = CPTColor(CGColor: (UIColor.yellowColor().CGColor))
    plot.dataLineStyle = actualPlotStyle
    plot.interpolation = CPTScatterPlotInterpolationCurved
  4. Add plots to graph
    self.graph.addPlot(plot)

Adding data points to each plot

Core Plot has two delegate methods to display the data points on the graph:

  1. numberOfRecordsForPlot: This method sets the number of data points the plot has
    func numberOfRecordsForPlot(plot: CPTPlot!) -> UInt {
    return UInt(self.userWorkout!.lastExpectedWorkoutLog()!.elapsedTime)
    }
  2. numberForPlot: This method returns the data point for each plot by using the plot identifier set above. If the identifier is set to “expected,” the data points for the expected HR graph are returned; otherwise, the real-time HR of the user is returned
    func numberForPlot(plot: CPTPlot!, field fieldEnum: UInt, recordIndex idx: UInt) -> NSNumber! {
            
            if plot.identifier.description == "expected" {
                var workoutLog =
                self.userWorkout!.expectedWorkoutLogAtIndexOrLast(idx)
                if workoutLog == nil {
                    return 0
                }
                
                return (fieldEnum == UInt(CPTScatterPlotFieldX.value)
                    ? workoutLog!.elapsedTime : workoutLog!.bpm)
                
            } else { // actual
                var workoutLog =self.userWorkout!.actualWorkoutLogAtIndexOrLast(idx)
                if workoutLog == nil {
                    return 0
                }
                
                return (fieldEnum == UInt(CPTScatterPlotFieldX.value)
                    ? workoutLog!.elapsedTime : workoutLog!.bpm)
            }
     }

Demonstration

The following is a video demonstration of the process we used above to plot the HR of a user that has been collected from a wearable device. As done above, the expected HR graph is plotted alongside the actual HR graph data, which allows the user to keep track of their progress. The graph should progress with time, thus generating a real-time plot.

 

Richa Gupta

Richa Gupta

Software Engineer

Richa Gupta is a Software Engineer for 3Pillar Global in Noida, India. She has over 4 years of experience developing applications in iOS, Objective-C, and Swift, as well as experience with manual and automation testing. She has also worked on Appium and the Selenium Automation tool, in addition to Apple’s latest technologies like the Apple Watch. She received a Masters in Computer Application from Uttrakhand Technical University. In her spare time, she likes to read novels, play badminton, and explore new places.

Akanksha Chaturvedi

Akanksha Chaturvedi

Software Engineer

Akanksha Chaturvedi is a Software Engineer at the 3Pillar Noida office in India. She has experience in Java technology, AngularJS, and iOS application development in Swift. She is very passionate about the design and analysis of computer algorithms, and has a keen interest in how those algorithms solve real world problems. She earned a Bachelor of Engineering in Computer Engineering from Netaji Subhas Institute of Technology (NSIT), Delhi. In her leisure time, she loves to travel, play badminton, watch tennis, and read novels.

Leave a Reply

Related Posts

WebAssembly: Running Byte Code in the Browser Currently, all of the major browser vendors are collaborating together on a new standard called WebAssembly, which is a compilation model for the web...