Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile

Scanned Data Visualization: Present Your Data in a Life-like Radar Screen

4.33/5 (5 votes)
8 Sep 2016CPOL4 min read 13.2K   449  
Draw a life-like radar screen by using simple code

Sample Image

Introduction

This is an Android application for drawing a radar screen using user's data like measuring data from sensors. You can simply draw a life-like radar screen by using this control.

This control will be displayed by three kinds of data, the first is data on a previous scanning stage, the second is data on a current scanning stage, and the last will be the average for previous and current values. If necessary, it can be changed to control shape that is a half circle screen having 0~180 degrees operating ranges or a full circle having 0~360 degrees.

Background

In my project to build a mobile robotic system, I needed to visualize measuring data from a sensor. Meanwhile, I was finding a similar project on surfing the Internet. What I found out was Larry's homepages (http://luckylarry.co.uk) But that example is of no use to me since that program is PC based code with the processing (the compiler developed by the non-profit foundation). So, I decided to develop newly a data visualization program based on Android system because there is a plan to develop the robotic system remotely controlled by a mobile device. However, because of being helped much by Larry's homepages, I express my gratitude for Mr. Larry.

Using the Code

This code is used simply by your project. First RadarView.java is added to your project. Then you edit resources file of the activity file that you want to display.

Definition

enum radarColor{LAY, PREVSWEEP,

CURSWEEP, AVRSWEEP}

This is an enum value for the setting color of scanned values.

void setDisplayColor(radarColor what,

int color)

A color variable will be set by Color.argb(), this has properties of alpha, red, green, blue.

void setDrawMode(boolean bPreSweep,

boolean bCurSweep,boolean bAvrSweep)

This is the option whether each value for three kinds of data is visible or not.
bPreSweep: Previous data visualization is enabled or disabled
bCurSweep: Current data visualization is enabled or disabled
bAvrSweep : Average data visualization is enabled or disabled [Average = (Previous data+Current data)/2]
void setHalfSweep(boolean ck) If a input variable is true Display screen will be half-circle, else it will be Full-circle. Display data range is 0 to 180 degrees for a half circle and 0 to 360 degree for a full circle.
[input: true] half-circle mode
[input: false] full-circle mode
void setMaxDistance(int val) This function is used to set maximum indicate value for detecting distance.
val: maximum distance.
void setValue(int index, int val) This is a function for setting current value.
index: a scanning angle with degree
val: a detecting value for distance

Resource

Add resource as below code in your layout.xml file. You have to change this package name (com.example.bskim).

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
	....
    tools:context="com.example.myradarctrlapp.MainActivity">

	
    <com.example.bskim.RadarView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/RadarView"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true">
	
</RelativeLayout>

Implementation

Sample Image Sample Image
                     full scan                                      half scan

RadarView Class (RadarView.java)

Scanned data is described by solid line from origin to sensing point. The line length is the distance between sensor and any object and the angle of line is detected orientation from base line(x-axis).
Sweep motion for detecting object is implemented by fan-shape.

Java
public class RadarView extends View {
	......

    void DrawDisplay(Canvas canvas, int radius, int centerx, int centery) {
        float x, y;
        boolean clockwise = false;

        float ratio = (float)radius/ (float)widthp*3.0f;
        int textsize = (int)(25.0* ratio);

        if ( (dectAngle - oldDectAngle) >= 0) {
            clockwise = false;
        } else {
            clockwise = true;
        }

        Paint paint=new Paint();
        paint.setStyle(Paint.Style.FILL_AND_STROKE);

        // previous sweep
        if(mbPrevSweep == true) {
            paint.setARGB(64, 0, 32, 0);
            Path path1st = new Path();
            path1st.reset();
            path1st.moveTo(centerx, centery);
            for (int i = 0; i < sweepAngle; i++) {
                x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) oldDistValue[i]));
                y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) oldDistValue[i]));
                path1st.lineTo(x, y);
            }
            canvas.drawPath(path1st, paint);
        }

        // current sweep
        if(mbCurSweep == true) {
            paint.setARGB(128, 0, 200, 0);
            Path path2nd = new Path();
            path2nd.reset();
            path2nd.moveTo(centerx, centery);
            for (int i = 0; i < sweepAngle; i++) {
                x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) distValue[i]));
                y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) distValue[i]));
                path2nd.lineTo(x, y);
            }
            canvas.drawPath(path2nd, paint);
        }

        // average
        if(mbAvrSweep == true) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setARGB(255, 0, 0, 255);
            Path pathavg = new Path();
            pathavg.reset();
            pathavg.moveTo(centerx, centery);
            for (int i = 0; i < sweepAngle; i++) {
                x = centerx + (int) (Math.cos(Math.toRadians((-i))) * 
                ((float) ((distValue[i] + oldDistValue[i]) / 2)));
                y = centery + (int) (Math.sin(Math.toRadians((-i))) * 
                ((float) ((distValue[i] + oldDistValue[i]) / 2)));
                pathavg.lineTo(x, y);
            }
            canvas.drawPath(pathavg, paint);
        }

        //sweep motion
        if(mbSweepMotion==true) {
            //sweep motion
            paint.setAntiAlias(true);
            paint.setStrokeWidth(7);

            int gradationAngle = 30;
            if(dectAngle < gradationAngle) gradationAngle = dectAngle;
            if(dectAngle> (sweepAngle-gradationAngle)) gradationAngle = sweepAngle-dectAngle;
            //scan motion drawing
            if (clockwise == false) {
                for (int i = gradationAngle; i >= 0; i--) {
                    if(i==0)
                        paint.setColor(mLayColor);
                    else paint.setARGB(128, 150+(100/gradationAngle * i), 
                    150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
                    canvas.drawLine(centerx, centery, centerx + 
                    (int)(Math.cos(Math.toRadians(-dectAngle + (i))) * 
                    (float)radius), centery + (int) (Math.sin(Math.toRadians(-dectAngle + (i))) * 
                    (float)radius), paint);
                }
            } else {
                for (int i = 0; i <= gradationAngle; i++) {
                    if(i==0)
                        paint.setColor(mLayColor);
                    else paint.setARGB(128, 150+(100/gradationAngle * i), 
                    150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
                    canvas.drawLine(centerx, centery, centerx + 
                    (int) (Math.cos(Math.toRadians(-dectAngle + (-i))) * (float) radius), 
                    centery + (int) (Math.sin(Math.toRadians(-dectAngle + (-i))) * 
                    (float) radius), paint);
                }
            }
        }

		......

    }
}

By changing a scanning orientation, the shadow's direction for scanning motion will be changed.

Sample Image

Clockwise Scan (180o,179o,....,2o,1o,0o)                     Counter-Clockwise Scan (0o,1o,2o,....,179o,180o)

Create View and Example of this Use

Create the object instance of RadarView and configure its properties.
For example, Full-circle mode, Scanning line color is green. A scanning distance range is set by constant value, MAX_DISTANCE and we can get a resource from pre-defined XML file as using findViewById(). R.id.RadarView is defined as android:id="@+id/RadarView" in layout.xml.

Java
RadarView mRadarView;

mRadarView = (RadarView) findViewById(R.id.RadarView);
mRadarView.setHalfSweep(false);
mRadarView.setDisplayColor(RadarView.radarColor.LAY, Color.argb(255,0,255,0));
mRadarView.setMaxDistance(MAX_DISTANCE);
mRadarView.setSweepMotion(true);

The below code shows the usage of this class. The data buffer to be drawn is filled by setValue(..) and it is used handler so that view is updated safely.

Java
TimerTask mTask;
Timer mTimer;

mTask = new TimerTask() {
    @Override
    public void run() {
        if(mRadarView.isHalfMode()==true) {
            if (angle > 180) flag = 0;
            if (angle <= 0) flag = 1;
            if (flag == 0) angle--;
            else angle++;
        }else {
            if(angle>360) angle = 0;
            else angle++;
        }
        int distance = mRandVal.nextInt(MAX_DISTANCE);
        
        mRadarView.setValue(angle, distance);
        RadarViewInvalidate();
        
    }
};
mTimer = new Timer();
mTimer.schedule(mTask, 500, 100);    //every 100ms after 500ms.

After you input new data from sensor by using setValue(..), you should call RadaViewInvalidate() to update view.

Java
Handler mDrawHandler = new Handler();

private void RadarViewInvalidate() {
    mDrawHandler.post(new Runnable() {
        @Override
        public void run() {
            mRadarView.invalidate();
        }
    });
}

That's it! I hope you could follow me easily. Thanks to everyone for reading this article.

Points of Interest

I would like to pay attention to creating a new graphic view class and a resource.

History

This article will be updated soon.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)