Plot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Graphs and charts provide a visual representation of the data. They provide a clearer and quicker understanding of the impact of certain statistics. Thus, SUSI.AI Android app makes use of bar charts to display statistics related to user ratings for SUSI skills. This blog guides through the steps to create a Horizontal Bar Chart, using MPAndroidChart library, that has been used in the SUSI.AI Android app skill details page to display the five star skill rating by the users.

On vertical axis : Labels of the rating shown
On horizontal axis : Percentage of total number
of users who rated the skill with the corresponding
number of stars on the vertical axis

Step – 1 : Add the required dependencies to your build.gradle.

(a) Project level build.gradle

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

(b) App level build.gradle

dependencies {
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}

 

Step – 2 : Create an XML layout.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
   android:layout_height="match_parent">

    <!-- Add a Horizontal Bar Chart using MPAndroidChart library -->
    <com.github.mikephil.charting.charts.HorizontalBarChart
       android:id="@+id/skill_rating_chart"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

 

Step – 3 : Create an Activity and initialize the Horizontal Bar Chart.

class MainActivity : Activity {

   lateinit var skillRatingChart : HorizontalBarChart

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.chart)

       setSkillGraph( )

   }
}

 

Step – 4 : Create a method in your MainActivity to set up the basic properties and the axes.

/**
* Set up the axes along with other necessary details for the horizontal bar chart.
*/
fun setSkillGraph(){
   skillRatingChart = skill_rating_chart              //skill_rating_chart is the id of the XML layout

   skillRatingChart.setDrawBarShadow(false)
   val description = Description()
   description.text = ""
   skillRatingChart.description = description
   skillRatingChart.legend.setEnabled(false)
   skillRatingChart.setPinchZoom(false)
   skillRatingChart.setDrawValueAboveBar(false)

   //Display the axis on the left (contains the labels 1*, 2* and so on)
   val xAxis = skillRatingChart.getXAxis()
   xAxis.setDrawGridLines(false)
   xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
   xAxis.setEnabled(true)
   xAxis.setDrawAxisLine(false)


   val yLeft = skillRatingChart.axisLeft

//Set the minimum and maximum bar lengths as per the values that they represent
   yLeft.axisMaximum = 100f
   yLeft.axisMinimum = 0f
   yLeft.isEnabled = false

   //Set label count to 5 as we are displaying 5 star rating
   xAxis.setLabelCount(5)

//Now add the labels to be added on the vertical axis
   val values = arrayOf("1 *", "2 *", "3 *", "4 *", "5 *")
   xAxis.valueFormatter = XAxisValueFormatter(values)        

   val yRight = skillRatingChart.axisRight
   yRight.setDrawAxisLine(true)
   yRight.setDrawGridLines(false)
   yRight.isEnabled = false

   //Set bar entries and add necessary formatting
   setGraphData()

   //Add animation to the graph
   skillRatingChart.animateY(2000)
}


Here is the XAxisValueFormatter class that is used to add the custom labels to the vertical axis :

public class XAxisValueFormatter implements IAxisValueFormatter {

   private String[] values;

   public XAxisValueFormatter(String[] values) {
       this.values = values;
   }

   @Override
   public String getFormattedValue(float value, AxisBase axis) {
       // "value" represents the position of the label on the axis (x or y)
       return this.values[(int) value];
   }

}

 

Step – 5 : Set the bar entries.

/**
* Set the bar entries i.e. the percentage of users who rated the skill with
* a certain number of stars.
*
* Set the colors for different bars and the bar width of the bars.
*/
private fun setGraphData() {

   //Add a list of bar entries
   val entries = ArrayList<BarEntry>()
   entries.add(BarEntry(0f, 27f))
   entries.add(BarEntry(1f, 45f))
   entries.add(BarEntry(2f, 65f))
   entries.add(BarEntry(3f, 77f))
   entries.add(BarEntry(4f, 93f))

  //Note : These entries can be replaced by real-time data, say, from an API

  ......

}

 

Step – 6 : Now create a BarDataSet.

To display the data in a bar chart, you need to initialize a
BarDataSet instance. BarDataSet is the Subclass of DataSet class. Now, initialize the BarDataSet and pass the argument as an ArrayList of BarEntry object.

val barDataSet = BarDataSet(entries, "Bar Data Set")

 

Step – 7 : Assign different colors to the bars (as required).

private fun setGraphData() {
    .....

   //Set the colors for bars with first color for 1*, second for 2* and so on
      barDataSet.setColors(
              ContextCompat.getColor(skillRatingChart.context, R.color.md_red_500),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_deep_orange_400),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_yellow_A700),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_green_700),
              ContextCompat.getColor(skillRatingChart.context, R.color.md_indigo_700)

   .....
)


Step – 8 : Populate data into Bar Chart.

To load the data into Bar Chart, you need to initialize a
BarData object  with bardataset. This BarData object is then passed into setData() method to load Bar Chart with data.

//Set bar shadows
   skillRatingChart.setDrawBarShadow(true)
   barDataSet.barShadowColor = Color.argb(40, 150, 150, 150)
   val data = BarData(barDataSet)

   //Set the bar width
   //Note : To increase the spacing between the bars set the value of barWidth to < 1f
   data.barWidth = 0.9f

   //Finally set the data and refresh the graph
   skillRatingChart.data = data
   skillRatingChart.invalidate()
}


Your Horizontal Bar Chart is now ready.
Note: You can format the labels as per your need and requirement with the help of XAxisValueFormatter.

Resources

Continue ReadingPlot a Horizontal Bar Graph using MPAndroidChart Library in SUSI.AI Android App

Comparing Different Graph View Libraries and Integrating Them in PSLab Android Application

There is a significant role of graphs in PSLab, they’re used for the following purpose:

For this, we need to implement real time graphs that stimulate real time data from the PSLab device efficiently. It is necessary to analyze each and every Graph View Library, compare them and integrate the best one in PSLab Android app.

Available Graph Libraries

The available Graph View libraries of Android are:

  1. MPAndroidChart
  2. Graph-View
  3. SciChart

Which one is the best with respect to the PSLab project?

MPAndroidChart

Line Graph plotted using MPAndroidChart (image source)

It is an open source graph view library by Philipp Jahoda. The following are the features of MPAndroidChart

  • There are 8 different chart types
  • Scaling on both axes. Scaling can be done using pinch zoom gesture.
  • Dual Axes, we can have 2 Y-axis.
  • Real time support
  • Customizable axis ie we can define different labels to the axis
  • Save chart to SD-Card
  • Predefined color templates
  • Legends which are used to define which line depicts what.
  • Animations
  • Fully customizable, from background color to color of the lines and grids.

On trying MPAndroidChart, I found it to be a slightly difficult to implement.

Graph-View

Line Graph plotted using GraphView Library (image source)

It is also an open source graph view library by Jonas Gehring. The following are features of the Graph-View

  • Supports Line Chart, Bar Chart and Points.
  • Scrolling vertical and horizontal
  • Scaling on both axes.
  • Realtime Graph support
  • Draw multiple series of data. Let the diagram show more that one series in a graph. You can set a color and a description for every series.
  • Legends (as discussed in MPAndroidChart)
  • Custom labels
  • Manual Y axis limits can be set.

SciChart

It is rich APIs for Axis Ranging, Label Formatting, Chart Modifiers (interaction) and Renderable Series. It is packed with features but unfortunately, it is not open sourced.

The Verdict

Both MPAndroidChart and Graph-View are good libraries, packed with a lot of features. GraphView is easier to implement as compared to MPAndroidChat (not that difficult either). Both of them have the features like pinch zoom. MPAndroidChart had the feature of scale adjustment even when the graph is being plotted. The rate of plotting was comparable in both but it was slightly faster in MPAndroidChart. So, finally GraphView is easier to implement but MPAndroidChart has slightly better performance. So, we integrated MPAndroidChart in PSLab Android application.

Integrating MPAndroidChart in PSLab Android App

In order to integrate MPAndroidChart in the Android project add the following code in the build.gradle of your project.

 

compile 'com.github.PhilJay:MPAndroidChart:v3.0.1'

Creating Oscilloscope like graph

If we observe an Oscilloscope, it has a black/blue screen with grid lines. An oscilloscope is a voltage vs time graph hence the x axis represents the time elapsed and y axis the voltage of the signal at the instant of time. There are left and right y axis for different channels.

An Oscilloscope

In order to implement a graph similar to that of Oscilloscope in PSLab Android App using MPAndroidChart library, the graph needed to be customized.

The following step was taken to customized the graph in Oscilloscope Activity.

Background Color

mChart.setBackgroundColor(Color.BLACK);

This sets the background color of the graph as black. mChart is an object of the Line graph.

Legend

Legend l = mChart.getLegend();
l.setForm(Legend.LegendForm.LINE);
l.setTextColor(Color.WHITE);

Here we are setting the Legend form. There are many options available for the same like SQUARE, CIRCLE, and LINE. We are using LINE Legend form.  Also, we set the white color for the legend text.

X Axis Customization

x = mChart.getXAxis();
x.setTextColor(Color.WHITE);

First, we create an object of XAxis and set the textcolor as white.

x.setDrawGridLines(true);

The above method draws the grid lines along the x axis.

x.setAxisMinimum(0f);
x.setAxisMaximum(875f);

Now we will set the range of x axis by setting minimum value as 0 and the maximum value is 875.

Y Axis  Customization

y1 = mChart.getAxisLeft();
y1.setTextColor(Color.WHITE);
y1.setAxisMaximum(16f);
y1.setAxisMinimum(-16f);
y1.setDrawGridLines(true);

This is similar to what we did in x axis formatting.

After performing the above steps we got the following results.

To follow the entire code for graph customization refer chartinit method in Oscilloscope Activity, PSLab Android repository.

Resources

Continue ReadingComparing Different Graph View Libraries and Integrating Them in PSLab Android Application

Generating Real-Time Graphs in PSLab Android App

In PSLab Android App, we need to log data from the sensors and correspondingly generate real-time graphs. Real-time graphs mean a data streaming chart that automatically updates itself after every n second. This was different from what we did in Oscilloscope’s graph, here we need to determine the relative time at which the data is recorded from the sensor by the PSLab.

Another thing we need to take care of was the range of x axis. Since the data to be streamed is ever growing, setting a large range of the x axis will only make reading sensor data tedious for the user. For this, the solution was to make real time rolling window graph. It’s like when the graph exceeds the maximum range of x axis, the graph doesn’t show the initial plots. For example, if I set that graph should show the data only for the 10-second window when the 11th-second data would be plot, the 1st-second data won’t be shown by the graph and maintains the difference between the maximum and the minimum range of the graph. The graph library we are going to use is MPAndroidChart. Let’s break-down the implementation step by step.

First, we create a long variable, startTime which records the time at which the entire process starts. This would be the reference time. Flags make sure when to reset this time.

if (flag == 0) {
   startTime = System.currentTimeMillis();
   flag = 1;
}

 

We used Async Tasks approach in which the data is from the sensors is acquired in the background thread and the graph is updated in the UI thread. Here we consider an example of the HMC5883L sensor, which is actually Magnetometer. We are calculating time elapsed by subtracting current time with the sartTime and the result is taken as the x coordinate.

private class SensorDataFetch extends AsyncTask<Void, Void, Void> {
   ArrayList<Double> dataHMC5883L = new ArrayList<Double>();
   long timeElapsed;

   @Override
   protected Void doInBackground(Void... params) {
       
     timeElapsed = (System.currentTimeMillis() - startTime) / 1000;

     entriesbx.add(new Entry((float) timeElapsed, dataHMC5883L.get(0).floatValue()));
     entriesby.add(new Entry((float) timeElapsed, dataHMC5883L.get(1).floatValue()));
     entriesbz.add(new Entry((float) timeElapsed, dataHMC5883L.get(2).floatValue()));
       
     return null;
   }

 

As we need to create a rolling window graph we require to add few lines of code with the standard implementation of the graph using MPAndroidChart. This entire code is placed under onPostExecute method of AsyncTasks. The following code sets data set for the Line Chart and tells the Line Chart that a new data is acquired. It’s very important to call notifyDataSetChanged, without this the things won’t work.

mChart.setData(data);
mChart.notifyDataSetChanged();

 

Now, we will set the visible range of x axis. This means that the graph window of the graph won’t change until and unless the range set by this method is not achieved. Here we are setting it to be 10 as we need a 10-second window.

mChart.setVisibleXRangeMaximum(10);

Then we will call moveViewToX method to move the view to the latest entry of the graph. Here, we have passed data.getEntryCount method which returns the no. of data points in the data set.

mChart.moveViewToX(data.getEntryCount());

 

We will get following results

To see the entire code visit this link.

Resources

Continue ReadingGenerating Real-Time Graphs in PSLab Android App

Integrating Stock Sensors with PSLab Android App

A sensor is a digital device (almost all the time an integrated circuit) which can receive data from outer environment and produce an electric signal proportional to that. This signal will be then processed by a microcontroller or a processor to provide useful functionalities. A mobile device running Android operating system usually has a few sensors built into it. The main purpose of these sensors is to provide user with better experience such as rotating the screen as he moves the device or turn off the screen when he is making a call to prevent unwanted screen touch events. PSLab Android application is capable of processing inputs received by different sensors plugged into it using the PSLab device and produce useful results. Developers are currently planning on integrating the stock sensors with the PSLab device so that the application can be used without the PSLab device.

This blog is about how to initiate a stock sensor available in the Android device and get readings from it. Sensor API provided by Google developers is really helpful in achieving this task. The process is consist of several steps. It is also important to note the fact that there are devices that support only a few sensors while some devices will support a lot of sensors. There are few basic sensors that are available in every device such as

  • “Accelerometer” – Measures acceleration along X, Y and Z axis
  • “Gyroscope” – Measures device rotation along X, Y and Z axis
  • “Light Sensor” – Measures illumination in Lux
  • “Proximity Sensor” – Measures distance to an obstacle from sensor

The implementing steps are as follows;

  1. Check availability of sensors

First step is to invoke the SensorManager from Android system services. This class has a method to list all the available sensors in the device.

SensorManager sensorManager;
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

Once the list is populated, we can iterate through this to find out if the required sensors are available and obstruct displaying activities related to sensors that are not supported by the device.

for (Sensor sensor : sensors) {
   switch (sensor.getType()) {
       case Sensor.TYPE_ACCELEROMETER:
           break;
       case Sensor.TYPE_GYROSCOPE:
           break;
       ...
   }
}

  1. Read data from sensors

To read data sent from the sensor, one should implement the SensorEventListener interface. Under this interface, there are two method needs to be overridden.

public class StockSensors extends AppCompatActivity implements SensorEventListener {

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) {

    }
}

Out of these two methods, onSensorChanged() method should be addressed. This method provides a parameter SensorEvent which supports a method call getType() which returns an integer value representing the type of sensor produced the event.

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
   switch (sensorEvent.sensor.getType()) {
       case Sensor.TYPE_ACCELEROMETER:
           break;
       case Sensor.TYPE_GYROSCOPE:
           break;
       ...
   }
}

Each available sensor should be registered under the SensorEventListener to make them available in onSensorChanged() method. The following code block illustrates how to modify the previous code to register each sensor easily with the listener.

for (Sensor sensor : sensors) {
   switch (sensor.getType()) {
       case Sensor.TYPE_ACCELEROMETER:
           sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);
           break;
       case Sensor.TYPE_GYROSCOPE:
           sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_UI);
           break;
   }
}

Depending on the readings we can provide user with numerical data or graphical data using graphs plotted using MPAndroidChart in PSLab Android application.

The following images illustrate how a similar implementation is available in Science Journal application developed by Google.

Resources

Continue ReadingIntegrating Stock Sensors with PSLab Android App