处理公共区域的多个地理围栏过渡

Posted

技术标签:

【中文标题】处理公共区域的多个地理围栏过渡【英文标题】:Handling multiple geofences transition with common area 【发布时间】:2014-05-19 18:52:38 【问题描述】:

如果我有这两个地理围栏,在注册这些地理围栏后,当我进入或离开这些圆圈的圆周时,我应该会收到通知。但是,如果我正在穿过公共区域,即从一个圆圈到另一个圆圈,我不希望我的应用发送通知。

有可能吗?如果是,那怎么办?

【问题讨论】:

是的,这是很有可能的,现在你必须在这里应用联合来获取圆周和子集下的公共位置和已知位置,以获得两个圆周的公共位置。现在您可以使用距中心点的距离并应用距离公式。 你能详细说明你的答案吗。thanx @jitainsharma 【参考方案1】:

您必须使用class 来监控您的围栏:

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());
    

您应该为要监控的每个区域创建侦听器,比如说 listener1 和 listener2。要优化这两个区域并整合它,最好的方法是使用 MongoDB 制作网格,在这种情况下,您甚至可以在构建网格时整合两个以上的点。

假设您要以一些 Lat-Lon 点的形式输出一个多边形,那么您可以生成一个grid,如下所示:

# 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

这种方法允许您使用智能电网实现very efficient 地理围栏以监控目标区域:

最近MongoDB 还添加了对android 的支持,从而为您的Android 应用后端集成提供了一种简单的方法。事实上,使用智能分布式数据的地理围栏开发预计会有越来越多的applications。

【讨论】:

【参考方案2】:

这可能是另一种选择:

所有地理围栏都有一个 id;我不确定它们是否需要是唯一的,但对于本次讨论,假设它们必须是唯一的。在您的两个地理围栏示例中,让我们使用 ids“fenceHome-1”和“fenceHome-2”作为显示的,第三个名为“someOther-1”的未显示。

现在您可以创建一个变量来存储用户所在的当前地理围栏。在此示例中,它将是一个带有地理围栏 ID 的字符串。让我们称之为

String currentGeofence = new String();

当用户输入新的地理围栏时,您现在可以检查 geofenceIds 是否相同。

     /** 
        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 
        

    

我就是这样做的。摆弄所有这些数学太难了。只需为您的地理围栏 ID 设置一个巧妙的命名约定。 关键是地理围栏的命名

在现实世界中,currentGeofence 需要是一个集合,因为用户可能在多个地理围栏中,并且 geofenceExit() 必须从 currentGeofence 中删除。

要记住 Android 通知管理器的另一件事是:如果您发送相同的通知两次,它只会发送一个通知。这可以用于您的优势。

【讨论】:

【参考方案3】:

非常示意性:

boolean isTransition1, isTransition2, isTransition, insideCircle1, insideCircle2, insideUnion, insideUnionPrev;

if (isTransition1 | isTransition2) 
    insideCircle1 = (dist(currPosition, centerCircle1) < radius1);
    insideCircle2 = (dist(currPosition, centerCircle2) < radius2);
    insideUnionPrev = insideUnion;
    insideUnion = insideCircle1 | insideCircle;
    isTransition = (insideUnion != insideUnionPrev);
    if (isTransition & insideUnion) println("Moved into region");
    if (isTransition & !insideUnion) println("Moved out of region");

【讨论】:

我认为它不适用于 Android 地理围栏,感谢thanx 的帮助 你为什么不认为它会起作用?很简单的逻辑,你只需使用回调设置 isTransition1,2 bools...

以上是关于处理公共区域的多个地理围栏过渡的主要内容,如果未能解决你的问题,请参考以下文章

Android 地理围栏(多边形)

如何创建一个嵌入多边形以进行地理围栏的圆形地理围栏区域?

使用新的 Android API 跟踪 100 多个同步地理围栏

ibeacons 和地理围栏结合 swift

Android - 需要添加超过 100 个地理围栏

动画及阴影