Android

Geoqueries

Introduction

In this guide, you’ll learn how to perform GeoPoint querying on Parse in an Android application.

This tutorial uses an app created in Android Studio Arctic Fox -2020.3.1 with compileSdk = 30 , minSdk = 23 and targetSdk = 30

At any time, you can access the complete Project via our GitHub repositories.

Goal

Perform Geoqueries using geopoints stored on Back4App and Android geolocation.

Here is a preview of what we are gonna achieve:

Prerequisites

To complete this tutorial, we need:

Let’s get started!

Before next steps, we need to connect Back4App to our application. You should save the appId and clientKey from the Back4App to string.xml file and then init Parse in our App.java or App.kt file.
Follow the New Parse App tutorial if you don’t know how to init Parse to your app.
Or you can download the projects we shared the github links above and edit only the appId and clientKey parts according to you.

Step 1 - Save some data on Back4App

In this step, we will create a Class with the JS Console and Javascript codes provided by Parse and we will create queries for this Class.

Let’s create a City class, which will be the target of our queries in this guide. On Parse JS Console is possible to run JavaScript code directly, querying and updating your application database contents using the JS SDK commands. Run the code below from your JS Console and insert the data on Back4App.

Here is how the JS Console looks like in your dashboard:

React Native Back4App

Go ahead and create the City class with the following example content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Add City objects and create table
// Note how GeoPoints are created, passing latitude and longitude as arguments
// Montevideo
City = new Parse.Object('City');
City.set('name', 'Montevideo - Uruguay');
City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
await City.save();

// Brasília
City = new Parse.Object('City');
City.set('name', 'Brasília - Brazil');
City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
await City.save();

// Bogotá
City = new Parse.Object('City');
City.set('name', 'Bogotá - Colombia');
City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
await City.save();

// Mexico City
City = new Parse.Object('City');
City.set('name', 'Mexico City - Mexico');
City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
await City.save();

// Washington, D.C.
City = new Parse.Object('City');
City.set('name', 'Washington, D.C. - USA');
City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
await City.save();

// Ottawa
City = new Parse.Object('City');
City.set('name', 'Ottawa - Canada');
City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
await City.save();

console.log('Success!');

Step 2 - Query the data from Android app

Now that you have a populated class, we can now perform some GeoPoint queries in it. Let’s begin by ordering City results by the nearest from Kingston in Jamaica (latitude 18.01808695059913 and longitude -76.79894232253473), using the ParseQuery.whereNear method:

1
2
3
4
5
6
7
8
9
    ParseQuery<ParseObject> query = new ParseQuery<>("City");
    query.whereNear("location",new ParseGeoPoint(18.018086, -76.798942));
    query.findInBackground((objects, e) -> {
        if (e==null){
            initData(objects);
        } else {
            Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }
    });
1
2
3
4
5
6
7
8
9
10
    val query = ParseQuery<ParseObject>("City")
    query.whereNear("location", ParseGeoPoint(18.018086, -76.798942))
    query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
        if (e == null) {
            initData(objects!!)
        } else {
            Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
                .show()
        }
    }

Let’s now query using the method ParseQuery.whereWithinKilometers, which will retrieve all results whose GeoPoint field is located within the max distance. Kingston will be used once again as a reference and the distance limit will be 3000 km.

1
2
3
4
5
6
7
8
9
    ParseQuery<ParseObject> query = new ParseQuery<>("City");
    query.whereWithinKilometers("location",new ParseGeoPoint(18.018086, -76.798942),3000);
    query.findInBackground((objects, e) -> {
        if (e==null){
            initData(objects);
        } else {
            Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }
    });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    val query = ParseQuery<ParseObject>("City")
    query.whereWithinKilometers(
        "location",
        ParseGeoPoint(18.018086, -76.798942),
        3000.0
    )
    query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
        if (e == null) {
            initData(objects!!)
        } else {
            Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
                .show()
        }
    }

Another useful query method is ParseQuery.whereWithinPolygon, which will query results whose GeoPoint field value is within the specified polygon, composed of an array of GeoPoints (at least three). If the polygon path is open, it will be closed automatically by Parse connecting the last and first points.
For this example, you will be using a simple polygon that roughly contains the South American continent, composed of 5 distant GeoPoints in the ocean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    ParseQuery<ParseObject> query = new ParseQuery<>("City");

    ParseGeoPoint geoPoint1 = new ParseGeoPoint(15.822238344514378, -72.42845934415942);
    ParseGeoPoint geoPoint2 = new ParseGeoPoint(-0.7433770196268968, -97.44765968406668);
    ParseGeoPoint geoPoint3 = new ParseGeoPoint(-59.997149373299166, -76.52969196322749);
    ParseGeoPoint geoPoint4 = new ParseGeoPoint(-9.488786415007201, -18.346101586021952);
    ParseGeoPoint geoPoint5 = new ParseGeoPoint(15.414859532811047, -60.00625459569375);
    ParseGeoPoint geoPoint6 = new ParseGeoPoint(41.015137, 28.97953);
    
    List<ParseGeoPoint> list = new ArrayList<>();
    list.add(geoPoint1);
    list.add(geoPoint2);
    list.add(geoPoint3);
    list.add(geoPoint4);
    list.add(geoPoint5);
    list.add(geoPoint6);
    query.whereWithinPolygon("location",list);

    query.findInBackground((objects, e) -> {
        if (e==null){
            initData(objects);
        } else {
            Toast.makeText(MainActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }
    });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    val query = ParseQuery<ParseObject>("City")
    val geoPoint1 = ParseGeoPoint(15.822238344514378, -72.42845934415942)
    val geoPoint2 = ParseGeoPoint(-0.7433770196268968, -97.44765968406668)
    val geoPoint3 = ParseGeoPoint(-59.997149373299166, -76.52969196322749)
    val geoPoint4 = ParseGeoPoint(-9.488786415007201, -18.346101586021952)
    val geoPoint5 = ParseGeoPoint(15.414859532811047, -60.00625459569375)
    val geoPoint6 = ParseGeoPoint(41.015137, 28.97953)
    val list: MutableList<ParseGeoPoint> =
        ArrayList()
    list.add(geoPoint1)
    list.add(geoPoint2)
    list.add(geoPoint3)
    list.add(geoPoint4)
    list.add(geoPoint5)
    list.add(geoPoint6)
    query.whereWithinPolygon("location", list)
    query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
        if (e == null) {
            initData(objects!!)
        } else {
            Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT)
                .show()
        }
    }

It’s done!

At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from an Android app. In the next guide, you will check how to create and manage users in Parse.