iOS
...
Data Objects
Geoqueries
9 min
geoqueries introduction we use the term geoqueries to refer to the type of queries where their conditions involve parsegeopoint parsegeopoint type fields it is recommended to use the parsegeopoint parsegeopoint struct to store geographic location data in a back4app database the parseswift sdk parseswift sdk provides a set of methods that allows us to query data according to conditions applied on parsegeopoint parsegeopoint data type prerequisites to complete this tutorial, you will need an app created on back4app a basic ios app to test queries goal to understand how to query data using conditions on geographic location data 1 quick review about the query\<u> class any query performed on a back4app database is done via the generic class query\<u> query\<u> the generic parameter u u (conforming to the parseobject parseobject protocol) is the data type of the objects we are trying to retrieve from the database given a data type like myobject myobject , we retrieve these objects from a back4app database in the following way 1 import parseswift 2 3 struct myobject parseobject { 4 5 } 6 7 let query = myobject query() 8 9 // executes the query asynchronously 10 query find { result in 11 // handle the result (of type result<\[myobject], parseerror>) 12 } you can read more about the query\<u> query\<u> class here at the official documentation https //github com/parse community/parse swift 2 save some data on back4app before we begin to execute queries, we should store some sample data on a back4app database by following the quickstart https //www back4app com/docs/ios/parse swift sdk/install sdk guide, you can configure and link your sample ios app to your back4app database for this guide, we will store information about cities we use the following struct to organize a city’s information 1 import parseswift 2 3 struct city parseobject { 4 5 6 var name string? // city's name 7 var location parsegeopoint? // it will store the city's coordinate on earth 8 } now, we proceed to store the sample data this step can be implemented using swift or directly from your app’s console https //www youtube com/watch?v=nvwryrzbcma on the back4app platform //after setting up your xcode project, calling the following method should save some sample data on your back4app database 1 import parseswift 2 3 func setupsampledata() { 4 do { 5 // montevideo uruguay 6 = try city( 7 name "montevideo", 8 location parsegeopoint(coordinate cllocationcoordinate2d(latitude 34 85553195363169, longitude 56 207280375137955)) 9 ) save() 10 11 // brasĂlia brazil 12 = try city( 13 name "brasĂlia", 14 location parsegeopoint(coordinate cllocationcoordinate2d(latitude 15 79485821477289, longitude 47 88391074690196)) 15 ) save() 16 17 // bogotá colombia 18 = try city( 19 name "bogotá", 20 location parsegeopoint(coordinate cllocationcoordinate2d(latitude 4 69139880891712, longitude 74 06936691331047)) 21 ) save() 22 23 // mexico city mexico 24 = try city( 25 name "mexico city", 26 location parsegeopoint(coordinate cllocationcoordinate2d(latitude 19 400977162618933, longitude 99 13311378164776)) 27 ) save() 28 29 // washington, d c united states 30 = try city( 31 name "washington, d c ", 32 location parsegeopoint(coordinate cllocationcoordinate2d(latitude 38 930727220189944, longitude 77 04626261880388)) 33 ) save() 34 35 // ottawa canada 36 = try city( 37 name "ottawa", 38 location parsegeopoint(latitude 45 41102167733425, longitude 75 695414598736) 39 ) save() 40 } catch let error as parseerror { 41 print("\[parseswift error]", error message) 42 } catch { 43 print("\[error]", error localizeddescription) 44 } 45 } back4app's console //a quick way to insert elements on your back4app database is via the console located in your app’s api section once you are there, you can start running javascript code to save the sample data 1 // add city objects and create table 2 // note how geopoints are created, passing latitude and longitude as arguments 3 // montevideo 4 city = new parse object('city'); 5 city set('name', 'montevideo uruguay'); 6 city set('location', new parse geopoint( 34 85553195363169, 56 207280375137955)); 7 await city save(); 8 9 // brasĂlia 10 city = new parse object('city'); 11 city set('name', 'brasĂlia brazil'); 12 city set('location', new parse geopoint( 15 79485821477289, 47 88391074690196)); 13 await city save(); 14 15 // bogotá 16 city = new parse object('city'); 17 city set('name', 'bogotá colombia'); 18 city set('location', new parse geopoint(4 69139880891712, 74 06936691331047)); 19 await city save(); 20 21 // mexico city 22 city = new parse object('city'); 23 city set('name', 'mexico city mexico'); 24 city set('location', new parse geopoint(19 400977162618933, 99 13311378164776)); 25 await city save(); 26 27 // washington, d c 28 city = new parse object('city'); 29 city set('name', 'washington, d c usa'); 30 city set('location', new parse geopoint(38 930727220189944, 77 04626261880388)); 31 await city save(); 32 33 // ottawa 34 city = new parse object('city'); 35 city set('name', 'ottawa canada'); 36 city set('location', new parse geopoint(45 41102167733425, 75 695414598736)); 37 await city save(); 3 query the data sorting the results with the sample data saved, we can start performing different query types for our first example, we will select all the cities and sort them depending on how far they are from a reference geopoint we implement this query by passing a constraint to the query\<city> query\<city> object the method near(key\ geopoint ) near(key\ geopoint ) available via the parseswift sdk parseswift sdk allows us to construct such constraint as arguments, we pass the field’s name (usually referred to as key key ) containing the reference geopoint geopoint 1 // the reference geopoint will be kingston city jamaica 2 guard let kingstongeopoint = try? parsegeopoint(latitude 18 018086950599134, longitude 76 79894232253473) else { return } 3 4 let query = city query(near(key "location", geopoint kingstongeopoint)) // the method near(key\ geopoint ) returns the constraint needed to sort the query 5 6 let sortedcities \[city]? = try? query find() // executes the query synchronosuly and returns an array containing the cities properly sorted 7 8 query find { result in // executes the query asynchronosuly and returns a result<\[city], parseerror> type object to handle the results 9 switch result { 10 case success(let cities) 11 // cities = \[bogotá, washington dc, mexico city, ottawa, brasĂlia, montevideo] 12 case failure(let error) 13 // handle the error if something happened 14 } 15 } selecting results within a given region suppose we want to select cities within a certain region we can achieve this with a constraint created by the method withinkilometers(key\ geopoint\ distance ) withinkilometers(key\ geopoint\ distance ) as arguments, we pass the field’s name containing the city’s location, the region’s center (a parsegeopoint parsegeopoint data type) and the maximum distance (in km ) a city can be from this region’s center to select all cities that are at most 3000km away from kingston jamaica, we can do it in the following way 1 let distance double = 3000 // km 2 guard let kingstongeopoint = try? parsegeopoint(latitude 18 018086950599134, longitude 76 79894232253473) else { return } 3 4 let query = city query(withinkilometers(key "location", geopoint kingstongeopoint, distance distance)) 5 // the method withinkilometers(key\ geopoint\ distance ) returns the constraint we need for this case 6 7 let sortedcities \[city]? = try? query find() // executes the query synchronosuly and returns an array containing the cities we are looking for (bogotá, washington dc and mexico city in this case) 8 9 query find { result in // executes the query asynchronosuly and returns a result<\[city], parseerror> type object to handle the results 10 switch result { 11 case success(let cities) 12 // cities = \[bogotá, washington dc, mexico city] 13 case failure(let error) 14 // handle the error if something happened 15 } 16 } additionally, when the distance is given in miles instead of kilomenters, we can use the withinmiles(key\ geopoint\ distance\ sorted ) withinmiles(key\ geopoint\ distance\ sorted ) method a less common method, withinradians(key\ geopoint\ distance\ sorted ) withinradians(key\ geopoint\ distance\ sorted ) , is also available if the distance is given in radians its use is very similar to the previous methods selecting results within a given polygon in the previous example, we selected cities within a region represented by a circular region in case we require to have a non circular shape for the region, the parseswift sdk parseswift sdk does allow us to construct such regions from their vertices now, the goal for this example is to select cities within a five vertex polygon these vertices are expressed using the parsegeopoint parsegeopoint struct once we have created the vertices, we instantiate a parsepolygon parsepolygon this polygon is then passed to the withinpolygon(key\ polygon ) withinpolygon(key\ polygon ) method (provided by the parseswift sdk parseswift sdk ) to construct the constraint that will allow us to select cities within this polygon 1 // the polygon where the selected cities are 2 let polygon parsepolygon? = { 3 do { 4 // we first instantiate the polygon vertices 5 let geopoint1 = try parsegeopoint(latitude 15 822238344514378, longitude 72 42845934415942) 6 let geopoint2 = try parsegeopoint(latitude 0 7433770196268968, longitude 97 44765968406668) 7 let geopoint3 = try parsegeopoint(latitude 59 997149373299166, longitude 76 52969196322749) 8 let geopoint4 = try parsegeopoint(latitude 9 488786415007201, longitude 18 346101586021952) 9 let geopoint5 = try parsegeopoint(latitude 15 414859532811047, longitude 60 00625459569375) 10 11 // next we compose the polygon 12 return try parsepolygon(\[geopoint1, geopoint2, geopoint3, geopoint4, geopoint5]) 13 } catch let error as parseerror { 14 print("failed to instantiate vertices \\(error message)") 15 return nil 16 } catch { 17 print("failed to instantiate vertices \\(error localizeddescription)") 18 return nil 19 } 20 }() 21 22 guard let safepolygon = polygon else { return } 23 24 let query = city query(withinpolygon(key "location", polygon safepolygon)) 25 // withinpolygon(key\ polygon ) returns the required constraint to apply on the query 26 27 let cities = try? query find() // executes the query synchronously 28 29 query find { result in // executes the query asynchronously and returns a result of type result<\[], parseerror> 30 // handle the result 31 } conclusion nowadays doing operations on location data to offer custom services is very important back4app together with the parseswift sdk parseswift sdk makes it easy to implement those kinds of operations