This article is Part in a series of how to use latitude and longitude (geolocation) in xAPI. If you have not yet viewed the introduction and how to setup Google Maps , please head on over to Capture Latitude and Longitude with xAPI (GeoLocation) and save to Learning Record Store Part 1 or Part 2.
Now we have our app setup with Google Maps, we can look at capturing the latitude and longitude in our xAPI Statement.
This part is a little lengthy, so stay with me!
Part 2 – Building the Framework
Part 3 – Building and sending xAPI Statement
xAPI Statement
Our xAPI Statement needs the three main components being the Actor, Verb and Object (Activity)
Actor
For the Actor, we will need to know who the person is, so we need to capture this information. There are many ways to achieve this, but in the past I’ve simply used a Bootstrap Modal. It’s clean, neat and professional.
We need to add some references to the Bootstrap CDN for this to work. Go ahead and update your index.html file to include the links (note the locations of the js files).
We’ve also added the Modal code. This is located under the Map Div.
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>xAPI GeoLocation</title>
<style>
/* Set the size of the div element that contains the map */
#map {
min-height: 800px;
width: 98.5%;
margin-left: 15px;
}
</style>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js" type="text/javascript"></script>
<script defer src="https://maps.googleapis.com/maps/api/js?key=You-API-Key"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<script src="app.js"></script>
</head>
<body>
<div id="map"></div>
<div class="modal fade" tabindex="-1" role="dialog" id="mdlLogin" aria-hidden="true" style="display: none;">
<div class="modal-dialog modal-notify modal-success" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Login - Need to know who you are first</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="message" class="alert alert-danger" style="display: none;"></div>
<form>
<div class="form-group">
<label>Name </label>
<input type="text" class="form-control" id="name" placeholder="Your Name" value="" required="true">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" id="email" placeholder="Your Email address" value="" required="true">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="btnStart" class="btn btn-primary btn-sm"> Submit </button>
</div>
</div>
</div>
</div>
</body>
</html>
To show the modal, we add a single line of code to the app.js file, directly after the check that the document has loaded:
$('#mdlLogin').modal('show');
Verb
There are a number of Verbs we can use from the Registries, however we will use the Checked In Verb. This is defined as https://activitystrea.ms/schema/1.0/checkin and has a definition of: Indicates that the actor has checked-in to the object. For instance, a person checking-in to a place. The Verb statement will like:
"verb" : {"id" : "https://activitystrea.ms/schema/1.0/checkin",
"display" : {"en-US" : "checked in"}},
Object (Activity)
This is where we capture Something that the person did with a unique ID. For the purpose of the exercise, we will call our object ID https://xapi-geo-demo/ as the ID with a definition type of http://activitystrea.ms/schema/1.0/application. Our Object statement will look like:
"object" : { "id": "https://xapi-geo-demo",
"objectType": "Activity",
"definition": {
"type": "http://activitystrea.ms/schema/1.0/application",
"name": {
"en-US": "xAPI Geolocation Demo"
}
}}
Context and Extensions
Now we can add an Extension as part of the Context section in the xAPI Statement. This can be described as Metadata, so more data to describe the data. There are many extensions that can be used, however for capturing the geolocations coordinates there are a few options. We can capture the Latitude and Longitude as separate entries, or we could use the GeoJson extension.
For simplicity, we simply use the Latitude and Longitutde extensions. I will write another article on using the the GeoJson extension as this requires a reverse lookup for a name.
So, the following two extensions are what we need to capture:
http://id.tincanapi.com/extension/latitude
http://id.tincanapi.com/extension/longitude
Below is what our Context Extension xAPI statement will look like, without any data yet 🙂
"context":{
"contextActivities":{
"category":{
"id":"https://w3id.org/xapi/application"
}
},
"extensions": {
"http://id.tincanapi.com/extension/latitude": 00.000,
"http://id.tincanapi.com/extension/longitude": 000.000
}
}
Pulling the xAPI Statement together
Now we know what the xAPI statement will look like, we need to pull it all together with some data we collected. in our app.js file, we can look at capturing an event when the user clicks on btnStart. This is in our Modal for login.
You would of course add validation here, but let’s assume you’re all good and the Name and Email data is ok. Calling the Click event on the Start button would look like:
$('btnStart').on('click',function(){
var stmt = {"actor" : {"objectType":"Agent", "mbox" : "mailto:"+ $('#email').val(),"name": $('#email').val() },
"verb" : {"id" : "http://activitystrea.ms/schema/1.0/checkin",
"display" : {"en-US" : "checked in"}},
"object" : { "id": "https://xapi-geo-demo",
"objectType": "Activity",
"definition": {
"type": "http://activitystrea.ms/schema/1.0/application",
"name": {
"en-US": "xAPI Geolocation Demo"
}
}},
"context":{
"contextActivities":{
"category":{
"id":"https://w3id.org/xapi/application"
}
},
"extensions": {
"http://id.tincanapi.com/extension/latitude": geolocation.lat,
"http://id.tincanapi.com/extension/longitude": geolocation.long,
}
}
}
console.log(stmt);
})
There’s a bit going on here, but if you have a look all we have done is pulled everything together and added our dynamic data. The geoLocation.lat and geoLocation.long are what we put in the array geoLocation when the page first loaded.
The entire statement is put into a variable called stmt. We can write this out to the console to see if it is working.
Now we have the xAPI Statement, we need to send it to the LRS. This can be done in several ways, but the easiest is to use the xAPIWrapper.
Once we include the library and it’s dependencies, you need to configure the LRS to accept xAPI. This will be different based on the LRS you choose. Given you’re here and have an idea of what xAPI is and LRS, I’ll leave this bit up to you 🙂
Below is the index.html file with the xAPIWrapper files. You will need to download them.
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>xAPI GeoLocation</title>
<style>
/* Set the size of the div element that contains the map */
#map {
min-height: 800px;
width: 98.5%;
margin-left: 15px;
}
</style>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js" type="text/javascript"></script>
<script defer src="https://maps.googleapis.com/maps/api/js?key=Your-API-Key"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<script src="cryptojs_v3.1.2.js" type="text/javascript"></script>
<script src="xapiwrapper.js" type="text/javascript"></script>
<script src="app.js"></script>
</head>
<body>
<div id="map"></div>
<div class="modal fade" tabindex="-1" role="dialog" id="mdlLogin" aria-hidden="true" style="display: none;">
<div class="modal-dialog modal-notify modal-success" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Login - Need to know who you are first</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="message" class="alert alert-danger" style="display: none;"></div>
<form>
<div class="form-group">
<label>Name </label>
<input type="text" class="form-control" id="name" placeholder="Your Name" value="" required="true">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" id="email" placeholder="Your Email address" value="" required="true">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="btnStart" class="btn btn-primary btn-sm"> Submit </button>
</div>
</div>
</div>
</div>
</body>
</html>
Next is to configure the LRS in the app.js file. Add the following as the first line under the document being ready:
Note – Make sure you have a trailing / at the end of your LRS Endpoint
//Connect to LRS
var conf = {
"endpoint" : "https://your-endpoint.com/data/xAPI/"
"auth" : "Basic " + toBase64('username:password'),
};
ADL.XAPIWrapper.changeConfig(conf);
The last thing we need to do is to send the xAPI Statement to the LRS and close the Modal. We do this in the click event of the btnStart with the following line at the end of the event:
var resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
$('#mdlLogin').modal('hide');
Save all your files and run the code. Use the ADL xAPI Statement Viewer tool to see if your statement has been successfully saved. The video below shows the final product.
From here you can expand to sue reverse lookup and capture this in the geoJSON extension.