Location Services with HTML5 GeoLocation
INTRO
AI2 provides a Location Sensor and Location services through the Map component natively, but there is another way, using the HTML5 GeoLocation API through a Webviewer. I aim to show you the basic setup of an aia project to run this, and a few examples of what you might do with it.
The API appears to use a "fused" approach to Google's Location Services, that is, a combination of IP, Data, and GPS, depending on what is available and what is most accurate. I found if I use this on an fixed computer, the locator will return my location as being somewhere near the local exchange, whilst using a smartphone reports an accurate reading of my position. I have found that using the HTML5 Geolocation can return a reliable and accurate method of determining your position, whether you are inside (a building) or outside, or away from wifi/data.
You can return the following values from a location fetch request:
Property:coords.latitude
Returns:The latitude as a decimal number (always returned)
Property:coords.longitude
Returns:The longitude as a decimal number (always returned)
Property:coords.accuracy
Returns:The accuracy of position (always returned)
Property:coords.altitude
Returns:The altitude in meters above the mean sea level (returned if available)
Property:coords.altitudeAccuracy
Returns:The altitude accuracy of position (returned if available)
Property:coords.heading
Returns:The heading as degrees clockwise from North (returned if available)
Property:coords.speed
Returns:The speed in meters per second (returned if available)
Property:timestamp
Returns:The date/time of the response (returned if available)
SETUP
The following will ensure that HMTL5 GeoLocation works during development in companion, and once your app is compiled.
Ensure you have Location Services turned on in your phone / device
In your aia project, request FINE_LOCATION permission, using your preferred method, I have done it like this: using a clock with a 500ms interval, to provide an event turnaround
3. You will then need a Button and a Webviewer, along with a WebViewStringChange event block and a label, to return lat/lon/other values back to your app from the webviewer , and another Button in order to "stop" the location finder (more later....)
4. You may also need some form of "stay-awake" routine, to keep your app alive and prevent it from going into the background, I will cover this later....
5. Finally, you will have observed that you need an html file to open in the webviewer, I will move onto these next.
HTML Files
These files can be uploaded as assets to your project. Use http://localhost/<myfile>.html in your webviewer goToUrl block.
In each example, I am returning the location data as a stringified json. This can be converted using the jsonTextDecode block from the web component to an AI2 list for re-use in the app.
You should always be using enableHighAccuracy:true; as an option
HTML1
Your basic, get my location html.
On the press of Button1 this will return your latitude and longitude and display this both in the webviewer and in the label in your aia project. You will notice that I use a conditional statement to check whether AppInventor is present, this ensures that the html can run in a normal browser on your computer, as well as within your app.
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<head>
<title>gl1</title>
</head>
<body>
<p>Get your coordinates...</p>
<p id="geo"></p>
<p id="lat"></p>
<p id="lon"></p>
<script>
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition, error, options);
} else {
document.getElementById("geo").innerHTML("Geolocation is not supported by this browser");
if( window.AppInventor ){
var msg = "Geolocation is not supported by this browser";
window.AppInventor.setWebViewString(msg);
}
};
function showPosition(position) {
document.getElementById("lat").innerHTML = "lat: " + position.coords.latitude;
document.getElementById("lon").innerHTML = "lon: " + position.coords.longitude;
if( window.AppInventor ){
var msg = "[" + position.coords.latitude + "," + position.coords.longitude + "]"
window.AppInventor.setWebViewString(msg);
}
};
function error(err) {
document.getElementById("geo").innerHTML = 'ERROR(' + err.code + '): ' + err.message;
};
</script>
</body>
</html>
HTML2
Get my location html, but this time include a timestamp.
On the press of Button1 this will return your latitude, longitude and timestamp and display this both in the webviewer and in the label in your aia project. The timestamp is in milliseconds, therefore you can use clock blocks to convert this to human readable date/time.
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<head>
<title>gl2</title>
</head>
<body>
<p>Get your coordinates...</p>
<p id="geo"></p>
<p id="lat"></p>
<p id="lon"></p>
<script>
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition, error, options);
} else {
document.getElementById("geo").innerHTML("Geolocation is not supported by this browser");
if( window.AppInventor ){
var msg = "Geolocation is not supported by this browser";
window.AppInventor.setWebViewString(msg);
}
};
function showPosition(position) {
document.getElementById("lat").innerHTML = "lat: " + position.coords.latitude;
document.getElementById("lon").innerHTML = "lon: " + position.coords.longitude;
const date = new Date(position.timestamp);
document.getElementById("geo").innerHTML = "timestamp: " + position.timestamp + "<br><br>date: " + date.toString();
if( window.AppInventor ){
var msg = "[" + position.coords.latitude + "," + position.coords.longitude + "," + position.timestamp + "]";
window.AppInventor.setWebViewString(msg);
}
};
function error(err) {
document.getElementById("geo").innerHTML = 'ERROR(' + err.code + '): ' + err.message;
};
</script>
</body>
</html>
HTML3
Continually get my location html, using the "watch" facility, which will return a new reading each time your location changes
On the press of Button1 this will return your latitude, longitude and timestamp and display this both in the webviewer and in the label in your aia project, and keep displaying new readings each time location changes. ( in my experience, even with my smartphone on my desk, not moving, location services keeps trying to find the most accurate reading, so you will get many readings when not moving, especially if you also call the timestamp, which will always be different)
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<head>
<title>gl3</title>
</head>
<body>
<p>Get your coordinates...</p>
<p id="geo"></p>
<p id="lat"></p>
<p id="lon"></p>
<script>
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
if (navigator.geolocation) {
navigator.geolocation.watchPosition(showPosition, error, options);
} else {
document.getElementById("geo").innerHTML("Geolocation is not supported by this browser");
if( window.AppInventor ){
var msg = "Geolocation is not supported by this browser";
window.AppInventor.setWebViewString(msg);
}
}
function showPosition(position) {
document.getElementById("lat").innerHTML = "lat: " + position.coords.latitude;
document.getElementById("lon").innerHTML = "lon: " + position.coords.longitude;
const date = new Date(position.timestamp);
document.getElementById("geo").innerHTML = "timestamp: " + position.timestamp + "<br><br>date: " + date.toString();
if( window.AppInventor ){
var msg = "[" + position.coords.latitude + "," + position.coords.longitude + "," + position.timestamp + "]";
window.AppInventor.setWebViewString(msg);
}
};
function error(err) {
document.getElementById("geo").innerHTML = 'ERROR(' + err.code + '): ' + err.message;
};
</script>
</body>
</html>
HTML4
Continually get my location html, using the "watch" facility, which will return a new reading each time your location changes, and set a target destination
On the press of Button1 this will return your latitude, longitude and timestamp and display this both in the webviewer and in the label in your aia project, and keep displaying new readings each time location changes. If the target lat/lon is reached, then the "watch" is stopped. ( I have set the target coordinates to the South Pole.....)
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<head>
<title>gl4</title>
</head>
<body>
<p>Get your coordinates...</p>
<p id="geo"></p>
<p id="lat"></p>
<p id="lon"></p>
<script>
var id, target, options;
function success(position) {
if (target.latitude === position.coords.latitude && target.longitude === position.coords.longitude) {
document.getElementById("geo").innerHTML = 'Congratulation, you reach the target';
navigator.geolocation.clearWatch(id);
} else {
document.getElementById("lat").innerHTML = "lat: " + position.coords.latitude;
document.getElementById("lon").innerHTML = "lon: " + position.coords.longitude;
const date = new Date(position.timestamp);
document.getElementById("geo").innerHTML = "timestamp: " + position.timestamp + "<br><br>date: " + date.toString();
if( window.AppInventor ){
var msg = "[" + position.coords.latitude + "," + position.coords.longitude + "," + position.timestamp + "]";
window.AppInventor.setWebViewString(msg);
}
}
};
function error(err) {
document.getElementById("geo").innerHTML = 'ERROR(' + err.code + '): ' + err.message;
};
target = {
latitude : -90.000,
longitude: 45.000,
}
options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
id = navigator.geolocation.watchPosition(success, error, options);
</script>
</body>
</html>
Stay-Awake
For examples HTML3 & HTML4, you may want your device/app to remain turned on and awake to continue receiving updates of position long after you phone/device would normally go to sleep. I have employed a simple stayawake routine, using a clock timer set to 10023ms (just over 10 seconds), and a notifier alert that displays "nothing". On my device, the phone settings significantly dim the screen after a time, but the app remains in the foreground.
Clock Timer Settings
Notifier Settings
Block Settings
EXAMPLE
I used the HTML3 file to help me plot a local walk, which was just under 2 miles. Here is the resultant map plotted from the coordinates recorded. I took a reading every 15 seconds (or so):
VIDEO
Showing the four html files in action returning lat/lon/timestamp information. Please note, the lat/lon values are fictitious.
RESOURCES
Credits:
To SteveJG on the AI2 community, for advice, testing and code snippets that helped me to get this all working