Skip to content

03 Current Position

Overview

You will learn how to use the android Location Service to acquire the current car position and strategies to display it on the map.

Base

Use Tutorial02 as a starting point for this project.

Requesting Location Updates

For this purpose, we will use the Android LocationManager to acquire location updates. For more advanced strategies to acquire location updates see Location Strategies.

To retrieve location updates, you must first request permission to access the NETWORK_PROVIDER or GPS_PROVIDER in your AndroidManifest.xml.

<manifest ... >
    <!-- permission to access the GPS_PROVIDER -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- permission to access the NETWORK_PROVIDER -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    ...
</manifest>

Attention

Be sure to set the Target-SDK version of your project to 22 to avoid problems with permission requests. Android target versions > 22 will not grant permissions to any resources like GPS or network when the user not explicitly allows this. If you want to use a newer target version, please implement the user request for yourself.

For retrieving location updates, you have to implement the LocationListener interface and request updates for either the NETWORK_PROVIDER or the GPS_PROVIDER. For this example we implement the LocationListener interface as a field locationListener in our MainActivity and request the updates in the onCreate method. We call removeUpdates in the onDestroy method to stop the location updates.

public class MainActivity extends AppCompatActivity {
    ...
    private LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            // here we will retreive new locations
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {}

        @Override
        public void onProviderEnabled(String s) {}

        @Override
        public void onProviderDisabled(String s) {}
    };

    private LocationManager locationManager;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        // request update use NETWORK provider if its sufficent to retreive less accurate positions
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        locationManager.removeUpdates(locationListener);
    }
    ...
}

Notifying the SDK

This would be enough to get the current position and show it on the MapView. But we also need to notify the SDK about the location updates. So we can use the position in a navigation for example. For this reason, we provide the GPSManager singleton in the NavigationSDK. We need to initialize it by calling openGPSDevice in our Application class.

public class Application extends android.app.Application {
    ...

    @Override
    public void onCreate() {
        ...

        try
        {
            GPSManager.getInstance().openGPSDevice(false);
        }
        catch (NavigationException e)
        {
            Log.e("Tutorial", "Unable to open GPS device: " + e.getMessage());
            startupException = e;
            return;
        }
    }

And then we need to push the location we are getting in onLocationChanged to the SDK.

public class MainActivity extends AppCompatActivity {
    ...
    private LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            GPSManager.getInstance().pushLocation(location);
        }
        ...
    };
    ...
}

The NavigationLoop

To update our MapView with the current position, we implement one of the core mechanisms of this Tutorial. The NavigationLoop is a runnable which is called every second by a timer to update our navigation state. We extend java.lang.Runnable. To implement this, we create a TimerTask in our MainActivity, start it in onCreate and stop it in onDestroy. Also we create a NavigationLoop member in our MainActivity, instantiate it in onCreate and call the run() method in the TimerTask.

public class NavigationLoop implements Runnable {
    public NavigationLoop() {
    }

    @Override
    public void run() {
       // here we will update our navigation state 
    }
}
public class MainActivity extends AppCompatActivity {
    NavigationLoop navigationLoop;
    ...
    TimerTask navigationLoopTrigger = new TimerTask(){
        @Override
        public void run() {
            navigationLoop.run();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        navigationLoop = new NavigationLoop(mapView);

        Timer t = new Timer();
        t.schedule(navigationLoopTrigger, 0, 1000);
    }

    @Override
    protected void onDestroy() {
        ...
        navigationLoopTrigger.cancel();
        ...
    }
}

To avoid updating our MainActivity or our MapView directly we also introduce a new interface called NavigationControl. Currently it will constist only of one callback function named onGPS which the NavigationLoop will call every run.

interface NavigationControl {
    void onGPS(final GPSData gps);
}

Because there will be more controls than only the MapView which possibly want to listen to our onGPS callback we store a List of them in our NavigationLoop:

class NavigationLoop implements Runnable {
    private final List<NavigationControl> controls = new ArrayList<>();

    public NavigationLoop() {
    }

    public void addControl(NavigationControl control) {
        controls.add(control);
    }

    public void removeControl(NavigationControl control) {
        controls.remove(control);
    }
    ...
}

We get the current GPS position from our GPSManager and notify all NavigationControls. To avoid notifying controls with a bogus position, we check if GPSData has a Fix with getFix() before calling onGPS.

class NavigationLoop implements Runnable {
    ...
    @Override
    public void run() {
        try
        {
            final GPSData gps = GPSManager.getInstance().getCurrentPosition();
            if (gps.getFix() > 0) {
                for (NavigationControl control : controls) {
                    control.onGPS(gps);
                }
            }
        }
        catch (NavigationException ignored)
        {
        }
    }
}

Update the map position

To update our MapView position we implement the NavigationControl Interface in our MapView class and update the center with the given GPSData

public class MapView extends com.ptvag.navigation.sdk.MapView implements NavigationControl {
    ...
    @Override
    public void onGPS(GPSData gps) {
        setCenter(gps.getGPSPositionMerc());
        update(); // force a redraw of the map with the new center
    }
}

Don't forget to register the MapView as a NavigationControl to the NavigationLoop.

public class MainActivity extends AppCompatActivity {
    private MapView mapView;

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        setContentView(R.layout.activity_main);
        mapView = (MapView)findViewById(R.id.map);

        ...

        navigationLoop = new NavigationLoop();
        navigationLoop.addControl(mapView);
    }

    ...

}

Draw the CCP

The easiest way to draw the current car position (CCP) is to use MapView.setMapMarker(). It comes in two styles:

MapMarkerStyles
round map marker directed map marker
MapView.MapMarkerStyle.Round MapView.MapMarkerStyle.Directed

We just set the MapMarker with the mercator position the orientation(course) of the GPSData and the desired style.

public class MapView extends com.ptvag.navigation.sdk.MapView implements NavigationControl {

    ...

    @Override
    public void onGPS(GPSData gps) {
        setMapMarker(
            gps.getGPSPositionMerc(),
            gps.getCourse(),
            true,
            com.ptvag.navigation.sdk.MapView.MapMarkerStyle.Directed
        );

        setCenter(gps.getGPSPositionMerc());
        update();
    }
}