08 Traffic information

Overview

In this tutorial, you will learn how to retrieve traffic information from the PTV.

Base

Use Tutorial07 as a starting point for this project.

Obtaining a key

For using the traffic information functionality in the sdk you will need an API key. Please contact the PTV Navigator product management to obtain such a key if you haven't already got one and set the key with NavigationSDK.setTrafficSearchWebConfiguration in the Application onCreate method.

public class Application extends android.app.Application {

    ...

    @Override
    public void onCreate() {
        ...

        // Uncomment the following line and replace <TOKEN> with your PTV Traffic Token if you want to use traffic
        // NavigationSDK.setTrafficSearchWebConfiguration("http://ti.ptvgroup.com/wfs", "<TOKEN>");
    }

    ...
}

Retrieving traffic informations

To retrieve traffic informations we have to call NavigationSDK.searchTrafficInformation with our current GPS position. This will trigger an asynchronous call to a traffic REST service. For getting notified when the traffic request is finished we implement an Observer and pass it to NavigationSDK.searchTrafficInformation. Because we want to request updates repeatedly we call it in the navigation loop every X times the loop is running.

public class NavigationLoop implements Runnable {

    ...

    Observer trafficObserver = new Observer() {
        @Override
        public void onFinished(final int error, int index) {
            super.onFinished(error, index);

            handler.post(new Runnable() {
                @Override
                public void run() {
                    Log.v("Traffic", "onFinished");
                }
            });
        }
    };

    long loop = 0;
    @Override
    public void run() {
        try
        {

            ...

            if ((loop % 10) == 0) {
                NavigationSDK.searchTrafficInformation(
                    gps.getGPSPosition(), 
                    trafficObserver
                );
            }

            ...

            loop++;
        }
        ...
    }

Traffic observer

The above mentioned Observer class is used for getting information if the previously triggered search has finished. This observer has one major function that we override: onFinished. (It has also a method onProgress, but we do not need this method in this chapter). In the above code, we only did output a string to the logcat that the function was called. Now, we output the traffic results to the logcat. For this, we retrieve the search results by calling NavigationSDK.getTrafficSearchResults(). We then iterate over the returned results and output the description of the current result to the logcat:

    Observer trafficObserver = new Observer() {
        @Override
        public void onFinished(final int error, int index) {
            super.onFinished(error, index);

            handler.post(new Runnable() {
                @Override
                public void run() {
                    Log.v("Traffic", "onFinished");
                    if (error == SDKErrorCode.Ok.getId()) {
                        Result<TrafficResults> results = 
                            NavigationSDK.getTrafficSearchResults();

                        for (TrafficSearchResult r : results.getResult()) {
                            Log.v("Traffic", r.getDescription());
                        }
                    }
                }
            });
        }
    };

Traffic activity

After starting the traffic search and getting informed that the search has finished, it is time to show the results in a separate activity. Let's start with adding a new activity t our project. Create a new activity called "TrafficActivity" and fill it with the code below:

package com.ptvag.navigation.tutorial;

import android.os.Bundle;
import android.widget.ListView;

import com.ptvag.navigation.helper.DrawerActivity;
import com.ptvag.navigation.sdk.NavigationSDK;
import com.ptvag.navigation.sdk.Result;
import com.ptvag.navigation.sdk.TrafficResults;

public class TrafficActivity extends DrawerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_traffic);

        ListView listView = (ListView) findViewById(R.id.listView);


        Result<TrafficResults> results = NavigationSDK.getTrafficSearchResults();

        TrafficAdapter adapter = new TrafficAdapter(this, R.layout.row_traffic_result, results.getResult());
        listView.setAdapter(adapter);
    }
}

The activity should show a listview, so we add a listview to the activity_traffic.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"
    android:id="@+id/activity_traffic"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.ptvag.navigation.tutorial.TrafficActivity">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/listView"/>

</RelativeLayout>

The above listview should show the results in rows, so we need an xml called row_traffic_result.xml to define the look of one row (this is the same pattern as in the search activity listview). The row view has the name "trafficResultItem".

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/trafficResultItem"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:padding="15dp"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
        android:maxLines="1"/>

</LinearLayout>

To fill the listview with the traffic search results, we need again an adapter class:

package com.ptvag.navigation.tutorial;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.ptvag.navigation.sdk.NativeArray;
import com.ptvag.navigation.sdk.Result;
import com.ptvag.navigation.sdk.SearchKind;
import com.ptvag.navigation.sdk.SearchResult;
import com.ptvag.navigation.sdk.TrafficResults;
import com.ptvag.navigation.sdk.TrafficSearchResult;

public class TrafficAdapter extends ArrayAdapter<TrafficSearchResult>
{
    protected final Activity context;
    protected TrafficResults results;

    public TrafficAdapter(Context context, int textViewResourceId, TrafficResults results)
    {
        super(context, textViewResourceId);
        this.context = (Activity) context;
        this.results = results;
    }

    @Override
    public int getCount()
    {
        return results.size();
    } 

    @Override
    public TrafficSearchResult getItem(int position)
    {
        return results.get(position);
    }

    @Override
    public long getItemId(int arg0)
    {
        if(getCount() > 0)
            return arg0;
        else 
            return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup)
    {
        if(getCount() < 1)
            return null;

        View v = convertView;
        if (v == null) { 
            LayoutInflater layoutInflater = (LayoutInflater)context.getLayoutInflater();
            v = layoutInflater.inflate(R.layout.row_traffic_result, null, true);
        }

        TrafficSearchResult result;
        result = getItem(position);

        String description = result.getDescription();

        TextView text = (TextView) v.findViewById(R.id.trafficResultItem);
        text.setText(description);

        return v;
    }

}

We have to override some functions to retrieve the current data for the needed listview row. In the constructor of the adapter, we set the traffic search result. In the getView method, we pick out the wanted result and set the text of the trafficResultItem view to the description of the wanted result.

The last thing to do is to show the traffic activity. This will be done by extending our drawer menu with another item. First, add the following to the drawer_menu.xml:

<item android:id="@+id/menu_traffic" android:title="traffic"></item>

Next, go to the main activity and add the following case statement to the onNavigationItemSelected() method:

 @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {
            ...
            case R.id.menu_traffic:
                Intent intent = new Intent(this, TrafficActivity.class);
                startActivity(intent);
                break;
        }
        ...
}

Now we have a new menu entry in our drawer, a click on it will bring up the traffic information activity.