1. Code
  2. Mobile Development

Code a Real-Time NativeScript App: Geolocation and Google Maps

Scroll to top
This post is part of a series called Code a Real-Time NativeScript App.
Code a Real-Time NativeScript App: SQLite

NativeScript is a framework for building cross-platform native mobile apps using XML, CSS, and JavaScript. In this series, we'll try out some of the cool things you can do with a NativeScript app: geolocation and Google Maps integration, SQLite database, Firebase integration, and push notifications. Along the way, we'll build a fitness app with real-time capabilities that will use each of these features.

In this tutorial, you'll learn how to work with geolocation and Google Maps in NativeScript apps. 

I'm assuming that you already know how to create apps in NativeScript. If you're new to NativeScript, I recommend that you first check out one of the earlier tutorials in NativeScript before trying to follow this tutorial.

What You'll Be Creating

You'll be creating a walking tracker using geolocation and Google Maps. It will show the user how much distance they've covered and the number of steps they've taken to cover that distance. There will also be a map that will show the user's current location.

To give you an idea, here's what the final output will look like:

app finalapp finalapp final

Setting Up the Project

Start by creating a new NativeScript app:

1
tns create fitApp --appid "com.yourname.fitApp"

To make it easier to set up the UI of the app, I've created a GitHub repo which includes both the starter and final version of the project. You can go ahead and copy the contents of the app folder to your project's app folder. We will only be working with two files: main-page.xml and main-page.js file. The rest is just boilerplate from the NativeScript demo project. 

Running the App

We will be using the Android emulator provided by Android Studio to test the app. This will allow us to use the Android GPS Emulator to simulate the changing of locations from the comfort of our own homes. I don't really like aimlessly walking around outside to test geolocation either! But if that's your thing then I won't stop you.

If you execute tns run android, it will automatically call the Android emulator if it's already installed. If it's not yet installed, you can install it by launching Android Studio, clicking configure, and selecting SDK Manager. This will open the SDK Platforms by default. Click on the SDK Tools tab and make sure to select Android Emulator, and click on Apply to install it.

To use the GPS emulator, download it from GitHub and run the executable war file:

1
java -jar android-gps-emulator-0.2.war

Once that's done, you should be able to access http://localhost:8080/gpsemulator/ from your browser and connect to localhost. Make sure that the Android emulator is already running when you do this. Once you're connected, simply zoom in the map and click on any place you want to use as the location. The app will detect this and use it as its current location.

GPS EmulatorGPS EmulatorGPS Emulator

Working With Geolocation

Geolocation in NativeScript is similar to the Geolocation API in JavaScript. The only difference in functionality is the addition of a distance() function which is used for calculating the distance between two locations.

Installing the Geolocation Plugin

In order to work with geolocation, you first need to install the geolocation plugin:

1
tns plugin add nativescript-geolocation

Once that's done, you can now include it from your script files:

1
var geolocation = require("nativescript-geolocation");

Getting the User's Current Location

The NativeScript geolocation plugin includes three functions which you can use for working with the user's current location. We will be using each of these in this app:

  • getCurrentLocation
  • watchLocation
  • distance

Open the main-view-model.js file and add the following code inside the createViewModel() function. Here we're initializing the variables that we will be using later on for storing the different values that are needed for keeping track of the user's location. 

I've added some comments in the code so you know what's going on. There are also some lines of code that are commented out; these are for the Google Maps integration. I've commented them out for now to keep things simple. Once we get to the Google Maps integration, you'll need to remove those comments.

1
function createViewModel() {
2
    var viewModel = new Observable();
3
    var watchId; // stores the ID of the location watcher so we can stop it later

4
    var start_location; // stores the location of the user when they first started tracking

5
    var current_location; // stores the current location of the user

6
   
7
    viewModel.is_tracking = false; // whether the user's location is currently being tracked or not

8
    //viewModel.latitude = 15.447819409392789;

9
    //viewModel.longitude = 120.93888133764267;

10
    //viewModel.zoom = 20;

11
12
    var total_distance = 0;
13
    var total_steps = 0;
14
15
    var locations = []; // array which will store the locations

16
17
    //var mapView;

18
    //var marker = new mapsModule.Marker();

19
20
    if (!geolocation.isEnabled()) { // check if geolocation is not enabled

21
        geolocation.enableLocationRequest(); // request for the user to enable it

22
    } 
23
24
    // next: add code for getting the user's current location

25
}

Next, add the code for getting the user's current location. This code is executed when the user taps on the button for starting and stopping the location tracking. The geolocation.getCurrentLocation() method is used to get the current location. 

Here we've specified three options: desiredAccuracy, updateDistance, and timeoutdesiredAccuracy allows you to specify the accuracy in meters. It has two possible values: Accuracy.high, which is about 3 meters, and Accuracy.any, which is about 300 meters. updateDistance specifies how much difference (in meters) there must be between the previous location and the current location before it will update. Lastly, timeout specifies how many milliseconds to wait for a location. 

Once a location is received, we set it as the start_location and push it on the locations array. Later on, this location will be used along with the first location that will be fetched from watching the user's current location to determine the distance traveled.

1
viewModel.toggleTracking = function() {
2
    
3
    if (geolocation.isEnabled()) {
4
5
        this.set('is_tracking', !viewModel.is_tracking); // flip the toggle for tracking

6
        if (viewModel.is_tracking) { 
7
            geolocation.getCurrentLocation(
8
                {
9
                    desiredAccuracy: Accuracy.high, // 3 meter accuracy 

10
                    updateDistance: 5, // 5 meters

11
                    timeout: 2000 // 2 seconds

12
                }
13
            ).
14
            then(function(loc) {
15
                if (loc) {
16
                    start_location = loc;
17
                    locations.push(start_location);
18
                    
19
                    //viewModel.set('latitude', loc.latitude);

20
                    //viewModel.set('longitude', loc.longitude);

21
                }
22
            }, function(e){
23
                dialogs.alert(e.message);
24
            });  
25
26
            // next: add code for watching user's current location

27
28
        } else {
29
            
30
            // next: add code to stop watching the user's current location

31
        }
32
33
    } else {
34
        dialogs.alert("Please enable Geolocation");
35
    }
36
}

Watching for the User's Current Location

To get the current location, we use the geolocation.watchLocation() function. This function is similar to the setInterval() function in JavaScript, because it also executes the callback function repeatedly until you stop it with the geolocation.clearWatch() function. The callback function is automatically called based on the updateDistance and minimumUpdateTime

In the code below, the location will be updated if it is at least 5 meters different from the previous location that was fetched. But this update will only happen every 5 seconds. This means that if the user hasn't walked 5 meters or more within 5 seconds, the location won't update. 

1
watchId = geolocation.watchLocation(
2
    function (loc) {
3
        if (loc) {
4
            current_location = loc;
5
            
6
            // next: add code for getting the distance between two locations

7
        }
8
    }, 
9
    function(e){
10
        dialogs.alert(e.message);
11
    }, 
12
    {
13
        desiredAccuracy: Accuracy.high, 
14
        updateDistance: 5, // 5 meters

15
        minimumUpdateTime : 5000 // update every 5 seconds

16
    }
17
);

Once the user indicates that they want to stop tracking, you need to call the geolocation.clearWatch() function. You also need to reset the rest of the values that are being updated every time the location is changed. 

1
geolocation.clearWatch(watchId); // stop watching the user's location

2
total_distance = 0;
3
total_steps = 0;
4
locations = [];
5
viewModel.set('distance', "distance travelled: " + total_distance + " meters");
6
viewModel.set('steps', "steps: " + total_steps);

Getting the Distance Between Two Locations

Now we're ready to get the distance. This can be done by calling the geolocation.distance() function. This function accepts two location objects as its arguments, so we'll use the last two locations that were pushed to the locations array to determine the distance (in meters) traveled by the user from a previously recorded location to the current one. From there, we can use an approximate conversion from meters to the number of steps—I say approximate because not all people will travel the same distance in a single step. 

After that, we can just add the resulting distance and steps to the total_distance and total_steps so we can keep track of the total distance and steps they have taken since they started tracking their location.

1
locations.push(loc);
2
3
//viewModel.set('latitude', loc.latitude);

4
//viewModel.set('longitude', loc.longitude);

5
//marker.position = mapsModule.Position.positionFromLatLng(viewModel.latitude, viewModel.longitude); 

6
7
location_count = locations.length;
8
9
if (location_count >= 2) {
10
    var distance = Math.round(geolocation.distance(locations[location_count - 2], locations[location_count - 1])); // get the distance between the last two locations

11
    var steps = Math.round(distance * 1.3123); // determine the approximate number of steps

12
    
13
    // add the current distance to the overall distance travelled

14
    total_distance = total_distance + distance;
15
    total_steps = total_steps + steps;
16
    
17
    // update the UI

18
    viewModel.set('distance', "distance travelled: " + total_distance + " meters");
19
    viewModel.set('steps', "steps: " + total_steps);
20
}

At this point, you can now start testing the app using the GPS emulator that I mentioned earlier. Do note that you need to hit save on the main-view-model.js file to trigger an app reload. 

Then pick a location in the GPS emulator so that a fresh location will be fetched by the app once it loads. If you don't do this, it will default to the Googleplex location in Mountain View, California. This means that the next time you pick a location on the emulator, it will jump from this location to the location that you picked. If it's far away then you'll get a really large number for the distance and steps. 

Alternately, you could test on a real device with internet and GPS enabled. Only GPS is required at this point, but once we add Google Maps, the app will need an internet connection.

Working With Google Maps

We will now use Google Maps to add a map that shows the user's current location.

Installing the Google Maps Plugin

1
tns plugin add nativescript-google-maps-sdk

Once installed, you need to copy the template string resource files for Android:

1
cp -r node_modules/nativescript-google-maps-sdk/platforms/android/res/values app/App_Resources/Android/

Next, open the app/App_Resources/Android/values/nativescript_google_maps_api.xml file and add your own Google Maps API key (server key):

1
<?xml version="1.0" encoding="utf-8"?>
2
<resources>
3
     <string name="nativescript_google_maps_api_key">YOUR GOOGLE MAPS API KEY HERE</string>
4
</resources>

Make sure that you have enabled the Google Maps Android API from the Google Console before you try to use it.

Adding the Map

For the map, open the main-page.xml file and you should see the following:

1
<maps:mapView 
2
    latitude="{{ latitude }}" 
3
    longitude="{{ longitude }}" 
4
    zoom="{{ zoom }}" 
5
    mapReady="{{ onMapReady }}" 
6
/>

Here we've specified three options (longitudelatitude, and zoom) and a function to execute once the map is ready. longitude and latitude specify the location you want to render in the map. The zoom specifies the zoom level of the map. mapReady is where we specify the function for adding the marker on the map. This marker represents the user's current location, so it will be rendered at the center of the map.

By default, this won't work as you haven't added the schema definition for the maps yet. So in your Page element, add the definition for the maps element:

1
<Page 
2
    xmlns="http://schemas.nativescript.org/tns.xsd" 
3
    xmlns:maps="nativescript-google-maps-sdk"
4
>
5
</Page>

Once that's done, a Google map instance should be rendered right below the button for tracking location. It won't have any maps yet since the latitude and longitude haven't been specified yet. To do that, go back to the main-view-model.js file and remove the comments for the lines of code for working with Google Maps:

1
// default coordinates

2
viewModel.latitude = 15.447819409392789;
3
viewModel.longitude = 120.93888133764267;
4
viewModel.zoom = 20; // default map zoom level

5
6
var mapView; // variable for storing the current map instance

7
var marker = new mapsModule.Marker(); // variable for storing the marker instance

Adding the Marker

Since we've already declared default coordinates for the marker, we can actually plot a marker once the map is ready:

1
viewModel.onMapReady = function(args) {
2
    mapView = args.object; // get the map view

3
    marker.position = mapsModule.Position.positionFromLatLng(viewModel.latitude, viewModel.longitude); // set the marker's position on the map

4
    mapView.addMarker(marker); // add the marker to the map

5
}

Next, we need to update the marker position once the user starts tracking their location. You can do that inside the success callback function for the getCurrentLocation() function:

1
locations.push(start_location);
2
3
// remove the comments for these:                       

4
//viewModel.set('latitude', loc.latitude);

5
//viewModel.set('longitude', loc.longitude);

6
//marker.position = mapsModule.Position.positionFromLatLng(viewModel.latitude, viewModel.longitude); 

We also need update it when the user's location is updated (inside the success callback function for watchLocation):

1
current_location = loc;
2
locations.push(loc);
3
4
// remove the comments for these:

5
//viewModel.set('latitude', loc.latitude);

6
//viewModel.set('longitude', loc.longitude);

7
//marker.position = mapsModule.Position.positionFromLatLng(viewModel.latitude, viewModel.longitude); 

Once that's done, a map which renders the default location should show in the app.

Conclusion

In this tutorial, you've created a NativeScript app that allows the user to track how much distance they have covered and the approximate number of steps they've taken to cover that distance. You've also used Google Maps to let the user view their current location. By doing so, you've learned how to use the geolocation and Google Maps plugins for NativeScript.

This is just the start! In the next posts of this series, we'll add a local database, push notifications and other cool features to our app.

In the meantime, check out some of our other posts on NativeScript and cross-platform mobile coding.

For a comprehensive introduction to NativeScript, try our video course Code a Mobile App With NativeScript. In this course, Keyvan Kasaei will show you step by step how to build a simple application. Along the way, you'll learn how to implement a simple app workflow with network requests, an MVVM architecture, and some of the most important NativeScript UI components. By the end, you'll understand why you should consider NativeScript for your next mobile app project.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.