Trigger geofences client-side to track mobile assets with Nav SDK

This document describes what client-side geofencing is, when to use it, and how to apply it to use cases in a mobile application. It also shows how to implement an example on Android using the Google Navigation SDK.

Nav SDK with geofence detection
Nav SDK with geofence detection

Companies often need to know when a mobile device enters or leaves a particular area. This is accomplished by maintaining virtual geographic boundaries, or geofences, enabling software to trigger events when a device crosses a boundary.

Understanding when a certain vehicle traverses a boundary, is important for multiple use cases such as:

  • Customer engagement: Businesses can use geofencing to send push notifications to end users about special offers, events, or new products.
  • Security and safety: Businesses can use geofencing to create virtual perimeters around sensitive areas, such as data centers or warehouses, and alert security personnel if someone enters or leaves the area.
  • Transportation: Businesses can use geofencing to track the location of vehicles and optimize routes and schedules.

Therefore it is important that you know how to represent those zones (polygons) inside a client facing app. This app should track the device location and check whether it breached a certain geofence.

Scope

This document focuses on a client-side implementation of geofencing . That means that the client app must have:

  1. The polygons it needs to check against for breaches;
  2. Real-time location of the user
  3. Logic to check whether the current location is inside or outside any of the polygons.

This guide includes examples on Android but there are equivalent ways to accomplish this on iOS. Android Location Service has a built-in implementation for circular geofences which can be seen here. The reference code and description below is a starting point for more complex implementations.

The Navigation SDK is a native Android / iOS library added to the driver app. It is responsible for:

  • Obtaining road snapped locations from the app running it. This is more precise than Android’s FusedLocationProvider (FLP) as it uses Google’s road network to snap locations to the nearest road segment which makes ETA much more accurate, and other information from FLP.
  • Turn-by-turn experience which allows drivers to get efficiently from point A to point B taking into account real-time traffic and other route restrictions.
  • Firing events through event listeners and registered callbacks.

Listeners

Navigation SDK has many listeners that you can use. To name a few:

  • Location changes via RoadSnappedLocation provider.
  • Reroute events (user misses u-turn, left turn, etc and deviates from the recommended route) via ReroutingListener.
  • Arrival events (user arrives at the planned destination) via ArrivalListener.
  • Remaining distance and ETA events (get notified when the driver is about to arrive at the destination - based on meters, get notified when the driver is about to arrive at the destination - based on time) both available via .RemainingTimeOrDistanceChangedListener

In this guide only the RoadSnappedLocationProvider and its LocationListener is used.

The Client Side Geofencing Solution

Now let's step through building a client side geofencing capability. In the example below we have Navigation SDK operating in turn-by-turn mode and a polygon defined in the route representing our geofence.

Functional diagram
Functional diagram

  1. Geofences are stored in BigQuery and pulled by your backend.
  2. The backend periodically pushes the geofences out to the drive apps.
  3. The Driver navigates and the driver app is regularly checking the geofences for a trigger.
  4. The Driver App notifies the backend of a trigger event so that it can act.

As the vehicle moves along the route, the app regularly checks whether the polygon has been breached. When the app detects that it has crossed a geofence a message is displayed on the UI which says: Geofence breached.

Configure Dependencies for Android-Maps-Utils

This solution uses Android-Maps-Utils, an open-source library containing utilities that are useful for a wide range of applications using the Google Maps Android API.

This library is public and hosted on Github and can be accessed at:

  • Android: https://github.com/googlemaps/android-maps-utils
  • iOS: https://github.com/googlemaps/google-maps-ios-utils

To include this library in your Android app (scope of this document), you should modify your build.gradle file to include it. Note that this build.gradle file is for the module (app) you are building, not at the project level.

dependencies {
   ...
   // Utilities for Maps SDK for Android (requires Google Play Services)
   implementation 'com.google.maps.android:android-maps-utils:2.3.0'
}

Then, after you sync Gradle with your latest build.gradle file, you can import com.google.maps.android.PolyUtil in your Java file:

import com.google.android.gms.maps.model.PolygonOptions;
import com.google.maps.android.PolyUtil;

Define Your Geofences

Note that here also PolygonOptions is getting imported. The reason is that this what is being used to represent the polygon:

mPolygonOptions = new PolygonOptions()
       .add(new LatLng(29.4264525,-98.4948758))
       .add(new LatLng(29.4267029,-98.4948758))
       .add(new LatLng(29.4273742,-98.4945822))
       .add(new LatLng(29.4264562,-98.4943592))
       .fillColor(0x0000ff36)
       .strokePattern(Arrays.asList(new Dash(45.0f), new Gap(10.0f)))
       .strokeColor(Color.BLUE)
       .strokeWidth(5);

As you can see above, here we are defining a fixed polygon with pre-established coordinates - (latitude, longitude) pairs. However in real scenarios those coordinates and polygon definitions most times will come from a backend endpoint and are probably going to be fetched remotely. This means that the polygon(s) will have to be created on the fly by the app.

For more details on what can be specified in PolygonOptions please check here.

You should define the polygon(s) during the creation of your Fragment or Activity. For example:

protected void onCreate(Bundle savedInstanceState) {
   ...
   mPolygonOptions = new PolygonOptions()
           .add(new LatLng(29.4264525,-98.4948758))
           .add(new LatLng(29.4267029,-98.4948758))
           .add(new LatLng(29.4273742,-98.4945822))
           .add(new LatLng(29.4264562,-98.4943592))
           .fillColor(0x0000ff36)
           .strokePattern(Arrays.asList(new Dash(45.0f), new Gap(10.0f)))
           .strokeColor(Color.BLUE)
           .strokeWidth(5);

   ...// more code here
}

Listen for Location Updates

After defining your geofences you just need to create a location update listener to subscribe to the aforementioned event in Navigation SDK called RoadSnappedLocationProvider which will return the latest location of the device.

mLocListener = new RoadSnappedLocationProvider.LocationListener() {
   @Override
   public void onLocationChanged(Location snapped) {
       LatLng snappedL = new LatLng(snapped.getLatitude(), snapped.getLongitude());
       if(PolyUtil.containsLocation(snappedL, mPolygonOptions.getPoints(), true) && !mGeofenceBreached){
           Log.d("Geofence", "Vehicle has breached the polygon");
       }
   }
   @Override
   public void onRawLocationUpdate(Location location) {
   }
};

With Android-Maps-Utils then you can use PolyUtil.containsLocation to check whether the received location is inside the predefined polygon. In the example below the predefined polygon, representing the geofence, is used but in practice you might have multiple polygons and a loop would be required.

An Alternative Approach

This document focuses on a client facing application that checks for a custom geofence (polygon) breach. There are scenarios though that you may want to make such checks on your backend.

This means that the app would be reporting location updates to a backend and this backend then would check whether that vehicle breached a certain polygon, thus not depending on the client app to do the validation.

A possible solution would be as follows:

[Execution Environment] Server side geofencing architecture

An example architecture demonstrating a server side approach to geofencing.

Server-side solution
Server-side solution

  1. The driver app, using Driver SDK, sends location updates to Fleet Engine. Location updates and in-app navigation happen via Navigation SDK.
  2. Fleet Engine outputs those updates to Cloud Logging or Pub/Sub.
  3. The backend collects those location signals.
  4. Geofences are stored in Big Query for analysis by the backend.
  5. Upon triggering the geofence, alerts are sent to the Driver App.

In this architecture, Driver SDK and Fleet Engine are used. Fleet Engine can emit PubSub updates and generate log entries in Cloud Logging. In both instances, vehicle location can be retrieved.

The backend then could be monitoring the PubSub queue or reading logs and watching for vehicle updates. Then, whenever an update happens (or every few seconds, minutes, subject to the criticality of it), the backend could call BigQuery GIS functions to determine whether a given vehicle is inside or outside geofences. In case one or more geofences have been breached, the backend can act and trigger internal pipelines or other relevant workflows.

Conclusion

Geofencing is a powerful tool that can be used for various purposes. Businesses can use geofencing to target end users with relevant ads and promotions, provide location-based services, and improve security and safety.

The Navigation SDK provides useful event listeners that can detect many important moments during a journey. Companies often require custom geofences for specific use cases. In this document we demonstrated a way to achieve this, but the possibilities are endless. We look forward to seeing what you come up with.

Next Actions

Suggested Further Reading: