Skip to content

Instantly share code, notes, and snippets.

@nixta
Last active May 14, 2017 13:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nixta/2a0bab67fe014a8098d33240ec90de9c to your computer and use it in GitHub Desktop.
Save nixta/2a0bab67fe014a8098d33240ec90de9c to your computer and use it in GitHub Desktop.
A PubNub Geofencing Block (See structure of sample services below at lines 16&17).
export default (request) => {
let query = require('codec/query_string');
// return if the block does not have anything to analyze
if (!query) {
return request.ok();
}
// Require console to print debug information
let console = require('console');
let xhr = require('xhr');
let promise = require('promise');
// Configure these ArcGIS Feature Service end points. These are the User Point and GeoFence Polygon layers.
let usersURL = 'https://services.arcgis.com/OfH668nDRN7tbJh0/ArcGIS/rest/services/PubNub_Locations/FeatureServer/0';
let geofencesURL = 'https://services.arcgis.com/OfH668nDRN7tbJh0/ArcGIS/rest/services/PubNub_Geofences/FeatureServer/0';
// Parse out the required parameters
let userId = request.message.user;
let newLat = request.message.lat;
let newLng = request.message.lng;
if (userId === undefined || newLat === undefined || newLng === undefined) {
console.log('You must provide "user", "lat" and "lng" parameters!');
return request.abort('You must provide "user", "lat" and "lng" parameters!');
// Sample parameters
// {
// "user": "YourUserID",
// "lat": 40.70961304,
// "lng": -73.986471073
// }
}
let oldFencesQueryParams = getUserFencesQueryParams(userId);
let queryOldFencesURL = `${usersURL}/query?${query.stringify(oldFencesQueryParams)}`;
// Get a user record for the UserID to find the last fences we saw the user in.
let getLastFences = xhr.fetch(queryOldFencesURL)
.then((response) => {
return response.json()
.then((parsedResponse) => {
let oldGeofences = parsedResponse.features.map(function (f) {
return f.attributes.LastGeofenceIDs.split(',');
})[0];
if (parsedResponse.features.length > 0) {
// If this exists, we update later, else we add later.
request.message.existingUserOID = parsedResponse.features[0].attributes.OBJECTID;
}
oldGeofences = oldGeofences || [];
request.message.oldFences = oldGeofences;
return request.ok();
})
.catch((err) => {
console.log('Error happened parsing the old geofences response JSON', err);
return request;
});
})
.catch((err) => {
console.log('Error happened fetching the old geofences', err);
return request;
});
let newFencesQueryParams = getGeofenceQueryParams(newLat, newLng);
let queryNewFencesURL = `${geofencesURL}/query?${query.stringify(newFencesQueryParams)}`;
// Get the fences that the updated lat/lng are in
let getNewFences = xhr.fetch(queryNewFencesURL)
.then((response) => {
return response.json()
.then((parsedResponse) => {
let currentGeofences = parsedResponse.features.map(function (f) {
return f.attributes.FenceID
});
request.message.newFences = currentGeofences;
return request.ok();
})
.catch((err) => {
console.log('Error happened parsing the new geofences JSON', err);
return request;
});
})
.catch((err) => {
console.log('Error happened fetching the new geofences', err);
return request;
});
return promise.all([getLastFences, getNewFences])
.then((results) => {
let newFences = request.message.newFences;
let oldFences = request.message.oldFences;
let enteredFences = newFences.filter(function (newFence) {
return oldFences.indexOf(newFence) < 0;
});
let exitedFences = oldFences.filter(function (oldFence) {
return newFences.indexOf(oldFence) < 0;
});
console.log('Old fences', request.message.oldFences);
console.log('New fences', request.message.newFences);
console.log('Entered', enteredFences);
console.log('Exited', exitedFences);
request.message.enteredFences = enteredFences;
request.message.exitedFences = exitedFences;
return updateUserWithGeofences(newFences);
})
.catch((errs) => {
console.log('Error happened fetching old and new geofences: ', errs);
return request.abort();
});
function updateUserWithGeofences(newFences) {
let userUpdateAction;
// See http://resources.arcgis.com/en/help/arcgis-rest-api/#/Apply_Edits_Feature_Service_Layer/02r3000000r6000000/ for more details
let userJSON = {
geometry: {
'x': newLng,
'y': newLat,
'spatialReference': {
'wkid': 4326
}
},
attributes: {
'LastGeofenceIDs': newFences.join()
}
};
if (request.message.existingUserOID === undefined) {
// Adding new user
userJSON.attributes.UserID = userId;
userUpdateAction = "adds";
} else {
// Updating existing user (the record already has the UserID)
userJSON.attributes.OBJECTID = request.message.existingUserOID;
userUpdateAction = "updates";
}
// We don't need to pass this back.
delete request.message.existingUserOID;
let userUpdateBody = `f=json&${userUpdateAction}=${JSON.stringify(userJSON)}`;
let postOptions = {
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": userUpdateBody
};
let addUpdateUserURL = `${usersURL}/applyEdits`;
// Now update or create the user record with the current fences listed.
return xhr.fetch(addUpdateUserURL, postOptions)
.then((updateResponse) => {
console.log(updateResponse.body);
return updateResponse.json()
.then((parsedResponse) => {
let result, writeType;
if (parsedResponse.addResults.length > 0) {
result = parsedResponse.addResults[0];
writeType = "Add";
} else if (parsedResponse.updateResults.length > 0) {
result = parsedResponse.updateResults[0];
writeType = "Update";
} else {
console.log('No add or update result returned. This is unexpected.');
return request.abort('No add or update result returned. This is unexpected.');
}
if (result.success) {
console.log(`${writeType} completed successfully for ${userId}`, result);
request.message.arcgisObjectId = result.objectId;
return request.ok();
} else {
return request.abort('Add or Update user in ArcGIS failed.');
}
})
.catch((err) => {
console.log('Error happened on parsing the user update response JSON', err);
return request.abort();
});
})
.catch((err) => {
console.log('Error happened POSTing a user update', err);
return request.abort();
});
}
};
function getGeofenceQueryParams(lat, lng) {
// For more information on querying a feature service's layer, see:
// http://resources.arcgis.com/en/help/arcgis-rest-api/#/Query_Feature_Service_Layer/02r3000000r1000000/
//
// Here we'll query by geometry to see which geofences the updated user position falls within.
return {
geometryType: 'esriGeometryPoint',
geometry: `${lng},${lat}`,
inSR: 4326,
spatialRel: 'esriSpatialRelIntersects',
outFields: 'FenceID',
returnGeometry: false,
f: 'json'
};
}
function getUserFencesQueryParams(userId) {
// For more information on querying a feature service's layer, see:
// http://resources.arcgis.com/en/help/arcgis-rest-api/#/Query_Feature_Service_Layer/02r3000000r1000000/
//
// Here we query by UserID to get the last known geofences the user was within.
return {
where: `UserID = '${userId}'`,
outFields: 'OBJECTID,LastGeofenceIDs',
returnGeometry: false,
resultRecordCount: 1,
f: 'json'
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment