Managing Geofences and Taking Action on Breaches

Geofence behavior, or the ability to take action when a device moves into or out of a geographic location, is accomplished using the Device Location features, such as Device Location Observations to update the device's location history. Then using a Policy attached to the device or device type and referencing a Geographic Region, you can take action on geofence breach.

Geographic Regions

Geographic regions are objects in the system that describe a real-world location. The geography component is handled by a GeoJSON object in the geojson field. Only a certain subset of GeoJSON is supported, as follows:

  • Must be of type 'Feature'
  • The 'geometry' must be either of type 'Point' or 'Polygon'
  • All positions in geometry.coordinates (latitude, longitude pairs) are assumed to be in the WSG84 coordinate reference system
  • If the geometry is a polygon, the coordinates must contain exactly one linear ring (array), which must be a closed polygon of between 4 and 50 positions. As noted in the GeoJSON spec, a closed polygon means that the first and last coordinate should be identical.
  • "Simple" points are not allowed. If the geometry is a 'Point', the properties of the feature must contain a type property set to 'Circle', and a radius property which is the size of the circle in meters. The radius may be a string or number, but it must be parsable to a positive number.

Here's an example of a valid polygon region:

{
    "name": "Valid Polygon Region",
    "metadata": {
        "metadata key": "metadata-content"
    },
    "geojson": {
        "type": "Feature",
        "properties": null,
        "geometry": {
            "type": "Polygon",
            "coordinates": [
                [
                    [
                        -70.6640625,
                        10.833305983642491
                    ],
                    [
                        -66.4453125,
                        4.565473550710278
                    ],
                    [
                        -58.71093750000001,
                        6.315298538330033
                    ],
                    [
                        -61.87499999999999,
                        12.897489183755892
                    ],
                    [
                        -69.9609375,
                        14.944784875088372
                    ],
                    [
                        -70.6640625,
                        10.833305983642491
                    ]
                ]
            ]
        }
    }
}

And here's an example of a valid point/radius region:

{
    "name": "Point/Radius Region",
    "geojson": {
        "type": "Feature",
        "properties": {
            "radius": 100,
            "type": "Circle"
        },
        "geometry": {
            "type": "Point",
            "coordinates": [
                -105.00234679450149,
                39.752580481707774
            ]
        }
    }
}

Testing and Understanding Device Location Behavior

Before having the system take action automatically when a device leaves or arrives at a geographic region, it may be helpful to understand device location in relation to a geographic region. There are two APIs that give some useful insights:

Location Relation API

The Location Relation API endpoint will show a device's last known location geospatial relation with a region. This will allow you to get a better understanding of a device's position in relation to a region. Example:

{
    "region_id": "d507809d-3c06-4abd-b112-b3a8e13c55d0",
    "device_location_observation": {
        "_id": "60ad7f09bdf3e70001d3cf3c",
        "origin": "cloud",
        "created_at": "2021-05-25T22:49:45.679333Z",
        "updated_at": "2021-05-25T22:49:45.679333Z",
        "company_id": "location_test_company",
        "user_id": "60ad7ec7bdf3e70001d3cf33",
        "device_id": "60ad7ec8bdf3e70001d3cf37",
        "device_unique_id": "location_producer",
        "via": "api",
        "generated_at": "2021-05-25T22:49:45.679333Z",
        "horizontal_accuracy_meters": 20,
        "latitude": "39.75403730911101",
        "longitude": "-105.00867082594193",
        "geojson": {"type":"Feature","geometry":{"type":"Point","coordinates":[-105.00867082594193,39.75403730911101]},"properties":{"date":"2021-05-25T22:49:45.679332618Z","device_location_observation_id":"60ad7f09bdf3e70001d3cf3c","radius":20,"type":"Circle"}}
    },
    "confidence_in": 0,
    "geojson": {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-105.003054142,39.752048526],[-105.001670122,39.752048526],[-105.001670122,39.753129076],[-105.003054142,39.753129076],[-105.003054142,39.752048526]]]},"properties":{"name":"Triangle Building","region_id":"d507809d-3c06-4abd-b112-b3a8e13c55d0"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-105.008437533,39.754037017],[-105.008442015,39.754001861],[-105.008455296,39.753968055],[-105.008476867,39.753936899],[-105.008505898,39.753909589],[-105.008541274,39.753887176],[-105.008581635,39.75387052],[-105.008625431,39.753860262],[-105.008670977,39.753856796],[-105.008716525,39.753860255],[-105.008760323,39.753870507],[-105.008800688,39.753887156],[-105.00883607,39.753909564],[-105.008865108,39.75393687],[-105.008886687,39.753968023],[-105.008899977,39.754001827],[-105.008904467,39.754036982],[-105.008899986,39.754072139],[-105.008886704,39.754105945],[-105.008865134,39.754137101],[-105.008836102,39.754164411],[-105.008800726,39.754186824],[-105.008760365,39.75420348],[-105.008716569,39.754213738],[-105.008671023,39.754217204],[-105.008625475,39.754213745],[-105.008581677,39.754203493],[-105.008541311,39.754186844],[-105.00850593,39.754164435],[-105.008476891,39.75413713],[-105.008455313,39.754105977],[-105.008442023,39.754072173],[-105.008437533,39.754037017]]]},"properties":{"date":"2021-05-25T22:49:45.679333Z","device_location_observation_id":"60ad7f09bdf3e70001d3cf3c","radius":20,"type":"Circle"}}]}
}

The properties of a LocationRelation are:

  • region_id: The ID of the associated region
  • device_location_observation: The most recent device location observation associated with the device (ordered by generated_at).
  • confidence_in: The confidence that the device's location is inside the region. A confidence of 0 implies 100% confidence that the device is outside the region
  • geojson: A GeoJSON FeatureCollection that contains a Feature for the location observation and the region. The properties of the location observations also include the order of occurrence, given by generated_at.

Confidence

Confidence is a measurement of the intersection of the device's probable location, based on its radius of accuracy, and the area of the region.

For instance, if the accuracy radius of a device's location is 20m, and the center point coincides with the center point of a region who's radius is 100m, confidence_in will be 100.

Another example: if the device's radius of accuracy is 100m (an area of approximately 31,416m) and the associated region's area is 20m (an area of approximately 1,257m), and completely inside the device observation's area, the area of intersection is ~1,257m, which is ~4% of the observation's area. So the confidence that the device was in the region is 4% (confidence_in: 4), and the confidence it was out is 96%. And so on, given other scenarios with different intersection areas.

📘

Circles Are Approximated

Note that in all cases of point/radius geography, which includes all device location observations and any circular geographic regions, the system uses an approximation of a circle to calculate the intersection of geographies. The circle approximation is a 32 point polygon. This means that area of intersection will be slightly different than using π r² to calculate the area.

Location Transition API

The Location Transition API will represent multiple device locations and a region. This will allow you to get a better understanding of a device's movement in relation to a region. Example

{
    "device_name": "Location Test Device",
    "device_id": "60ad7ec8bdf3e70001d3cf37",
    "company_id": "location_test_company",
    "device_unique_id": "location_producer",
    "region_name": "Triangle Building",
    "region_id": "d507809d-3c06-4abd-b112-b3a8e13c55d0",
    "location_relations": [
        {
            "region_id": "d507809d-3c06-4abd-b112-b3a8e13c55d0",
            "device_location_observation": {
                "_id": "60ad7f09bdf3e70001d3cf3c",
                "origin": "cloud",
                "created_at": "2021-05-25T22:49:45.679333Z",
                "updated_at": "2021-05-25T22:49:45.679333Z",
                "company_id": "location_test_company",
                "user_id": "60ad7ec7bdf3e70001d3cf33",
                "device_id": "60ad7ec8bdf3e70001d3cf37",
                "device_unique_id": "location_producer",
                "via": "api",
                "generated_at": "2021-05-25T22:49:45.679333Z",
                "horizontal_accuracy_meters": 20,
                "latitude": "39.75403730911101",
                "longitude": "-105.00867082594193",
                "geojson": {"type":"Feature","geometry":{"type":"Point","coordinates":[-105.00867082594193,39.75403730911101]},"properties":{"date":"2021-05-25T22:49:45.679332618Z","device_location_observation_id":"60ad7f09bdf3e70001d3cf3c","radius":20,"type":"Circle"}}
            },
            "confidence_in": 0
        },
        {
            "region_id": "d507809d-3c06-4abd-b112-b3a8e13c55d0",
            "device_location_observation": {
                "_id": "60ad80e4bdf3e70001d3cf3f",
                "origin": "cloud",
                "created_at": "2021-05-25T22:57:40.28036Z",
                "updated_at": "2021-05-25T22:57:40.28036Z",
                "company_id": "location_test_company",
                "user_id": "60ad7ec7bdf3e70001d3cf33",
                "device_id": "60ad7ec8bdf3e70001d3cf37",
                "device_unique_id": "location_producer",
                "via": "api",
                "generated_at": "2021-05-25T22:57:40.28036Z",
                "horizontal_accuracy_meters": 20,
                "latitude": "39.75254659332623",
                "longitude": "-105.00231808069262",
                "geojson": {"type":"Feature","geometry":{"type":"Point","coordinates":[-105.00231808069262,39.75254659332623]},"properties":{"date":"2021-05-25T22:57:40.280360001Z","device_location_observation_id":"60ad80e4bdf3e70001d3cf3f","radius":20,"type":"Circle"}}
            },
            "confidence_in": 100
        }
    ],
    "geojson": {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-105.003054142,39.752048526],[-105.001670122,39.752048526],[-105.001670122,39.753129076],[-105.003054142,39.753129076],[-105.003054142,39.752048526]]]},"properties":{"name":"Triangle Building","region_id":"d507809d-3c06-4abd-b112-b3a8e13c55d0"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-105.008437533,39.754037017],[-105.008442015,39.754001861],[-105.008455296,39.753968055],[-105.008476867,39.753936899],[-105.008505898,39.753909589],[-105.008541274,39.753887176],[-105.008581635,39.75387052],[-105.008625431,39.753860262],[-105.008670977,39.753856796],[-105.008716525,39.753860255],[-105.008760323,39.753870507],[-105.008800688,39.753887156],[-105.00883607,39.753909564],[-105.008865108,39.75393687],[-105.008886687,39.753968023],[-105.008899977,39.754001827],[-105.008904467,39.754036982],[-105.008899986,39.754072139],[-105.008886704,39.754105945],[-105.008865134,39.754137101],[-105.008836102,39.754164411],[-105.008800726,39.754186824],[-105.008760365,39.75420348],[-105.008716569,39.754213738],[-105.008671023,39.754217204],[-105.008625475,39.754213745],[-105.008581677,39.754203493],[-105.008541311,39.754186844],[-105.00850593,39.754164435],[-105.008476891,39.75413713],[-105.008455313,39.754105977],[-105.008442023,39.754072173],[-105.008437533,39.754037017]]]},"properties":{"date":"2021-05-25T22:49:45.679333Z","device_location_observation_id":"60ad7f09bdf3e70001d3cf3c","order":1,"radius":20,"type":"Circle"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-105.002084538,39.752547004],[-105.002089023,39.752511848],[-105.002102307,39.752478043],[-105.00212388,39.752446888],[-105.002152913,39.75241958],[-105.002188291,39.752397169],[-105.002228653,39.752380515],[-105.002272448,39.75237026],[-105.002317994,39.752366796],[-105.00236354,39.752370258],[-105.002407336,39.752380512],[-105.002447699,39.752397163],[-105.002483078,39.752419573],[-105.002512113,39.75244688],[-105.002533688,39.752478034],[-105.002546975,39.752511839],[-105.002551462,39.752546995],[-105.002546977,39.752582151],[-105.002533693,39.752615956],[-105.00251212,39.752647112],[-105.002483087,39.75267442],[-105.00244771,39.752696831],[-105.002407348,39.752713485],[-105.002363552,39.75272374],[-105.002318006,39.752727204],[-105.00227246,39.752723742],[-105.002228663,39.752713488],[-105.0021883,39.752696836],[-105.002152921,39.752674426],[-105.002123886,39.75264712],[-105.002102311,39.752615965],[-105.002089025,39.75258216],[-105.002084538,39.752547004]]]},"properties":{"date":"2021-05-25T22:57:40.28036Z","device_location_observation_id":"60ad80e4bdf3e70001d3cf3f","order":2,"radius":20,"type":"Circle"}}]}
}

The fields of a LocationTransition are relatively self explanatory, but note that location_relations contains an array of LocationRelation objects (see above). The geojson field contains a FeatureCollection which contains Feature objects for each device location observation and finally the region itself. The properties of the device location features also contain an order field, which helps identify the order of the observation in the set.

📘

Using GeoJSON

The GeoJSON represented in both LocationRelation and LocationTransition objects is helpful for a number of things. First of all, many web mapping frameworks will accept GeoJSON, so displaying the LocationTransition results of a geofence policy (see below) is easy. Also, for help in understanding a location relation or location transition, simply taking the GeoJSON and pasting it into http://geojson.io will give you a sense of what's going on with location.

1888

A location transition GeoJSON feature collection showing four device locations in relation to a circular geographic region, as displayed on http://geojson.io

Geofence Breach Policies/Rules

To take action when a device moves in relation to a Geographic region, create a policy/rule with a rule condition of type device_location_transition. Example:

{
    "company_id": "my_company_id",
    "description": "Device Arrival Geofence Policy",
    "active": true,
    "cloud_rule": true,
    "rule_condition": {
        "type": "device_location_transition",
        "location_transition_evaluator": {
            "region_id" : "94c3a16a-19bd-498c-b527-ceb5f4d6b5d5",
            "direction" : "arriving",
            "num_observations_to_consider" : 2,
            "required_num_breaching_observations" : 1,
            "confidence_threshold" : 1,
            "initial_observation_confidence_threshold": 0,
            "allow_breach_on_initial_observation" : false
        }
    },
    "then_actions": [
        {
            "type": "notification",
            "body_template": "Device {{.Device.UniqueID}} arrived at {{.LocationTransition.RegionName}}"
        }
    ]
}

The rule condition's location_transition_evaluator is required when the type is device_location_transition. It must refer to the target geographic region by id, in the region_id field. Beyond that, the object contains a number of parameters to help manage the details of the geofence breach.

Location Transition Evaluator Options

  • direction: Either arriving or leaving. This configures the rule to trigger when the device is either leaving or arriving at the associated region.
  • num_observations_to_consider: How many location observations to review when deciding if the device arrived at or left the region. The default and minimum is 2, the maximum is 5.
  • required_num_breaching_observations: How many location observations must be inside the region, for an arriving direction, or outside the region for a leaving direction. Must be less than num_observations_to_consider by a least one. The default and minimum is 1, and the maximum is 4.
  • confidence_threshold: The percent confidence to use when determining if a the device location observation is "inside" of a region. Default is 1 (1% overlap in area between the device location and the region). Must be between 1 and 100. For example, if confidence_threshold is set to 50, location observations with 49% intersection with the region will be considered "outside".
  • initial_observation_confidence_threshold: The percent confidence to use when determining if the first considered observation (see num_observations_to_consider) meets the expected conditions for a breach. Must be greater than 0 for 'leaving' breaches (meaning "the device must have been in the region with at least 1% confidence at the first eligible observation). Likely should be (but is not required to be) 0 for 'arriving' breaches (meaning "the device should be outside the region before being considered to be in and thus arriving")
  • allow_breach_on_initial_observation: Whether to allow a breach based on a device's first location observation, with no past observations to consider. For an "arriving" geofence rule, in the case that a device's first location observation is "inside" of a region, if allow_breach_on_initial_observation is true, the policy will evaluate true. If false, the rule will not evaluate true until in the future the device produces location observations that are first "outside" and then later "inside" of the region

🚧

Confidence Thresholds and Evaluator Options Defaults

Confidence threshold defaults to 1

  • To consider "outside" only device locations observations that do not intersect with the region at all
  • To consider "inside" any device locations observations that slightly intersect with the region

As you can imagine, this is designed to be as forgiving as possible in interpreting "arrival" breaches and as strict as possible on "leaving" breaches.

Required number of breaching observations

Changing required_num_breaching_observations allows you to have a little more assurance that the device actually did what its location observations are saying. Changing this parameter might be more useful for "leaving" geofences than "arriving", especially if the device's location "jumps" a lot.

These parameters are there to give some leeway in tuning the breaches for your device's particular situations. Be cautious and do plenty of testing with real world data before changing these values too much.

Data Available to Rule Actions

On the context object available for templating, the LocationTransition that drives the geofence rule is available. It can either provide simple information to the rule action template, such as "Region Name" in the above example, or it can be forwarded into another system via http.