Implementing an Analysis to notify when a device is inside of a geofence

Implementing an Analysis to notify when a device is inside of a geofence

@Filipe da Silva de Oliveira

Here I will show how to implement an Analysis to notify me if my device is inside of any geofence.
Captura de tela de 2021-01-18 15-42-07

In this tutorial, I use Events’ code as a name, and when I send a notification is this name that will appear at the message.

Also, I store the Devices’ name at the variable that I have the location of the Device.

 {
   "variable": "location",
   "value": "Device name here",
   "location": {
     "lat": 35.770723,
     "lng": -78.677328
   }
 }

In fact, I am just showing one of many ways to do something like that. You can use this example the way you prefer.

1 - Copy this code below and paste it on a new NodeJS’s Analysis

const { Utils, Account, Analysis, Device, Services } = require("@tago-io/sdk");
const geolib = require("geolib");
// This function checks if our device is inside a polygon geofence
function insidePolygon(point, geofence) {
  const x = point[1];
  const y = point[0];
  let inside = false;
  for (let i = 0, j = geofence.length - 1; i < geofence.length; j = i++) {
    const xi = geofence[i][0];
    const yi = geofence[i][1];
    const xj = geofence[j][0];
    const yj = geofence[j][1];
    const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
    if (intersect) inside = !inside;
  }
  return inside;
}

// This function checks if our device is inside any geofence
function checkZones(point, geofence_list) {
  const insideZones = [];
  // The line below gets all Polygon geofences that we may have.
  const polygons = geofence_list.filter((x) => x.geolocation.type === "Polygon");
  if (polygons.length) {
    // Here we check if our device is inside any Polygon geofence using our function above.
    for (const polygon of polygons) {
        if (insidePolygon(point,  polygon.geolocation.coordinates[0])){
            insideZones.push(polygon.event);
        }
    }
  }
  // The line below gets all Point (circle) geofences that we may have.
  const circles = geofence_list.filter((x) => x.geolocation.type === "Point");
  if (circles.length) {
    // Here we check if our device is inside any Point geofence using a third party library called geolib.
    for (const circle of circles) {
        if(  
            geolib.isPointWithinRadius(
            { latitude: point[1], longitude: point[0] },
            { latitude: circle.geolocation.coordinates[0], longitude: circle.geolocation.coordinates[1] },
            circle.geolocation.radius
        )) {
            insideZones.push(circle.event);
        }
    }
  }
  return insideZones;
}

// This function help us get the device using just its id.
async function getDevice(account, device_id) {
  const customer_token = await Utils.getTokenByName(account, device_id);
  const customer_dev = new Device({ token: customer_token });
  return customer_dev;
}

async function startAnalysis(context, scope) {
  context.log("Running");

  if (!scope[0]) throw "Scope is missing"; // doesn't need to run if scope[0] is null

  // The code block below gets all environment variables and checks if we have the needed ones.
  const environment = Utils.envToJson(context.environment);
  if (!environment.account_token) throw "Missing account_token environment var";

  // The line below starts our notification service.
  const notification = new Services({ token: context.token }).Notification;

   const account = new Account({ token: environment.account_token });
  const device_id = scope[0].origin;

  // Here we get the device information using our account data and the device id.
  const device = await getDevice(account, device_id);

  // This checks if we received a location
  const location = scope.find((data) => data.variable === "YourLocationVariable");
  if (!location || !location.location) return context.log("No location found in the scope.");
  // Now we check if we have any geofences to go through.
  const geofences = await device.getData({ variable: "YourGeofenceVariableName", qty: 10 });
  const zones = geofences.map((geofence) => geofence.metadata);

  const insideZones = checkZones(location.location.coordinates, zones);
  if (insideZones && insideZones.length > 0) {
    const notificationMessage = `The ${location.value} is inside of ${insideZones.length > 1 ? "these" : "this"} geofence ${insideZones.join(", ")}`;
    notification.send({ title: "Inside geofence", message: notificationMessage });
    return;
  }
}

module.exports = new Analysis(startAnalysis);

Change YourLocationVariable for the variable that has the location coordinates of the device, YourGeofenceVariableName for the variable that is used to store the geofences, and include your account_token at Environment variables.

2 - Create an Action to trigger the Analysis when devices receive an update at the location variable.

In this action, you will choose which device do you want to trigger the analysis, in my case I didn’t put a condition, so every time that I receive an update of the location, the analysis will run.

That’s it, with these steps you can build receive notifications every time a device enters a Geofence. This code can be adapted to notify when the device is not inside of any geofence as well, you just need to include a new notification.send before the function startAnalysis finish.

Thanks!