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.