Skip to content

Instantly share code, notes, and snippets.

@mkruisselbrink
Last active August 29, 2015 14:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mkruisselbrink/ea833f70041ab3c2aa69 to your computer and use it in GitHub Desktop.
Save mkruisselbrink/ea833f70041ab3c2aa69 to your computer and use it in GitHub Desktop.

Geofencing Explained

What's All This About?

This API will allow web applications to react to changes in the users location without having to poll/watch the position constantly (allowing the device to be more energy efficient). Essentially it allows web applications to do things based on physical proximity of the device to specific locations.

Draft spec

[exposed=Window&Worker]
partial interface Navigator {
  readonly attribute Geofencing geofencing;
};
 
partial interface ServiceWorkerGlobalScope
{
  attribute EventHandler ongeofenceenter;
  attribute EventHandler ongeofenceleave;
};
 
[NoInterfaceObject]
interface GeofencingEvent {
  readonly attribute GeofencingRegion region;
  readonly attribute Position position;
}
 
[NoInterfaceObject]
interface Geofencing {
  // Registers a new region
  Promise<undefined> registerRegion(GeofencingRegion region);
  // Unregisters a region
  Promise<undefined> unregisterRegion(DOMString regionId);
  // Get all the currently registered regions
  Promise<sequence<GeofencingRegion>> getRegisteredRegions();
  // Get the current state of a particular registered region
  // (I'm not entirely sure about the value of this method)
  Promise<GeofencingRegionState> getRegionState(DOMString regionId);
};
 
dictionary GeofencingRegionState {
  readonly attribute GeofencingRegion region;
  readonly attribute DOMString state; // "inside", "outside"
};

// Base class for various supported region types 
[NoInterfaceObject]
interface GeofencingRegion {
  readonly attribute DOMString id;
};
 
// A specific geographic point. Different from Coordinates in the
// existing geolocation spec, since Coordinates has required fields
// like accuracy that make no sense here.
dictionary GeolocationPoint {
  double latitude;
  double longitude;
  double? altitude; // probably don't need/want altitude for now
};
 
// options dictionary has center and radius fields
[Constructor(optional String? id, dictionary options), exposed=Window&Worker]
interface CircularRegion : GeofencingRegion {
  // these values are completely arbitrary and I have no idea if they make sense
  // or if it even makes sense to have these constants in the first place.
  // (iOS' geofencing API does have an equivalent to the MAX_RADIUS field).
  const double MIN_RADIUS = 10;
  const double MAX_RADIUS = 1000;
  readonly attribute GeolocationPoint center;
  readonly attribute double radius;
};
 
// Other potential region types, I'm not actually suggesting to implement any
// of these, but they could be interested additions later:

// Polygon of nonintersecting line segments.
[Constructor(optional String? id, dictionary options), exposed=Window&Worker]
interface PolygonRegion : GeofencingRegion {
  readonly attribute sequence<GeolocationPoint> points;
};
 
// A region centered around whatever the current location of the device is
[Constructor(optional String? id, dictionary options), exposed=Window&Worker]
interface CurrentLocationRegion : GeofencingRegion {
  readonly attribute double radius;
};
 
// Show some UI to let the user select a region. Not sure if this makes sense,
// and not sure what the use case would be.
[Constructor(optional String? id), exposed=Window&Worker]
interface UserSelectedRegion : GeofencingRegion {
};

Registering A Geofence

Registering for a geofence is straightforward:

<!DOCTYPE html>
<!-- https://app.example.com/index.html -->
<html>
  <head>
    <script>
      navigator.serviceWorker.register("/sw.js");

      navigator.serviceWorker.whenReady().then(function(sw) {
        navigator.geofencing.registerRegion(new CircularRegion(
          "string id of region",
          {
            center: {latitude: 37.421999, longitude: -122.084015},
            radius: 100.0               // meters
          }
        )).then(function() { // Success
                  // No resolved value
                  // Success, region is now registered
                },
                function() { // Failure
                });
      });
    </script>
  </head>
  <body> ... </body>
</html>

TODO(mek): Maybe pass the ID as a separate parameter, instead of it being part of the region. Not sure what is more normal for APIs like this.

Handling Geofence Events

Location notification happens from the Service Worker context via the new ongeofenceenter and ongeofenceenter events.

// sw.js
self.ongeofenceenter = function(event) {
  if (event.region.id == "string id of region") {
    // entered the region, do something useful
  } else {
    // garbage collect unknown regions (perhaps from older pages).
    navigator.geofencing.unregisterRegion(event.region.id);
};

self.ongeofenceexit = function(event) {
  if (event.region.id == "string id of region") {
    // left the region, do something useful
  } else {
    // garbage collect unknown regions (perhaps from older pages).
    navigator.geofencing.unregisterRegion(event.region.id);
};

Removing Geofences

navigator.geofencing.unregisterRegion("string id of region to remove").then(
  function() {
    // removed successfully
  },
  function() {
    // removal failed, possibly because there was no region registered with the id
  });

Enumerating registered Geofences

navigator.geofencing.getRegisteredRegions().then(
  function(regions) {
    // regions is an array of all currently registered regions
  },
  function() {
    // failed to get list of regions.
    // simply no regions being registered is not considered a failure.
  });

Notes

  • As written here Service Workers are a requirement for Geofencing. It's not clear yet if we'll be able to convince other browser vendors of this, but relaxing this requirement would change the API too much anyway.
  • Since Service Workers are a requirement for Geofencing, and since Service Workers are limited to HTTPS origins, sites served without encryption will always fail to register for fences (and if the SW requirement gets relaxed this should still be limited to secure origins).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment