Last active
May 14, 2017 13:21
-
-
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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