Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling multiple geofences transition with common area

If I have these two geofences, after registering these geofences I should get notified when I'm entering or exiting the circumference of these circles. However, I don't want my App to send a notification if I'm moving through the common area, i.e. from once circle to another.

Is it possible? If so, then how?

Image of map with two circles intersecting

like image 276
Pankaj Avatar asked Apr 07 '14 13:04

Pankaj


People also ask

What is the limit on the number of geofences for each app?

Note: On single-user devices, there is a limit of 100 geofences per app. For multi-user devices, the limit is 100 geofences per app per device user.

Can you do geofencing on your own?

Building your own geofence solution seems easy enough: find the point of interest you want to geofence, find the latitude and longitude point it corresponds to, identify the precise boundaries in the real world the fence corresponds to, make sure the location sensors in your app are tuned just right..


2 Answers

You will have to use a class for monitoring your fencing:

public class GeofenceReceiver extends BroadcastReceiver {
    Context context;

    Intent broadcastIntent = new Intent();

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);

        if (LocationClient.hasError(intent)) {
            handleError(intent);
        } else {
            handleEnterExit(intent);
        }
    }

    private void handleError(Intent intent){
        // Get the error code
        int errorCode = LocationClient.getErrorCode(intent);

        // Get the error message
        String errorMessage = LocationServiceErrorMessages.getErrorString(
                context, errorCode);

        // Log the error
        Log.e(GeofenceUtils.APPTAG,
                context.getString(R.string.geofence_transition_error_detail,
                        errorMessage));

        // Set the action and error message for the broadcast intent
        broadcastIntent
                .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
                .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);

        // Broadcast the error *locally* to other components in this app
        LocalBroadcastManager.getInstance(context).sendBroadcast(
                broadcastIntent);
    }


    private void handleEnterExit(Intent intent) {
        // Get the type of transition (entry or exit)
        int transition = LocationClient.getGeofenceTransition(intent);

        // Test that a valid transition was reported
        if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
                || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {

            // Post a notification
            List<Geofence> geofences = LocationClient
                    .getTriggeringGeofences(intent);
            String[] geofenceIds = new String[geofences.size()];
            String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
                    geofenceIds);
            String transitionType = GeofenceUtils
                    .getTransitionString(transition);

            for (int index = 0; index < geofences.size(); index++) {
                Geofence geofence = geofences.get(index);
                ...do something with the geofence entry or exit. I'm saving them to a local sqlite db

            }
            // Create an Intent to broadcast to the app
            broadcastIntent
                    .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
                    .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
                            transitionType);

            LocalBroadcastManager.getInstance(MyApplication.getContext())
                    .sendBroadcast(broadcastIntent);

            // Log the transition type and a message
            Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
            Log.d(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_notification_text));

            // In debug mode, log the result
            Log.d(GeofenceUtils.APPTAG, "transition");

            // An invalid transition was reported
        } else {
            // Always log as an error
            Log.e(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_invalid_type,
                            transition));
        }
    }

    //Posts a notification in the notification bar when a transition 
    private void sendNotification(String transitionType, String locationName) {

        // Create an explicit content Intent that starts the main Activity
        Intent notificationIntent = new Intent(context, MainActivity.class);

        // Construct a task stack
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);

        // Adds the main Activity to the task stack as the parent
        stackBuilder.addParentStack(MainActivity.class);

        // Push the content Intent onto the stack
        stackBuilder.addNextIntent(notificationIntent);

        // Get a PendingIntent containing the entire back stack
        PendingIntent notificationPendingIntent = stackBuilder
                .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        // Get a notification builder that's compatible with platform versions
        // >= 4
        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                context);

        // Set the notification contents
        builder.setSmallIcon(R.drawable.ic_notification)
                .setContentTitle(transitionType + ": " + locationName)
                .setContentText(
                        context.getString(R.string.geofence_transition_notification_text))
                .setContentIntent(notificationPendingIntent);

        // Get an instance of the Notification manager
        NotificationManager mNotificationManager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);

        // Issue the notification
        mNotificationManager.notify(0, builder.build());
    }

You should create listeners for each one of the areas you wish to monitor, lets say listener1 and listener2. To optimize both areas and integrate it, the best approach is to make a grid using MongoDB, which in such case even allows you to integrate more than two points, as you build a grid.

enter image description here

Assuming that you're going to get out a polygon in the form of some Lat-Lon points, then you can generate a grid like the following:

# Method to get the min and max values for the polygon  
def get_bounding_box(coords)
# get max and min coords
max = coords.inject({lat:0, lon:0}) do |max, c|
max[:lon] = c[0] if c[0] > max[:lon]
max[:lat] = c[1] if c[1] > max[:lat]
max
end
min = coords.inject({lat:MAX_LAT, lon:MAX_LON}) do |min, c|
min[:lon] = c[0] if c[0] < min[:lon]
min[:lat] = c[1] if c[1] < min[:lat]
min
end
# add a little padding to the max and min
max.each {|k, v| max[k] += 1 }
min.each {|k, v| min[k] -= 1 }

{min: min, max: max}
end

def generate_grid(bounds)
lon_range = bounds[:min][:lon]...bounds[:max][:lon]
lat_range = bounds[:min][:lat]...bounds[:max][:lat]

grid = []
lon_range.each do |lon|
lat_range.each do |lat|
grid << [lon + 0.25, lat + 0.25]
grid << [lon + 0.25, lat + 0.75]
grid << [lon + 0.75, lat + 0.25]
grid << [lon + 0.75, lat + 0.75]
end
end

grid
end

Such approach allows you to achieve very efficient geofencing with smart grids for monitoring target areas:

enter image description here

Most recently MongoDB also added support for Android, thus providing an easy way for your Android App back-end integration. In fact geofencing development with smart distributed data is expected to have a growing number of applications.

like image 106
Avanz Avatar answered Oct 26 '22 15:10

Avanz


This may be an alternative:

All geofences have an id; I am not sure they need to be unique, but for this discussion let's say they must be unique. In your two geofence example let's use ids "fenceHome-1" and "fenceHome-2" for the ones shown and a third one called "someOther-1" which is not shown.

Now what you can do is create a variable to store the current geofence the user is in. In this example it will be a String with the geofence id. Let's call it

String currentGeofence = new String();

When the user enters a new geofence you can now check if the geofenceIds are the same.

     /** 
        geofenceEntered  get from the Intent.  Should be "fenceHome-1" or "fenceHome-2" or "someOther=1" 
        */    
    public void enteredGeoFence(String geofenceEntered) {

        // strip off the "-1" or "-2"  
        geofenceEntered = geofenceEntered.stripOff(); 

        if (currentGoofence.equals(geofenceEntered) == false} {
           // user entered a new geofence  Ex: "SomeOther-1" to "fenceHome-1" 
           sendNotification(geofenceEntered, .....); 
           currentGeofence = geofencecEntered;
        } else {
           // user entered a geofence with in the same 'area'.  Ex:  "fenceHome-1" to "fenceHome-2" 
           // do nothing 
        }

    }

That is how I would do it. Fiddling with all that math is way too hard. Just set a clever naming convention for you geofence ids. The key is the naming of the geofences.

In the real world currentGeofence would need to be a Collection as a user may be in multiple geofences and the geofenceExit() would have to remove from currentGeofence.

One other thing to remember of the Android Notification manager is: if you send the same notification twice it will only send one notification. This could be used to your advantage.

like image 30
fishjd Avatar answered Oct 26 '22 15:10

fishjd